From 747807224b3dce9360118d1bf72a461615a84b5c Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 6 Oct 2021 23:17:45 +0200
Subject: [PATCH 01/52] 2.5d view enhancements
* Fine-tuned 2.5d view's key events
* better error message in case of z-stack/layers mismatch
* Don't confine bounding box to actual layout - that can be confusing
---
src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 2 +-
.../tools/view_25d/lay_plugin/layD25ViewWidget.cc | 13 ++++---------
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc
index 0055a43cf..603136699 100644
--- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc
+++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc
@@ -203,7 +203,7 @@ D25View::activated ()
bool any = mp_ui->d25_view->attach_view (view ());
if (! any) {
mp_ui->d25_view->attach_view (0);
- throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in the view.\nUse \"Tools/Manage Technologies\" to set up a z stack.")));
+ throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in this view.\nUse \"Tools/Manage Technologies\" to set up a z stack or check if it applies to the layers here.")));
}
mp_ui->d25_view->reset ();
diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc
index f83b57214..3eb97dfee 100644
--- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc
+++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc
@@ -311,13 +311,13 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
// Move "into" or "out"
- double d = (event->key () == Qt::Key_Up ? 0.1 : -0.1);
+ double d = (event->key () == Qt::Key_Up ? 0.05 : -0.05);
QMatrix4x4 t;
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
QVector3D cd = t.inverted ().map (QVector3D (0, 0, cam_dist ()));
- set_displacement (displacement () + d * cd);
+ set_displacement (displacement () + d * cd / m_scale_factor);
}
@@ -327,7 +327,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
// Ctrl + left/right changes azimuths
- double d = (event->key () == Qt::Key_Right ? 2 : -2);
+ double d = (event->key () == Qt::Key_Right ? 1 : -1);
double a = cam_azimuth () + d;
if (a < -180.0) {
@@ -348,7 +348,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event)
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
QVector3D cd = t.inverted ().map (QVector3D (cam_dist (), 0, 0));
- set_displacement (displacement () + d * cd);
+ set_displacement (displacement () + d * cd / m_scale_factor);
}
@@ -634,11 +634,6 @@ D25ViewWidget::prepare_view ()
}
- m_bbox &= cell_bbox;
- if (m_bbox.empty ()) {
- return false;
- }
-
bool any = false;
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
From ec527367f24698a794985e6b923b60fbf1ae2b6e Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 6 Oct 2021 23:26:12 +0200
Subject: [PATCH 02/52] 'tap' shows layer details like view name
---
src/edt/edt/edtMainService.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc
index f778d6fe2..133b6f18a 100644
--- a/src/edt/edt/edtMainService.cc
+++ b/src/edt/edt/edtMainService.cc
@@ -2115,7 +2115,7 @@ MainService::cm_tap ()
QPoint mp = view ()->view_object_widget ()->mapToGlobal (view ()->view_object_widget ()->mouse_position ());
for (std::vector::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
- QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->source (true).to_string ()));
+ QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->display_string (view (), true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}
From 710d217b173624ee01bd66fcec7d3d98219bcd21 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Thu, 7 Oct 2021 22:17:03 +0200
Subject: [PATCH 03/52] strmxor: better command line doc, show file read timing
like main app.
---
src/buddies/src/bd/strmxor.cc | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc
index 6741f773b..6072e1e73 100644
--- a/src/buddies/src/bd/strmxor.cc
+++ b/src/buddies/src/bd/strmxor.cc
@@ -180,7 +180,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
<< tl::arg ("input_b", &infile_b, "The second input file (any format, may be gzip compressed)")
<< tl::arg ("?output", &output, "The output file to which the XOR differences are written",
"This argument is optional. If not given, the exit status alone will indicate whether the layouts "
- "are identical or not."
+ "are identical or not. The output is a layout file. The format of the file is derived "
+ "from the file name's suffix (.oas[.gz] for (gzipped) OASIS, .gds[.gz] for (gzipped) GDS2 etc.)."
)
<< tl::arg ("-ta|--top-a=name", &top_a, "Specifies the top cell for the first layout",
"Use this option to take a specific cell as the top cell from the first layout. All "
@@ -248,6 +249,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
db::Layout layout_b;
{
+ tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (A): ")) + infile_a);
+
db::LoadLayoutOptions load_options;
generic_reader_options_a.configure (load_options);
@@ -257,6 +260,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
}
{
+ tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (B): ")) + infile_b);
+
db::LoadLayoutOptions load_options;
generic_reader_options_b.configure (load_options);
From c48f930fb3a2fc59741582919f8ed99b885c51c5 Mon Sep 17 00:00:00 2001
From: klayoutmatthias
Date: Thu, 14 Oct 2021 23:48:24 +0200
Subject: [PATCH 04/52] Fixed #918 by using ignore size policy for the editor
options tab widget
---
src/laybasic/laybasic/layEditorOptionsPages.cc | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc
index 0d53bae60..eaac5cd8c 100644
--- a/src/laybasic/laybasic/layEditorOptionsPages.cc
+++ b/src/laybasic/laybasic/layEditorOptionsPages.cc
@@ -56,6 +56,7 @@ EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vectorsetMargin (0);
mp_pages = new QTabWidget (this);
+ mp_pages->setSizePolicy (QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored));
ly1->addWidget (mp_pages);
m_pages = pages;
From 4d64e95680c22f5d693eea8825ed03447b7e41b5 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 18 Oct 2021 23:08:38 +0200
Subject: [PATCH 05/52] Shape objects from custom queries 'select' are
translated into shape markers when exporting to RDB
---
src/lay/lay/laySearchReplaceDialog.cc | 5 +--
src/rdb/rdb/rdbUtils.cc | 47 +++++++++++++++++++++++++++
src/rdb/rdb/rdbUtils.h | 12 +++++++
3 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc
index 4f8196e64..3c70640ba 100644
--- a/src/lay/lay/laySearchReplaceDialog.cc
+++ b/src/lay/lay/laySearchReplaceDialog.cc
@@ -38,6 +38,7 @@
#include "tlLog.h"
#include "tlProgress.h"
#include "tlTimer.h"
+#include "rdbUtils.h"
#include
#include
@@ -537,10 +538,10 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu)
rdb::Item *item = rdb.create_item (cell->id (), cat->id ());
if (! v->is_list ()) {
- item->add_value (std::string (v->to_string ()));
+ rdb::add_item_value (item, *v, dbu);
} else {
for (std::vector::const_iterator i = v->get_list ().begin (); i != v->get_list ().end (); ++i) {
- item->add_value (std::string (i->to_string ()));
+ rdb::add_item_value (item, *i, dbu);
}
}
diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc
index 336ed963e..c6940a56a 100644
--- a/src/rdb/rdb/rdbUtils.cc
+++ b/src/rdb/rdb/rdbUtils.cc
@@ -317,5 +317,52 @@ void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb:
}
}
+void add_item_value (rdb::Item *item, const tl::Variant &v, double dbu)
+{
+ if (dbu > 0 && v.is_user ()) {
+ item->add_value (db::CplxTrans (dbu) * v.to_user ());
+ } else if (dbu > 0 && v.is_user ()) {
+ db::DPoint p = db::CplxTrans (dbu) * v.to_user ();
+ item->add_value (db::DEdge (p, p));
+ } else if (dbu > 0 && v.is_user ()) {
+ item->add_value (db::CplxTrans (dbu) * v.to_user ());
+ } else if (dbu > 0 && v.is_user ()) {
+ db::DPolygon p;
+ db::DSimplePolygon sp = db::CplxTrans (dbu) * v.to_user ();
+ p.assign_hull (sp.begin_hull (), sp.end_hull ());
+ item->add_value (p);
+ } else if (dbu > 0 && v.is_user ()) {
+ item->add_value (db::CplxTrans (dbu) * v.to_user ());
+ } else if (dbu > 0 && v.is_user ()) {
+ item->add_value (db::CplxTrans (dbu) * v.to_user ());
+ } else if (dbu > 0 && v.is_user ()) {
+ item->add_value (db::CplxTrans (dbu) * v.to_user ());
+ } else if (dbu > 0 && v.is_user ()) {
+ item->add_value (db::CplxTrans (dbu) * v.to_user ());
+ } else if (v.is_user ()) {
+ item->add_value (v.to_user ());
+ } else if (v.is_user ()) {
+ db::DPoint p = v.to_user ();
+ item->add_value (db::DEdge (p, p));
+ } else if (v.is_user ()) {
+ item->add_value (v.to_user ());
+ } else if (v.is_user ()) {
+ db::DPolygon p;
+ db::DSimplePolygon sp = v.to_user ();
+ p.assign_hull (sp.begin_hull (), sp.end_hull ());
+ item->add_value (p);
+ } else if (v.is_user ()) {
+ item->add_value (v.to_user ());
+ } else if (v.is_user ()) {
+ item->add_value (v.to_user ());
+ } else if (v.is_user ()) {
+ item->add_value (v.to_user ());
+ } else if (v.is_user ()) {
+ item->add_value (v.to_user ());
+ } else {
+ item->add_value (std::string (v.to_string ()));
+ }
+}
+
}
diff --git a/src/rdb/rdb/rdbUtils.h b/src/rdb/rdb/rdbUtils.h
index d89cd6c72..f6b579008 100644
--- a/src/rdb/rdb/rdbUtils.h
+++ b/src/rdb/rdb/rdbUtils.h
@@ -136,6 +136,18 @@ RDB_PUBLIC_TEMPLATE void create_items_from_sequence (rdb::Database *db, rdb::id_
}
}
+/**
+ * @brief Creates a value from a tl::Variant
+ *
+ * This produces values which reflect some values the variant can assume - specifically
+ * shapes are converted into corresponding RDB values. The database unit is used to
+ * translate integer-type values. Using a database unit of 0 will disable the conversion of
+ * such types.
+ *
+ * Unknown types are converted to strings.
+ */
+RDB_PUBLIC void add_item_value (rdb::Item *item, const tl::Variant &v, double dbu = 0.0);
+
}
#endif
From 04deb785baa35b2b7790a68fc3a3749edbb3e7b4 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 18 Oct 2021 23:10:39 +0200
Subject: [PATCH 06/52] Asking whether to apply a .lyp file on multiple layouts
on MRU-listed .lyp files too.
---
src/lay/lay/layMainWindow.cc | 67 +++++++++++++++++++-----------------
src/lay/lay/layMainWindow.h | 1 +
2 files changed, 36 insertions(+), 32 deletions(-)
diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc
index c97f0def7..b886acfc8 100644
--- a/src/lay/lay/layMainWindow.cc
+++ b/src/lay/lay/layMainWindow.cc
@@ -2025,44 +2025,47 @@ MainWindow::cm_load_layer_props ()
if (current_view ()) {
std::string fn;
if (mp_lprops_fdia->get_open (fn, tl::to_string (QObject::tr ("Load Layer Properties File")))) {
-
- int target_cv_index = -2;
-
- if (current_view ()->cellviews () > 1 && is_single_cv_layer_properties_file (fn)) {
-
- QStringList items;
- items << QString (QObject::tr ("Take it as it is"));
- items << QString (QObject::tr ("Apply to all layouts"));
- for (unsigned int i = 0; i < current_view ()->cellviews (); ++i) {
- items << QString (tl::to_qstring (tl::to_string (QObject::tr ("Apply to ")) + current_view ()->cellview (i)->name () + " (@" + tl::to_string (i + 1) + ")"));
- }
-
- bool ok;
- QString item = QInputDialog::getItem(this, QObject::tr ("Apply Layer Properties File"),
- QObject::tr ("There are multiple layouts in that panel but the layer properties file contains properties for a single one.\nWhat should be done?"),
- items, 1, false, &ok);
- if (!ok || item.isEmpty()) {
- return;
- }
-
- target_cv_index = items.indexOf (item) - 2;
-
- }
-
- if (target_cv_index > -2) {
- load_layer_properties (fn, target_cv_index, false /*current view only*/, false /*don't add default*/);
- } else {
- load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
- }
-
+ load_layer_props_from_file (fn);
add_to_other_mru (fn, cfg_mru_layer_properties);
-
}
} else {
throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the layer properties for")));
}
}
+void
+MainWindow::load_layer_props_from_file (const std::string &fn)
+{
+ int target_cv_index = -2;
+
+ if (current_view ()->cellviews () > 1 && is_single_cv_layer_properties_file (fn)) {
+
+ QStringList items;
+ items << QString (QObject::tr ("Take it as it is"));
+ items << QString (QObject::tr ("Apply to all layouts"));
+ for (unsigned int i = 0; i < current_view ()->cellviews (); ++i) {
+ items << QString (tl::to_qstring (tl::to_string (QObject::tr ("Apply to ")) + current_view ()->cellview (i)->name () + " (@" + tl::to_string (i + 1) + ")"));
+ }
+
+ bool ok;
+ QString item = QInputDialog::getItem(this, QObject::tr ("Apply Layer Properties File"),
+ QObject::tr ("There are multiple layouts in that panel but the layer properties file contains properties for a single one.\nWhat should be done?"),
+ items, 1, false, &ok);
+ if (!ok || item.isEmpty()) {
+ return;
+ }
+
+ target_cv_index = items.indexOf (item) - 2;
+
+ }
+
+ if (target_cv_index > -2) {
+ load_layer_properties (fn, target_cv_index, false /*current view only*/, false /*don't add default*/);
+ } else {
+ load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
+ }
+}
+
void
MainWindow::save_session (const std::string &fn)
{
@@ -3238,7 +3241,7 @@ MainWindow::open_recent_layer_properties (size_t n)
if (n < m_mru_layer_properties.size ()) {
std::string fn = m_mru_layer_properties [n];
- load_layer_properties (fn, false /*current view only*/, false /*don't add default*/);
+ load_layer_props_from_file (fn);
add_to_other_mru (fn, cfg_mru_layer_properties); // make it the latest
}
diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h
index f9fe51611..cb2f2246d 100644
--- a/src/lay/lay/layMainWindow.h
+++ b/src/lay/lay/layMainWindow.h
@@ -805,6 +805,7 @@ private:
int dirty_files (std::string &dirty_files);
+ void load_layer_props_from_file (const std::string &fn);
void interactive_close_view (int index, bool all_cellviews);
void call_on_current_view (void (lay::LayoutView::*func) (), const std::string &op_desc);
void current_view_changed ();
From 97d393c78a8e63c4074d9089b0b2b64fb5089206 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 18 Oct 2021 23:24:28 +0200
Subject: [PATCH 07/52] Layout#is_editable
---
src/laybasic/laybasic/gsiDeclLayLayoutView.cc | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc
index a01db5183..938ca9d36 100644
--- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc
+++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc
@@ -690,6 +690,11 @@ Class decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
"\n"
"In 0.25, this method has been moved from MainWindow to LayoutView.\n"
) +
+ gsi::method ("is_editable?", &lay::LayoutView::is_editable,
+ "@brief Returns true if the view is in editable mode\n"
+ "\n"
+ "This read-only attribute has been added in version 0.27.5.\n"
+ ) +
gsi::method ("reload_layout", &lay::LayoutView::reload_layout, gsi::arg ("cv"),
"@brief Reloads the given cellview\n"
"\n"
From 89a50abeddf99f26c1a7b4cc74f8081d1e9bec2d Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Tue, 19 Oct 2021 00:11:27 +0200
Subject: [PATCH 08/52] Some more documentation about layer views.
---
src/lay/lay/doc/manual/basic.xml | 2 +-
.../lay/doc/manual/layer_source_expert.xml | 4 +
src/lay/lay/doc/manual/layer_views.xml | 84 +++++++++++++++++++
src/lay/lay/layHelpResources.qrc | 1 +
4 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 src/lay/lay/doc/manual/layer_views.xml
diff --git a/src/lay/lay/doc/manual/basic.xml b/src/lay/lay/doc/manual/basic.xml
index 92969e177..ec136b253 100644
--- a/src/lay/lay/doc/manual/basic.xml
+++ b/src/lay/lay/doc/manual/basic.xml
@@ -24,9 +24,9 @@
+
-
diff --git a/src/lay/lay/doc/manual/layer_source_expert.xml b/src/lay/lay/doc/manual/layer_source_expert.xml
index 30619a4c1..97a50d9dc 100644
--- a/src/lay/lay/doc/manual/layer_source_expert.xml
+++ b/src/lay/lay/doc/manual/layer_source_expert.xml
@@ -100,5 +100,9 @@
a selector as well, only those shapes will be shown that meet both criteria.
+
+ A general description for the source notation is found here: .
+
+
diff --git a/src/lay/lay/doc/manual/layer_views.xml b/src/lay/lay/doc/manual/layer_views.xml
new file mode 100644
index 000000000..c9c294b0c
--- /dev/null
+++ b/src/lay/lay/doc/manual/layer_views.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+ The Layer List (Layer Views)
+
+
+
+
+ An important concept in KLayout are the layer views. KLayout displays the layers
+ of a layout by default in a list on the right side of the main window.
+ This list however, does not directly reflect the layers in the layout database.
+ Instead this list is a collection of "views". A view is a description of what is
+ to be displayed and how.
+
+
+
+ Essentially, the entries in the layer list are pointers to layers in the
+ database, together with a description how to paint the shapes on these layers (the
+ "layer properties").
+
+
+
+ The pointer is the "source" of a layer view. This is typically a GDS layer and datatype,
+ but can be a layer name (for DXF for example). There are also abstract sources (such as
+ cell boundaries) and the sources can include selectors or modifiers. Selectors are
+ used to only display shapes with certain user properties or from certain hierarchy
+ levels. Modifiers transform the shapes before they are drawn for example.
+ The source is defined by a "source specification" - this is a string describing the
+ database layer and selectors and modifiers. A simple source string is "1/0" which is
+ for GDS layer 1, datatype 0 without and selectors or modifiers.
+ To change the source, use "Change Source" from the layer list's context menu.
+
+
+
+ See for some source specification string applications
+ and more details.
+
+
+
+ Beside the source, a layer entry has a display name. This is an arbitrary text
+ providing a description for the user. By default - when no such name is present -
+ the source of the layer will be displayed.
+ To change the display name, use "Rename" from the layer list's context menu.
+
+
+
+ Plus of course, the layer views have many options to specify the drawing style,
+ animations and decorations.
+
+
+
+ The concept of separating views from the database layers opens some interesting options:
+
+
+
+
Layer views can refer to individual layouts from multi-layout views (through the "@1", "@2", ... notation in the source).
+ Hence, multiple layouts can be mixed in a single layer list.
+
Layers can be present in the list which do not need to be present in the database. Such a layer is shown as empty.
+ This is important as in GDS an empty layer is equivalent to non-existing. Still you may want to have it shown in the
+ layer list - the views offer this option.
+
Vice versa, database layer may not be listed in the layer list if no corresponding layer view is present. This
+ way, auxiliary or debug layers can be omitted from the layer list. A "wildcard specification" is available to make
+ sure, all layers are shown if you need to see all.
+
Multiple tabs can be present to provide multiple views on the same layouts. This is just an alternative set of
+ layer views.
+
Layer grouping, sorting etc. are just operations on the views, no database change is involved.
+
+
+
+ The concept on the other hand is slightly counter-intuitive at first.
+ Here are some hints:
+
+
+
+
Renaming a layer does not change the source - if you rename a layer to something like "1/0", you are likely to fool yourself thinking this is layer 1, datatype 0.
+
Changing a layer view's source does not change the database too - it will just change the pointer. To change a layer's information in the database, use Edit/Layer/Edit Layer Specification.
+
Deleting a layer from the layer list does not delete the layer from the database. Use Edit/Layer/Delete Layer instead.
+
Additing a new layer does not immediately create the layer in the database. Only once you draw something on that layer, it is generated in the database.
+
+
+
+
diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc
index 0e0053df0..06524e1f5 100644
--- a/src/lay/lay/layHelpResources.qrc
+++ b/src/lay/lay/layHelpResources.qrc
@@ -158,6 +158,7 @@
doc/manual/landmarks.xmldoc/manual/layer_animation.xmldoc/manual/layer_boolean.xml
+ doc/manual/layer_views.xmldoc/manual/layer_color.xmldoc/manual/layer_content.xmldoc/manual/layer_fill.xml
From abe40ae99f83e72d79eb1ee3a84117b7c1a544ef Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Tue, 19 Oct 2021 20:48:13 +0200
Subject: [PATCH 09/52] Bugfix plus more tests
---
src/db/db/dbNetlistDeviceExtractorClasses.cc | 5 +-
.../dbNetlistDeviceExtractorTests.cc | 292 ++++++++++++++++++
testdata/algo/bjt3_1.gds | Bin 0 -> 234 bytes
testdata/algo/bjt3_2.gds | Bin 0 -> 298 bytes
testdata/algo/bjt3_3.gds | Bin 0 -> 298 bytes
testdata/algo/bjt3_4.gds | Bin 0 -> 362 bytes
testdata/algo/bjt4_1.gds | Bin 0 -> 362 bytes
7 files changed, 296 insertions(+), 1 deletion(-)
create mode 100644 testdata/algo/bjt3_1.gds
create mode 100644 testdata/algo/bjt3_2.gds
create mode 100644 testdata/algo/bjt3_3.gds
create mode 100644 testdata/algo/bjt3_4.gds
create mode 100644 testdata/algo/bjt4_1.gds
diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc
index 957c9e8bb..c4cbd3844 100644
--- a/src/db/db/dbNetlistDeviceExtractorClasses.cc
+++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc
@@ -694,13 +694,16 @@ void NetlistDeviceExtractorBJT3Transistor::extract_devices (const std::vector collector terminal is collector outside base
- rcollector = rcollector2base - rbase;
+ rcollector = rcollectors.selected_interacting (rbase) - rbase;
} else {
// lateral transistor: base is reduced by collector area
rcollector = rcollector2base;
rbase -= rcollector2base;
}
+ // TODO: rbase - rcollector2base above could basically split a base region into different
+ // subregions potentially forming one transistor each.
+
// this is what is the true base contact
rbase -= remitter2base;
diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc
index c6077ce61..6978a692c 100644
--- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc
+++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc
@@ -853,3 +853,295 @@ TEST(42_DMOS4DeviceExtractorTestCircular)
EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)");
EXPECT_EQ (o4.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)");
}
+
+TEST(50_BJT3DeviceExtractorTest)
+{
+ db::Layout ly;
+
+ {
+ db::LoadLayoutOptions options;
+
+ std::string fn (tl::testdata ());
+ fn = tl::combine_path (fn, "algo");
+ fn = tl::combine_path (fn, "bjt3_1.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 l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
+ db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
+ db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
+ db::Region o1 (dss);
+ db::Region o2 (dss);
+ db::Region o3 (dss);
+
+ // perform the extraction
+
+ db::Netlist nl;
+ db::hier_clusters cl;
+
+ db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
+
+ db::NetlistDeviceExtractor::input_layers dl;
+
+ dl["E"] = &l0;
+ dl["B"] = &l1;
+ dl["C"] = &l2;
+ dl["tE"] = &o1;
+ dl["tB"] = &o2;
+ dl["tC"] = &o3;
+ ex.extract (dss, 0, dl, nl, cl);
+
+ EXPECT_EQ (nl.to_string (),
+ "circuit TOP ();\n"
+ " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n"
+ "end;\n"
+ );
+ EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
+ EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
+ EXPECT_EQ (o3.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
+}
+
+TEST(51_BJT3DeviceExtractorTest)
+{
+ db::Layout ly;
+
+ {
+ db::LoadLayoutOptions options;
+
+ std::string fn (tl::testdata ());
+ fn = tl::combine_path (fn, "algo");
+ fn = tl::combine_path (fn, "bjt3_2.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 l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
+ db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
+ db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
+ db::Region o1 (dss);
+ db::Region o2 (dss);
+ db::Region o3 (dss);
+
+ // perform the extraction
+
+ db::Netlist nl;
+ db::hier_clusters cl;
+
+ db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
+
+ db::NetlistDeviceExtractor::input_layers dl;
+
+ dl["E"] = &l0;
+ dl["B"] = &l1;
+ dl["C"] = &l2;
+ dl["tE"] = &o1;
+ dl["tB"] = &o2;
+ dl["tC"] = &o3;
+ ex.extract (dss, 0, dl, nl, cl);
+
+ EXPECT_EQ (nl.to_string (),
+ "circuit TOP ();\n"
+ " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n"
+ "end;\n"
+ );
+ EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
+ EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
+ EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000)");
+}
+
+TEST(52_BJT3DeviceExtractorTestLateral)
+{
+ db::Layout ly;
+
+ {
+ db::LoadLayoutOptions options;
+
+ std::string fn (tl::testdata ());
+ fn = tl::combine_path (fn, "algo");
+ fn = tl::combine_path (fn, "bjt3_3.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 l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
+ db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
+ db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
+ db::Region o1 (dss);
+ db::Region o2 (dss);
+ db::Region o3 (dss);
+
+ // perform the extraction
+
+ db::Netlist nl;
+ db::hier_clusters cl;
+
+ db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
+
+ db::NetlistDeviceExtractor::input_layers dl;
+
+ dl["E"] = &l0;
+ dl["B"] = &l1;
+ dl["C"] = &l2;
+ dl["tE"] = &o1;
+ dl["tB"] = &o2;
+ dl["tC"] = &o3;
+ ex.extract (dss, 0, dl, nl, cl);
+
+ EXPECT_EQ (nl.to_string (),
+ "circuit TOP ();\n"
+ " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=0.8,PC=4.8,NE=1);\n"
+ "end;\n"
+ );
+ EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
+ EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2100,2000;2100,0/700,400;1600,400;1600,1300;700,1300)");
+ EXPECT_EQ (o3.to_string (), "(2100,0;2100,2000;2500,2000;2500,0)");
+}
+
+TEST(53_BJT3DeviceExtractorTestMultEmitter)
+{
+ db::Layout ly;
+
+ {
+ db::LoadLayoutOptions options;
+
+ std::string fn (tl::testdata ());
+ fn = tl::combine_path (fn, "algo");
+ fn = tl::combine_path (fn, "bjt3_4.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 l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
+ db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
+ db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
+ db::Region o1 (dss);
+ db::Region o2 (dss);
+ db::Region o3 (dss);
+
+ // perform the extraction
+
+ db::Netlist nl;
+ db::hier_clusters cl;
+
+ db::NetlistDeviceExtractorBJT3Transistor ex ("BJT3");
+
+ db::NetlistDeviceExtractor::input_layers dl;
+
+ dl["E"] = &l0;
+ dl["B"] = &l1;
+ dl["C"] = &l2;
+ dl["tE"] = &o1;
+ dl["tB"] = &o2;
+ dl["tC"] = &o3;
+ ex.extract (dss, 0, dl, nl, cl);
+
+ EXPECT_EQ (nl.to_string (),
+ "circuit TOP ();\n"
+ " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.5,PE=3,AB=10,PB=14,AC=10,PC=14,NE=1);\n"
+ " device BJT3 $2 (C=(null),B=(null),E=(null)) (AE=0.5,PE=3,AB=10,PB=14,AC=10,PC=14,NE=1);\n"
+ "end;\n"
+ );
+ EXPECT_EQ (o1.to_string (), "(1000,500;1000,1500;1500,1500;1500,500);(3500,500;3500,1500;4000,1500;4000,500)");
+ EXPECT_EQ (o2.to_string (), "(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500);(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500)");
+ EXPECT_EQ (o3.to_string (), "(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000);(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000)");
+}
+
+TEST(54_BJT4DeviceExtractorTest)
+{
+ db::Layout ly;
+
+ {
+ db::LoadLayoutOptions options;
+
+ std::string fn (tl::testdata ());
+ fn = tl::combine_path (fn, "algo");
+ fn = tl::combine_path (fn, "bjt4_1.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 l0 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(0, 0))), dss);
+ db::Region l1 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(1, 0))), dss);
+ db::Region l2 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(2, 0))), dss);
+ db::Region l3 (db::RecursiveShapeIterator (ly, tc, ly.get_layer (db::LayerProperties(3, 0))), dss);
+ db::Region o1 (dss);
+ db::Region o2 (dss);
+ db::Region o3 (dss);
+ db::Region o4 (dss);
+
+ // perform the extraction
+
+ db::Netlist nl;
+ db::hier_clusters cl;
+
+ db::NetlistDeviceExtractorBJT4Transistor ex ("BJT4");
+
+ db::NetlistDeviceExtractor::input_layers dl;
+
+ dl["E"] = &l0;
+ dl["B"] = &l1;
+ dl["C"] = &l2;
+ dl["S"] = &l3;
+ dl["tE"] = &o1;
+ dl["tB"] = &o2;
+ dl["tC"] = &o3;
+ dl["tS"] = &o4;
+ ex.extract (dss, 0, dl, nl, cl);
+
+ EXPECT_EQ (nl.to_string (),
+ "circuit TOP ();\n"
+ " device BJT4 $1 (C=(null),B=(null),E=(null),S=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n"
+ "end;\n"
+ );
+ EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)");
+ EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)");
+ EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000)");
+ EXPECT_EQ (o4.to_string (), "(0,0;0,2000;2500,2000;2500,0)");
+}
+
diff --git a/testdata/algo/bjt3_1.gds b/testdata/algo/bjt3_1.gds
new file mode 100644
index 0000000000000000000000000000000000000000..11a0abdc5060455b33d4a9f0b37573e3257bb7d9
GIT binary patch
literal 234
zcmZQzV_;&6V31*CVt>lO#UKoX{0ywfYz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+
zDuh9X6^}kPwh;dS1_l-m1_m}>CI&_ZHa;c>1_m7gW*~_K*e@_Ja2{b`fYC@&EP@FA
jKvNLKq&_V4HE~_VEybsGgue^l;Rh_
literal 0
HcmV?d00001
diff --git a/testdata/algo/bjt3_2.gds b/testdata/algo/bjt3_2.gds
new file mode 100644
index 0000000000000000000000000000000000000000..08c87c9ffc74cd8c2033b6314f05f01002ab1b14
GIT binary patch
literal 298
zcmZQzV_;&6V31*CVt>lO#UKoX{0w5qYz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+
zDuh9X6^}kPwh;dS1_l-m1_m}>CI&_ZHa;c>1_m7gW*~_K*e@_Ja2{b`fYC@&EP@FA
zObGq||NoKr|NkEkh=%Cp-T|Xw;vkvY4#FfcGqU|;~ztRg@u2Lufh2hw2u
K>_9iLFaQ9|TrK|q
literal 0
HcmV?d00001
diff --git a/testdata/algo/bjt3_3.gds b/testdata/algo/bjt3_3.gds
new file mode 100644
index 0000000000000000000000000000000000000000..11d454da87196f1203365429663ae8a71378b4f8
GIT binary patch
literal 298
zcmZQzV_;&6V31*CVt>lO#UKoX$_(1bYz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+
zDuh9X6^}kPwh;dS1_l-m1_m}>CI&_ZHa;c>1_m7gW*~_K*e@_Ja2{b`fYC@&EP@FA
zOvw5$HTzDVFJ<1y#q$W#KAPOexNH5_A~8aU|^iUzyP9IMSxNc2pT31q`~^x
Jfo@=7000KdCLsU-
literal 0
HcmV?d00001
diff --git a/testdata/algo/bjt3_4.gds b/testdata/algo/bjt3_4.gds
new file mode 100644
index 0000000000000000000000000000000000000000..d326300d8c75c64a2a44477aac7e50066e4bcb2e
GIT binary patch
literal 362
zcmZQzV_;&6V31*CVt>lO#UKoXrVQf9Yz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+
zDuh9X6^}kPwh;dS1_l-m1_m}>CI&_ZHa;c>1_m7gW*~_K*e@_J2zM|rz-S~X7D0r5
zCWQX~|Nrs8FarbU5e5d)8W;@|2hj}3`hl)M*w6fefr0T00|SU=y@QHj;y@awe%>_@
Y{U92opML>@2I+^&gY~lm{lLNi02_)r;{X5v
literal 0
HcmV?d00001
diff --git a/testdata/algo/bjt4_1.gds b/testdata/algo/bjt4_1.gds
new file mode 100644
index 0000000000000000000000000000000000000000..bffdecae4660d3aae2b7e5b063ba4ed57709f5ba
GIT binary patch
literal 362
zcmaKny$%6E6h_a^?#}vKA+}H`l|m^RK{P@_v4waeonE8UcmRb`)Lufb-iR?vCNq^O
z=1jhullu)4A=b1!5j`l;AfZd>z9|T~m`xz@!{K<_pWlwp!Svia>^CW`@Q){Dx?Dj}
z02vEBWK{s2ny+u?M0Y4&!O$56b?Xc3EA?tB^{(Xnx#hE=$#dfRx`o{@F5vATXPD^G
g$kODRuJ2o4sjU6$khLM-9iI$M{_XblBmIYf4{eJ?O8@`>
literal 0
HcmV?d00001
From 17a6459f833d2ebc9a803f7e01bbe6900eb2b20a Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Tue, 19 Oct 2021 21:05:09 +0200
Subject: [PATCH 10/52] Fixed #920 (fill border parameter sign issue)
---
src/lay/lay/layFillDialog.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lay/lay/layFillDialog.cc b/src/lay/lay/layFillDialog.cc
index c3cb63568..b9b6458e1 100644
--- a/src/lay/lay/layFillDialog.cc
+++ b/src/lay/lay/layFillDialog.cc
@@ -220,7 +220,7 @@ FillDialog::generate_fill (const FillParameters &fp)
// preprocess fill regions
if (distance_x != 0 || distance_y != 0) {
- fill_region.size (distance_x, distance_y);
+ fill_region.size (-distance_x, -distance_y);
} else {
fill_region.merge ();
}
From 4ecfd93c693c95ed947ce72f63cafae752eddde8 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Tue, 19 Oct 2021 22:50:05 +0200
Subject: [PATCH 11/52] Using Python3 for CentOS7 builds now
---
scripts/rpm-data/klayout.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/rpm-data/klayout.spec b/scripts/rpm-data/klayout.spec
index e326ed4ff..023bf38cf 100644
--- a/scripts/rpm-data/klayout.spec
+++ b/scripts/rpm-data/klayout.spec
@@ -52,7 +52,7 @@ Requires: qt5-qttools-devel >= 5.11.1
# CentOS7 requirements
%if "%{target_system}" == "centos7"
Requires: ruby >= 2.0.0
-Requires: python >= 2.7.5
+Requires: python >= 3.6.0
Requires: qt-x11 >= 4.8.5
%define buildopt -j2
%define pylib %{python_sitearch}
From 8a892cb6e25a4d5b083f2e7867502f877cbdf44e Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 20 Oct 2021 21:35:00 +0200
Subject: [PATCH 12/52] Fixed python3 dependency for CentOS7 RPM
---
scripts/rpm-data/klayout.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/rpm-data/klayout.spec b/scripts/rpm-data/klayout.spec
index 023bf38cf..072327cd1 100644
--- a/scripts/rpm-data/klayout.spec
+++ b/scripts/rpm-data/klayout.spec
@@ -52,7 +52,7 @@ Requires: qt5-qttools-devel >= 5.11.1
# CentOS7 requirements
%if "%{target_system}" == "centos7"
Requires: ruby >= 2.0.0
-Requires: python >= 3.6.0
+Requires: python3 >= 3.6.0
Requires: qt-x11 >= 4.8.5
%define buildopt -j2
%define pylib %{python_sitearch}
From f968751a44a5836c59e9f8741787acabaab55765 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 20 Oct 2021 22:21:56 +0200
Subject: [PATCH 13/52] RPM build script patch for Python3 @ CentOS7
---
scripts/rpm-data/klayout.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/rpm-data/klayout.spec b/scripts/rpm-data/klayout.spec
index 072327cd1..11bdb5614 100644
--- a/scripts/rpm-data/klayout.spec
+++ b/scripts/rpm-data/klayout.spec
@@ -55,7 +55,7 @@ Requires: ruby >= 2.0.0
Requires: python3 >= 3.6.0
Requires: qt-x11 >= 4.8.5
%define buildopt -j2
-%define pylib %{python_sitearch}
+%define pylib %{python3_sitearch}
%endif
%if "%{target_system}" == "centos6"
From 09df614d8a93afcc2ab7332e534cf91f9fd245e5 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Fri, 22 Oct 2021 23:52:35 +0200
Subject: [PATCH 14/52] Bugfix: array iterator must not iterate on empty search
box.
---
src/db/db/dbArray.h | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h
index a61f1a8cd..5834743c4 100644
--- a/src/db/db/dbArray.h
+++ b/src/db/db/dbArray.h
@@ -964,9 +964,7 @@ struct iterated_array
virtual std::pair *, bool>
begin_touching (const box_type &b) const
{
- if (b.empty ()) {
- return std::make_pair (new iterated_array_iterator (m_v.begin (), m_v.end ()), false);
- } else if (! b.touches (m_box)) {
+ if (b.empty () || ! b.touches (m_box)) {
return std::make_pair (new iterated_array_iterator (m_v.end (), m_v.end ()), false);
} else {
box_convert_type bc;
From c6bd8563311b0c3cebc5daa618d5df4fdf17acd7 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sun, 24 Oct 2021 00:05:50 +0200
Subject: [PATCH 15/52] WIP
---
src/db/db/dbHierProcessor.cc | 386 +++++++++++++++++++++++++++++++----
1 file changed, 348 insertions(+), 38 deletions(-)
diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc
index 33d9fbc7b..01d47e1ec 100644
--- a/src/db/db/dbHierProcessor.cc
+++ b/src/db/db/dbHierProcessor.cc
@@ -1015,9 +1015,141 @@ private:
}
};
+#if 1
+template
+struct interaction_registration_inst2inst
+ : db::box_scanner_receiver2
+{
+public:
+ typedef typename local_processor_cell_contexts::context_key_type interactions_value_type;
+ typedef std::unordered_map, interactions_value_type> interactions_type;
+
+ interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, interactions_type *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), m_foreign (foreign)
+ {
+ // nothing yet ..
+ }
+
+ void add (const db::CellInstArray *inst1, unsigned int id1, const db::CellInstArray *inst2, unsigned int id2)
+ {
+ // 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 || id1 != id2 || inst1->size () > 1) {
+
+ bool ignore = false;
+ if (mp_subject_layout == mp_intruder_layout && m_subject_layer == m_intruder_layer && ! m_foreign) {
+ 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) {
+ collect_instance_interactions (inst1, inst2);
+ }
+
+ }
+ }
+
+private:
+ const db::Layout *mp_subject_layout, *mp_intruder_layout;
+ unsigned int m_subject_layer, m_intruder_layer;
+ db::Coord m_dist;
+ interactions_type *mp_result;
+ std::unordered_set > m_interactions;
+ bool m_foreign;
+
+ void
+ collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
+ {
+printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
+ mp_subject_layout->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert (*mp_subject_layout)).to_string()).c_str(),
+ mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert (*mp_intruder_layout)).to_string()).c_str()); // @@@
+ // TODO: this algorithm is not in particular effective for identical arrays
+
+ const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
+ const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
+ db::box_convert inst2_bc (*mp_intruder_layout, m_intruder_layer);
+
+ std::unordered_map interactions_cache;
+
+ 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 (m_subject_layer).enlarged (db::Vector (m_dist, m_dist));
+
+ std::set *insts = 0;
+
+ if (! ibox1.empty ()) {
+
+ // TODO: in some cases, it may be possible to optimize this for arrays
+
+ for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -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);
+
+ // NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
+ db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
+
+ db::ICplxTrans tn21 = tni1 * tn2;
+
+ db::Box cbox = ibox1 & ibox2;
+ if (! cbox.empty ()) {
+
+ bool interacts = false;
+
+ std::unordered_map::const_iterator ic = interactions_cache.find (tn21);
+ if (ic != interactions_cache.end ()) {
+
+ interacts = ic->second;
+
+ } else {
+
+ 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
+ // NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
+ // objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
+ // argument)
+ interacts = (! db::RecursiveShapeIterator (*mp_subject_layout, cell1, m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
+ ! db::RecursiveShapeIterator (*mp_intruder_layout, cell2, m_intruder_layer, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ());
+ interactions_cache.insert (std::make_pair (tn21, interacts));
+
+ }
+
+ if (interacts) {
+ if (! insts) {
+ insts = & (*mp_result) [std::make_pair (cell1.cell_index (), tn1)].first;
+ }
+ insts->insert (db::CellInstArray (db::CellInst (cell2.cell_index ()), tn21));
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+ }
+};
+#else
+
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, db::Coord dist)
{
+printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
+ layout1->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert (*layout1)).to_string()).c_str(),
+ layout2->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert (*layout2)).to_string()).c_str()); // @@@
// TODO: this algorithm is not in particular effective for identical arrays
const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ());
@@ -1124,7 +1256,69 @@ private:
std::unordered_set > m_interactions;
bool m_foreign;
};
+#endif
+#if 1
+template
+struct interaction_registration_inst2shape
+ : db::box_scanner_receiver2
+{
+public:
+ typedef typename local_processor_cell_contexts::context_key_type interactions_value_type;
+ typedef std::unordered_map, interactions_value_type> interactions_type;
+
+ interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *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, unsigned int, const TI *ref, unsigned int layer)
+ {
+ collect_instance_shape_interactions (inst, layer, *ref, m_dist);
+ }
+
+private:
+ db::Layout *mp_subject_layout;
+ unsigned int m_subject_layer;
+ db::Coord m_dist;
+ interactions_type *mp_result;
+
+ void
+ collect_instance_shape_interactions (const db::CellInstArray *inst, unsigned int layer, const TI &ref, db::Coord dist)
+ {
+ const db::Cell &cell = mp_subject_layout->cell (inst->object ().cell_index ());
+ db::box_convert inst_bc (*mp_subject_layout, m_subject_layer);
+ db::Box rbox = db::box_convert () (ref);
+
+ for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
+
+ db::ICplxTrans tn = inst->complex_trans (*n);
+ db::Box cbox = (tn * cell.bbox (m_subject_layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
+
+ if (! cbox.empty ()) {
+
+ db::ICplxTrans tni = tn.inverted ();
+ db::shape_reference_translator_with_trans rt (mp_subject_layout, tni);
+
+ std::set *shapes = 0;
+
+ // not very strong, but already useful: the cells interact if there is a layer in cell
+ // in the common box
+ // NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
+ // dot edges. Instead safe-shrink the search box and use touching mode.
+ for (db::RecursiveShapeIterator s (*mp_subject_layout, cell, m_subject_layer, safe_box_enlarged (tni * cbox, -1, -1), false); !s.at_end (); ++s) {
+ if (! shapes) {
+ shapes = & (*mp_result) [std::make_pair (cell.cell_index (), tn)].second [layer];
+ }
+ shapes->insert (rt (ref));
+ }
+ }
+
+ }
+ }
+};
+#else
template
static bool
instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const T &ref, db::Coord dist)
@@ -1181,7 +1375,7 @@ private:
db::Coord m_dist;
std::unordered_map, std::map > > > *mp_result;
};
-
+#endif
}
// ---------------------------------------------------------------------------------------------
@@ -1504,9 +1698,119 @@ void local_processor::compute_contexts (local_processor_contextsbegin ().at_end ()) {
- typedef std::pair, std::map > > interaction_value_type;
+#if 1
+ // Key: single instance given by cell index and transformation
+ // Value the contexts for the child cell for this instance
+ typedef typename local_processor_cell_contexts::context_key_type interactions_value_type;
+ typedef std::unordered_map, interactions_value_type> interactions_type;
+ interactions_type 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
+ // 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 ()) {
+ db::cell_index_type ci = i->cell_inst ().object ().cell_index ();
+ for (db::CellInstArray::iterator n = i->begin (); ! n.at_end (); ++n) {
+ db::ICplxTrans tn = i->complex_trans (*n);
+ interactions.insert (std::make_pair (std::make_pair (ci, tn), interactions_value_type ()));
+ }
+ }
+ }
+
+ // TODO: can we shortcut this if interactions is empty?
+ for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
+
+ db::box_convert inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
+
+ db::box_scanner2 scanner;
+ interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
+
+ unsigned int id = 0;
+
+ 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 them 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 () && ! subject_cell_is_breakout (i->cell_index ())) {
+ scanner.insert1 (&i->cell_inst (), iid);
+ }
+ if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
+ 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 () && ! subject_cell_is_breakout (i->cell_index ())) {
+ 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 () && ! intruder_cell_is_breakout (i->cell_index ())) {
+ scanner.insert2 (&i->cell_inst (), ++id);
+ }
+ }
+ }
+
+ }
+
+ std::list instance_heap;
+
+ for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
+ if (! inst_bci (*i).empty ()) {
+ scanner.insert2 (i.operator-> (), ++id);
+ }
+ }
+
+ scanner.process (rec, dist, inst_bcs, inst_bci);
+
+ }
+
+// TODO: can we shortcut this if interactions is empty?
+ {
+ db::box_scanner2 scanner;
+ interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
+
+ for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
+ if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
+ scanner.insert1 (&i->cell_inst (), 0);
+ }
+ }
+
+ for (typename std::map >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) {
+ for (typename std::set::const_iterator i = il->second.begin (); i != il->second.end (); ++i) {
+ scanner.insert2 (i.operator-> (), il->first);
+ }
+ }
+
+ for (std::map::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) {
+ for (db::Shapes::shape_iterator i = im->second->begin (shape_flags ()); !i.at_end (); ++i) {
+ scanner.insert2 (i->basic_ptr (typename TI::tag ()), im->first);
+ }
+ }
+
+ scanner.process (rec, dist, inst_bcs, db::box_convert ());
+ }
+
+ // produce the tasks for computing the next-level interactions
+ for (typename interactions_type::iterator i = interactions.begin (); i != interactions.end (); ++i) {
+
+ db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
+ db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
+
+ issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
+
+ }
+
+#else
+ typedef std::pair, std::map > > interaction_value_type;
// 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
@@ -1560,6 +1864,8 @@ void local_processor::compute_contexts (local_processor_contexts instance_heap;
+
for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++id);
@@ -1601,57 +1907,61 @@ void local_processor::compute_contexts (local_processor_contexts > effective_instance_cache_type;
effective_instance_cache_type effective_instance_cache;
+size_t n = 0, nmax = interactions.size(); // @@@
for (typename std::unordered_map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
+printf("@@@ %d/%d -> #%d,#%d\n", int(n++), int(nmax), int (i->second.first.size()), int(i->second.second.size())); fflush(stdout);
- db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ());
+ db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first->object ().cell_index ());
+ db::Box sc_bbox = subject_child_cell->bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist));
+ if (sc_bbox.empty ()) {
+ continue;
+ }
+
+ db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
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 * subject_child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist));
+ db::Box nbox = tn * sc_bbox;
- if (! nbox.empty ()) {
+ typename local_processor_cell_contexts::context_key_type intruders_below;
- typename local_processor_cell_contexts::context_key_type intruders_below;
+ db::shape_reference_translator_with_trans rt (mp_subject_layout, tni);
- db::shape_reference_translator_with_trans rt (mp_subject_layout, tni);
-
- for (typename std::map >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) {
- std::set &out = intruders_below.second [pl->first];
- for (typename std::unordered_set::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) {
- if (nbox.overlaps (db::box_convert () (*p))) {
- out.insert (rt (*p));
- }
+ for (typename std::map >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) {
+ std::set &out = intruders_below.second [pl->first];
+ for (typename std::unordered_set::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) {
+ if (nbox.overlaps (db::box_convert () (*p))) {
+ out.insert (rt (*p));
}
}
+ }
- // 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::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
+ for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
- unsigned int ail = contexts.actual_intruder_layer (*il);
- db::box_convert inst_bcii (*mp_intruder_layout, ail);
+ unsigned int ail = contexts.actual_intruder_layer (*il);
+ db::box_convert inst_bcii (*mp_intruder_layout, ail);
- for (std::unordered_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 (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
+ for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
- db::ICplxTrans tk = (*j)->complex_trans (*k);
- // NOTE: no self-interactions
- if (i->first != *j || tn != tk) {
-
- // optimize the intruder instance so it will be as low as possible
- effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
- effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
- if (cached == effective_instance_cache.end ()) {
- std::pair ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
- cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
- }
- if (cached->second.first) {
- intruders_below.first.insert (cached->second.second);
- }
+ db::ICplxTrans tk = (*j)->complex_trans (*k);
+ // NOTE: no self-interactions
+ if (i->first != *j || tn != tk) {
+ // optimize the intruder instance so it will be as low as possible
+ effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
+ effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
+ if (cached == effective_instance_cache.end ()) {
+ std::pair ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
+ cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
+ }
+ if (cached->second.first) {
+ intruders_below.first.insert (cached->second.second);
}
}
@@ -1660,14 +1970,14 @@ void local_processor::compute_contexts (local_processor_contexts
Date: Sun, 24 Oct 2021 20:28:15 +0200
Subject: [PATCH 16/52] WIP
---
src/db/db/dbHierProcessor.cc | 475 ++++++-----------------------------
1 file changed, 78 insertions(+), 397 deletions(-)
diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc
index 01d47e1ec..e8dc9dc1b 100644
--- a/src/db/db/dbHierProcessor.cc
+++ b/src/db/db/dbHierProcessor.cc
@@ -1015,7 +1015,6 @@ private:
}
};
-#if 1
template
struct interaction_registration_inst2inst
: db::box_scanner_receiver2
@@ -1064,16 +1063,18 @@ private:
void
collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
{
+#if 0
printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
mp_subject_layout->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert (*mp_subject_layout)).to_string()).c_str(),
mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert (*mp_intruder_layout)).to_string()).c_str()); // @@@
+#endif
// TODO: this algorithm is not in particular effective for identical arrays
const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
db::box_convert inst2_bc (*mp_intruder_layout, m_intruder_layer);
- std::unordered_map interactions_cache;
+ std::unordered_map > > interactions_cache;
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
@@ -1099,38 +1100,26 @@ printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
- db::ICplxTrans tn21 = tni1 * tn2;
-
db::Box cbox = ibox1 & ibox2;
- if (! cbox.empty ()) {
+ if (! cbox.empty () && ! db::RecursiveShapeIterator (*mp_subject_layout, cell1, m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end ()) {
- bool interacts = false;
+ db::ICplxTrans tn21 = tni1 * tn2;
- std::unordered_map::const_iterator ic = interactions_cache.find (tn21);
+ std::list > *interactions = 0;
+
+ std::unordered_map > >::iterator ic = interactions_cache.find (tn21);
if (ic != interactions_cache.end ()) {
-
- interacts = ic->second;
-
+ interactions = &ic->second;
} else {
-
- 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
- // NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
- // objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
- // argument)
- interacts = (! db::RecursiveShapeIterator (*mp_subject_layout, cell1, m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
- ! db::RecursiveShapeIterator (*mp_intruder_layout, cell2, m_intruder_layer, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ());
- interactions_cache.insert (std::make_pair (tn21, interacts));
-
+ interactions = &interactions_cache [tn21];
+ collect_intruder_tree_interactions (cell1, cell2, tni1, tn21, cbox, *interactions);
}
- if (interacts) {
+ for (std::list >::const_iterator i = interactions->begin (); i != interactions->end (); ++i) {
if (! insts) {
insts = & (*mp_result) [std::make_pair (cell1.cell_index (), tn1)].first;
}
- insts->insert (db::CellInstArray (db::CellInst (cell2.cell_index ()), tn21));
+ insts->insert (db::CellInstArray (db::CellInst (i->first), i->second));
}
}
@@ -1141,124 +1130,98 @@ printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
}
}
-};
-#else
-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, db::Coord dist)
-{
-printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
- layout1->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert (*layout1)).to_string()).c_str(),
- layout2->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert (*layout2)).to_string()).c_str()); // @@@
- // TODO: this algorithm is not in particular effective for identical arrays
+// @@@
+ bool has_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox)
+ {
+ db::ICplxTrans tni2 = tn21.inverted () * tni1;
+ db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
- 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);
+ if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
- std::unordered_set relative_trans_seen;
+ // we're overlapping with shapes from the intruder - do not recursive further
+ return true;
- 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).enlarged (db::Vector (dist, dist));
+ for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
- if (! ibox1.empty ()) {
+ const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
- // TODO: in some cases, it may be possible to optimize this for arrays
+ for (db::CellInstArray::iterator ia = i->begin_touching (tbox2, mp_intruder_layout); ! ia.at_end (); ++ia) {
- for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
+ db::ICplxTrans it = i->complex_trans (*ia);
- if (inst1 == inst2 && *n == *k) {
- // skip self-interactions - this is handled inside the cell
- continue;
- }
+ db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
+ db::Box ccbox = cbox & ibox2;
- db::ICplxTrans tn2 = inst2->complex_trans (*k);
-
- // NOTE: we need to enlarge both subject *and* intruder boxes - either object 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) {
- // 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
- // NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
- // objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
- // argument)
- if (! db::RecursiveShapeIterator (*layout1, cell1, layer1, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
- ! db::RecursiveShapeIterator (*layout2, cell2, layer2, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ()) {
+ if (! ccbox.empty () && ! db::RecursiveShapeIterator (*mp_subject_layout, subject_cell, m_subject_layer, safe_box_enlarged (tni1 * ccbox, -1, -1), false).at_end ()) {
+ if (has_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox)) {
return true;
}
-
}
}
}
+ return false;
}
+// @@@
- return false;
-}
-
-template
-struct interaction_registration_inst2inst
- : db::box_scanner_receiver2
-{
-public:
- typedef std::pair, std::map > > interaction_value_type;
-
- interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, 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), m_foreign (foreign)
+ void collect_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox, std::list > &interactions)
{
- // nothing yet ..
- }
+#if 0
+ if (has_intruder_tree_interactions (subject_cell, intruder_cell, tni1, tn21, cbox)) {
+ interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
+ }
+#elif 1
+ db::ICplxTrans tni2 = tn21.inverted () * tni1;
+ db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
- void add (const db::CellInstArray *inst1, unsigned int id1, const db::CellInstArray *inst2, unsigned int id2)
- {
- // 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 || id1 != id2 || inst1->size () > 1) {
+ if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
- bool ignore = false;
- if (mp_subject_layout == mp_intruder_layout && m_subject_layer == m_intruder_layer && ! m_foreign) {
- 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));
+ // we're overlapping with shapes from the intruder - do not recursive further
+ interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
+ return;
+
+ }
+
+ for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
+
+ const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
+
+ for (db::CellInstArray::iterator ia = i->begin_touching (tbox2, mp_intruder_layout); ! ia.at_end (); ++ia) {
+
+ db::ICplxTrans it = i->complex_trans (*ia);
+
+ db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
+ db::Box ccbox = cbox & ibox2;
+
+ if (! ccbox.empty () && ! db::RecursiveShapeIterator (*mp_subject_layout, subject_cell, m_subject_layer, safe_box_enlarged (tni1 * ccbox, -1, -1), false).at_end ()) {
+ collect_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox, interactions);
}
- }
- 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);
}
}
- }
+#else
+ // 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
+ // NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
+ // objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
+ // argument)
+ db::ICplxTrans tni2 = tn21.inverted () * tni1;
+ bool interacts = (! db::RecursiveShapeIterator (*mp_subject_layout, subject_cell, m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
+ ! db::RecursiveShapeIterator (*mp_intruder_layout, intruder_cell, m_intruder_layer, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ());
-private:
- const db::Layout *mp_subject_layout, *mp_intruder_layout;
- unsigned int m_subject_layer, m_intruder_layer;
- db::Coord m_dist;
- std::unordered_map, std::map > > > *mp_result;
- std::unordered_set > m_interactions;
- bool m_foreign;
-};
+ if (interacts) {
+ interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
+ }
#endif
+ }
+};
-#if 1
template
struct interaction_registration_inst2shape
: db::box_scanner_receiver2
@@ -1318,64 +1281,7 @@ private:
}
}
};
-#else
-template
-static bool
-instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const T &ref, db::Coord dist)
-{
- const db::Cell &cell = layout->cell (inst->object ().cell_index ());
- db::box_convert inst_bc (*layout, layer);
- db::Box rbox = db::box_convert () (ref);
- for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
-
- db::ICplxTrans tn = inst->complex_trans (*n);
- db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
-
- 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
- // NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
- // dot edges. Instead safe-shrink the search box and use touching mode.
- if (! db::RecursiveShapeIterator (*layout, cell, layer, safe_box_enlarged (tni * cbox, -1, -1), false).at_end ()) {
- return true;
- }
-
- }
-
- }
-
- return false;
-}
-
-template
-struct interaction_registration_inst2shape
- : db::box_scanner_receiver2
-{
-public:
- interaction_registration_inst2shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map, std::map > > > *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, unsigned int, const T *ref, unsigned int layer)
- {
- if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) {
- (*mp_result) [inst].second [layer].insert (*ref);
- }
- }
-
-private:
- const db::Layout *mp_subject_layout;
- unsigned int m_subject_layer;
- db::Coord m_dist;
- std::unordered_map, std::map > > > *mp_result;
-};
-#endif
}
// ---------------------------------------------------------------------------------------------
@@ -1630,6 +1536,9 @@ void local_processor::compute_contexts (local_processor_contexts::context_key_type &intruders,
db::Coord dist) const
{
+#if 0 // @@@
+printf("@@@ --- compute_contexts (%s @ %s)\n", mp_subject_layout->cell_name (subject_cell->cell_index ()), subject_cell_inst.to_string().c_str()); fflush(stdout);
+#endif
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
if (tl::verbosity () >= m_base_verbosity + 20) {
@@ -1698,7 +1607,6 @@ void local_processor::compute_contexts (local_processor_contextsbegin ().at_end ()) {
-#if 1
// Key: single instance given by cell index and transformation
// Value the contexts for the child cell for this instance
typedef typename local_processor_cell_contexts::context_key_type interactions_value_type;
@@ -1773,7 +1681,7 @@ void local_processor::compute_contexts (local_processor_contexts scanner;
interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
@@ -1809,237 +1717,10 @@ void local_processor::compute_contexts (local_processor_contexts, std::map > > interaction_value_type;
-
- // 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
- // 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 ()));
- }
- }
-
-// TODO: can we shortcut this if interactions is empty?
- for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
-
- db::box_convert inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
-
- db::box_scanner2 scanner;
- interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
-
- unsigned int id = 0;
-
- 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 them 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 () && ! subject_cell_is_breakout (i->cell_index ())) {
- scanner.insert1 (&i->cell_inst (), iid);
- }
- if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
- 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 () && ! subject_cell_is_breakout (i->cell_index ())) {
- 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 () && ! intruder_cell_is_breakout (i->cell_index ())) {
- scanner.insert2 (&i->cell_inst (), ++id);
- }
- }
- }
-
- }
-
- std::list instance_heap;
-
- for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
- if (! inst_bci (*i).empty ()) {
- scanner.insert2 (i.operator-> (), ++id);
- }
- }
-
- scanner.process (rec, dist, inst_bcs, inst_bci);
-
- }
-
-// TODO: can we shortcut this if interactions is empty?
- {
- db::box_scanner2 scanner;
- interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
-
- for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
- if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
- scanner.insert1 (&i->cell_inst (), 0);
- }
- }
-
- for (typename std::map >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) {
- for (typename std::set::const_iterator i = il->second.begin (); i != il->second.end (); ++i) {
- scanner.insert2 (i.operator-> (), il->first);
- }
- }
-
- for (std::map::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) {
- for (db::Shapes::shape_iterator i = im->second->begin (shape_flags ()); !i.at_end (); ++i) {
- scanner.insert2 (i->basic_ptr (typename TI::tag ()), im->first);
- }
- }
-
- scanner.process (rec, dist, inst_bcs, db::box_convert ());
- }
-
- // this cache should reduce the effort of checking array vs. array
- typedef std::pair > effective_instance_cache_key_type;
- typedef std::map > effective_instance_cache_type;
- effective_instance_cache_type effective_instance_cache;
-
-size_t n = 0, nmax = interactions.size(); // @@@
- for (typename std::unordered_map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
-printf("@@@ %d/%d -> #%d,#%d\n", int(n++), int(nmax), int (i->second.first.size()), int(i->second.second.size())); fflush(stdout);
-
- db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first->object ().cell_index ());
- db::Box sc_bbox = subject_child_cell->bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist));
- if (sc_bbox.empty ()) {
- continue;
- }
-
- db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
-
- 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 * sc_bbox;
-
- typename local_processor_cell_contexts::context_key_type intruders_below;
-
- db::shape_reference_translator_with_trans rt (mp_subject_layout, tni);
-
- for (typename std::map >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) {
- std::set &out = intruders_below.second [pl->first];
- for (typename std::unordered_set::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) {
- if (nbox.overlaps (db::box_convert () (*p))) {
- out.insert (rt (*p));
- }
- }
- }
-
- // TODO: in some cases, it may be possible to optimize this for arrays
-
- for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
-
- unsigned int ail = contexts.actual_intruder_layer (*il);
- db::box_convert inst_bcii (*mp_intruder_layout, ail);
-
- 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 (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
-
- db::ICplxTrans tk = (*j)->complex_trans (*k);
- // NOTE: no self-interactions
- if (i->first != *j || tn != tk) {
-
- // optimize the intruder instance so it will be as low as possible
- effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
- effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
- if (cached == effective_instance_cache.end ()) {
- std::pair ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
- cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
- }
- if (cached->second.first) {
- intruders_below.first.insert (cached->second.second);
- }
-
- }
-
- }
-
- }
-
- }
-
- issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, tn, intruder_child_cell, intruders_below, dist);
-
- }
-
- }
-#endif
-
}
}
-/**
- * @brief Returns a cell instance array suitable for adding as intruder
- *
- * The given intruder cell with the transformation ti2s - which transforms the intruder instance into
- * the coordinate system of the subject cell - is analysed and either this instance or a sub-instance
- * is chosen.
- * Sub-instances are chosen if the intruder cell does not have shapes which interact with the subject
- * cell and there is exactly one sub-instance interacting with the subject cell.
- */
-template
-std::pair
-local_processor::effective_instance (unsigned int subject_layer, db::cell_index_type subject_cell_index, unsigned int intruder_layer, db::cell_index_type intruder_cell_index, const db::ICplxTrans &ti2s, db::Coord dist) const
-{
- db::Box bbox = safe_box_enlarged (mp_subject_layout->cell (subject_cell_index).bbox (subject_layer), dist - 1, dist - 1);
- if (bbox.empty ()) {
- // should not happen, but skip if it does
- return std::make_pair (false, db::CellInstArray ());
- }
-
- db::Box ibbox = bbox.transformed (ti2s.inverted ());
-
- const db::Cell &intruder_cell = mp_intruder_layout->cell (intruder_cell_index);
- const db::Shapes &intruder_shapes = intruder_cell.shapes (intruder_layer);
- if (! intruder_shapes.empty () && ! intruder_shapes.begin_touching (ibbox, db::ShapeIterator::All).at_end ()) {
- return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
- }
-
- db::box_convert inst_bcii (*mp_intruder_layout, intruder_layer);
-
- size_t ni = 0;
- db::cell_index_type eff_cell_index = 0;
- db::ICplxTrans eff_trans;
-
- for (db::Cell::touching_iterator i = intruder_cell.begin_touching (ibbox); ! i.at_end() && ni < 2; ++i) {
- const db::CellInstArray &ci = i->cell_inst ();
- db::Box cbox = mp_intruder_layout->cell (ci.object ().cell_index ()).bbox (intruder_layer);
- for (db::CellInstArray::iterator k = ci.begin_touching (ibbox, inst_bcii); ! k.at_end () && ni < 2; ++k) {
- db::ICplxTrans tk = ci.complex_trans (*k);
- if (ibbox.overlaps (cbox.transformed (tk))) {
- eff_trans = tk;
- eff_cell_index = ci.object ().cell_index ();
- ++ni;
- }
- }
- }
-
- if (ni == 0) {
- // should not happen, but skip if it does
- return std::make_pair (false, db::CellInstArray ());
- } else if (ni == 1) {
- // one instance - dive down
- return effective_instance (subject_layer, subject_cell_index, intruder_layer, eff_cell_index, ti2s * eff_trans, dist);
- } else {
- return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
- }
-}
-
template
void
local_processor::compute_results (local_processor_contexts &contexts, const local_operation *op, const std::vector &output_layers) const
From 596080669da7ad1dc172db50c13a0b7009317a76 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Tue, 26 Oct 2021 23:10:29 +0200
Subject: [PATCH 17/52] WIP
---
src/db/db/dbArray.h | 18 ++++-------
src/db/db/dbHierProcessor.cc | 62 ++----------------------------------
src/db/db/dbTrans.h | 9 ++++++
src/db/db/gsiDeclDbTrans.cc | 10 +++++-
4 files changed, 28 insertions(+), 71 deletions(-)
diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h
index 5834743c4..ba2efe78a 100644
--- a/src/db/db/dbArray.h
+++ b/src/db/db/dbArray.h
@@ -1697,7 +1697,7 @@ struct array
* an appropriate basic_array object using a complex transformation.
*/
array (const Obj &obj, const complex_trans_type &ct, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
- : m_obj (obj), m_trans (ct), mp_base (new regular_complex_array (ct.rcos (), ct.mag (), a, b, amax, bmax))
+ : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new regular_complex_array (ct.rcos (), ct.mag (), a, b, amax, bmax) : new regular_array (a, b, amax, bmax))
{
// .. nothing yet ..
}
@@ -1725,7 +1725,7 @@ struct array
* it's own storage.
*/
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
- : m_obj (obj), m_trans (ct), mp_base (rep.insert (regular_complex_array (ct.rcos (), ct.mag (), a, b, amax, bmax)))
+ : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (regular_complex_array (ct.rcos (), ct.mag (), a, b, amax, bmax)) : rep.insert (regular_array (a, b, amax, bmax)))
{
// .. nothing yet ..
}
@@ -1764,7 +1764,7 @@ struct array
*/
template
array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to)
- : m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array (ct.rcos (), ct.mag (), from, to))
+ : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new iterated_complex_array (ct.rcos (), ct.mag (), from, to) : new iterated_array (from, to))
{
// .. nothing yet ..
}
@@ -1777,7 +1777,7 @@ struct array
*/
explicit
array (const Obj &obj, const complex_trans_type &ct)
- : m_obj (obj), m_trans (ct), mp_base (new single_complex_inst (ct.rcos (), ct.mag ()))
+ : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new single_complex_inst (ct.rcos (), ct.mag ()) : 0)
{
// .. nothing yet ..
}
@@ -1807,7 +1807,7 @@ struct array
*/
explicit
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep)
- : m_obj (obj), m_trans (ct), mp_base (rep.insert (single_complex_inst (ct.rcos (), ct.mag ())))
+ : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (single_complex_inst (ct.rcos (), ct.mag ())) : 0)
{
// .. nothing yet ..
}
@@ -1888,17 +1888,13 @@ struct array
array_iterator begin_touching (const box_type &b, const BoxConv &bc) const
{
if (b.empty ()) {
- if (mp_base) {
- return array_iterator (m_trans, mp_base->begin_touching (box_type ()));
- } else {
- return array_iterator (m_trans, true);
- }
+ return array_iterator (m_trans, true);
} else if (b == box_type::world ()) {
return begin ();
} else if (mp_base) {
box_type ob (bc (m_obj));
if (ob.empty ()) {
- return array_iterator (m_trans, mp_base->begin_touching (box_type ()));
+ return array_iterator (m_trans, true);
} else {
if (mp_base->is_complex ()) {
complex_trans_type ct = mp_base->complex_trans (simple_trans_type (m_trans));
diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc
index e8dc9dc1b..3b589f420 100644
--- a/src/db/db/dbHierProcessor.cc
+++ b/src/db/db/dbHierProcessor.cc
@@ -1076,6 +1076,9 @@ printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
std::unordered_map > > interactions_cache;
+#if 0
+printf("@@@ -> #%d\n", int(inst1->size()));
+#endif
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn1 = inst1->complex_trans (*n);
@@ -1131,51 +1134,8 @@ printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
}
}
-// @@@
- bool has_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox)
- {
- db::ICplxTrans tni2 = tn21.inverted () * tni1;
- db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
-
- if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
-
- // we're overlapping with shapes from the intruder - do not recursive further
- return true;
-
- }
-
- for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
-
- const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
-
- for (db::CellInstArray::iterator ia = i->begin_touching (tbox2, mp_intruder_layout); ! ia.at_end (); ++ia) {
-
- db::ICplxTrans it = i->complex_trans (*ia);
-
- db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
- db::Box ccbox = cbox & ibox2;
-
- if (! ccbox.empty () && ! db::RecursiveShapeIterator (*mp_subject_layout, subject_cell, m_subject_layer, safe_box_enlarged (tni1 * ccbox, -1, -1), false).at_end ()) {
- if (has_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox)) {
- return true;
- }
- }
-
- }
-
- }
-
- return false;
- }
-// @@@
-
void collect_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox, std::list > &interactions)
{
-#if 0
- if (has_intruder_tree_interactions (subject_cell, intruder_cell, tni1, tn21, cbox)) {
- interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
- }
-#elif 1
db::ICplxTrans tni2 = tn21.inverted () * tni1;
db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
@@ -1205,20 +1165,6 @@ printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
}
}
-#else
- // 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
- // NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
- // objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
- // argument)
- db::ICplxTrans tni2 = tn21.inverted () * tni1;
- bool interacts = (! db::RecursiveShapeIterator (*mp_subject_layout, subject_cell, m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
- ! db::RecursiveShapeIterator (*mp_intruder_layout, intruder_cell, m_intruder_layer, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ());
-
- if (interacts) {
- interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
- }
-#endif
}
};
@@ -1669,8 +1615,6 @@ printf("@@@ --- compute_contexts (%s @ %s)\n", mp_subject_layout->cell_name (sub
}
- std::list instance_heap;
-
for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++id);
diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h
index 93a6b08a7..c08915e74 100644
--- a/src/db/db/dbTrans.h
+++ b/src/db/db/dbTrans.h
@@ -2011,6 +2011,15 @@ public:
m_mag = m_mag < 0.0 ? -m : m;
}
+ /**
+ * @brief Returns a value indicating whether the transformation is a complex one
+ * The transformation can safely be converted to a simple transformation if this value is false.
+ */
+ bool is_complex () const
+ {
+ return is_mag () || ! is_ortho ();
+ }
+
/**
* @brief Test, if the transformation is mirroring
*/
diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc
index 0ebbb4d0b..a14eaa66b 100644
--- a/src/db/db/gsiDeclDbTrans.cc
+++ b/src/db/db/gsiDeclDbTrans.cc
@@ -916,7 +916,7 @@ struct cplx_trans_defs
"@brief Gets the magnification\n"
) +
method ("is_mag?", &C::is_mag,
- "@brief Test, if the transformation is a magnifying one\n"
+ "@brief Tests, if the transformation is a magnifying one\n"
"\n"
"This is the recommended test for checking if the transformation represents\n"
"a magnification.\n"
@@ -925,6 +925,14 @@ struct cplx_trans_defs
"@brief Sets the magnification\n"
"@param m The new magnification"
) +
+ method ("is_complex?", &C::is_complex,
+ "@brief Return true if the transformation is a complex one\n"
+ "\n"
+ "If this value is false, the transformation can safely be converted to a simple transformation.\n"
+ "The return value is equivalent to 'is_mag || !is_ortho'.\n"
+ "\n"
+ "This method has been introduced in version 0.27.5."
+ ) +
method ("R0", &trans_r0,
"@brief A constant giving \"unrotated\" (unit) transformation\n"
"The previous integer constant has been turned into a transformation in version 0.25."
From 20f3733c58e7edee5234dcd343dfec87e7d01d5c Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 27 Oct 2021 00:38:08 +0200
Subject: [PATCH 18/52] WIP: lean recursive shape touch check
---
src/db/db/dbCell.cc | 35 ++++++++++++++++++++++++
src/db/db/dbCell.h | 5 ++++
src/db/db/dbHierProcessor.cc | 6 ++---
src/db/unit_tests/dbCellTests.cc | 46 ++++++++++++++++++++++++++++++++
4 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc
index 346f3d8ab..906752112 100644
--- a/src/db/db/dbCell.cc
+++ b/src/db/db/dbCell.cc
@@ -487,6 +487,41 @@ Cell::hierarchy_levels () const
return m_hier_levels;
}
+static bool
+has_shapes_touching_impl (const db::Cell &cell, unsigned int layer, const db::Box &box)
+{
+ if (! cell.shapes (layer).begin_touching (box, db::ShapeIterator::All).at_end ()) {
+ return true;
+ }
+
+ for (db::Cell::touching_iterator i = cell.begin_touching (box); ! i.at_end (); ++i) {
+
+ for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (box, db::box_convert (*cell.layout (), layer)); ! ia.at_end (); ++ia) {
+
+ db::Box cbox;
+ if (i->is_complex ()) {
+ cbox = i->complex_trans (*ia).inverted () * box;
+ } else {
+ cbox = (*ia).inverted () * box;
+ }
+
+ if (has_shapes_touching_impl (cell.layout ()->cell (i->cell_index ()), layer, cbox)) {
+ return true;
+ }
+
+ }
+
+ }
+
+ return false;
+}
+
+bool
+Cell::has_shapes_touching (unsigned int layer, const db::Box &box) const
+{
+ return has_shapes_touching_impl (*this, layer, box);
+}
+
void
Cell::collect_caller_cells (std::set &callers) const
{
diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h
index b20803953..db7089b2a 100644
--- a/src/db/db/dbCell.h
+++ b/src/db/db/dbCell.h
@@ -721,6 +721,11 @@ public:
return shapes (layer).begin_touching (box, flags, prop_sel, inv_prop_sel);
}
+ /**
+ * @brief A quick, recursive test whether the cell has shapes touching the given box on the given layer
+ */
+ bool has_shapes_touching (unsigned int layer, const db::Box &box) const;
+
/**
* @brief Collect all calling cells (either calling this cell directly or indirectly)
*
diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc
index 3b589f420..1bb829fd8 100644
--- a/src/db/db/dbHierProcessor.cc
+++ b/src/db/db/dbHierProcessor.cc
@@ -1104,7 +1104,7 @@ printf("@@@ -> #%d\n", int(inst1->size()));
db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box cbox = ibox1 & ibox2;
- if (! cbox.empty () && ! db::RecursiveShapeIterator (*mp_subject_layout, cell1, m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end ()) {
+ if (! cbox.empty () && cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1))) {
db::ICplxTrans tn21 = tni1 * tn2;
@@ -1151,14 +1151,14 @@ printf("@@@ -> #%d\n", int(inst1->size()));
const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
- for (db::CellInstArray::iterator ia = i->begin_touching (tbox2, mp_intruder_layout); ! ia.at_end (); ++ia) {
+ for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (tbox2, db::box_convert (*mp_intruder_layout, m_intruder_layer)); ! ia.at_end (); ++ia) {
db::ICplxTrans it = i->complex_trans (*ia);
db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box ccbox = cbox & ibox2;
- if (! ccbox.empty () && ! db::RecursiveShapeIterator (*mp_subject_layout, subject_cell, m_subject_layer, safe_box_enlarged (tni1 * ccbox, -1, -1), false).at_end ()) {
+ if (! ccbox.empty ()) {
collect_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox, interactions);
}
diff --git a/src/db/unit_tests/dbCellTests.cc b/src/db/unit_tests/dbCellTests.cc
index 448583d81..6578dea84 100644
--- a/src/db/unit_tests/dbCellTests.cc
+++ b/src/db/unit_tests/dbCellTests.cc
@@ -1014,3 +1014,49 @@ TEST(6)
}
+TEST(10_HasShapesTouching)
+{
+ db::Layout ly;
+ unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
+
+ db::Cell &a = ly.cell (ly.add_cell ("A"));
+
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
+
+ a.shapes (l1).insert (db::Box (-100, -100, 0, 0));
+
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 1, 100, 100)), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, -1, 100, 100)), true);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (-1, -1, -1, -1)), true);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (1, 1, 1, 1)), false);
+}
+
+TEST(11_HasShapesTouchingWithHier)
+{
+ db::Layout ly;
+ unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
+ unsigned int l2 = ly.insert_layer (db::LayerProperties (2, 0));
+
+ db::Cell &a = ly.cell (ly.add_cell ("A"));
+ db::Cell &b = ly.cell (ly.add_cell ("B"));
+
+ a.insert (db::CellInstArray (b.cell_index (), db::Trans (db::Vector (100, 100)), db::Vector (0, 200), db::Vector (200, 0), 2, 2));
+
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
+ EXPECT_EQ (a.has_shapes_touching (l2, db::Box ()), false);
+
+ b.shapes (l1).insert (db::Box (0, 0, 10, 10));
+
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
+ EXPECT_EQ (a.has_shapes_touching (l2, db::Box (0, 0, 100, 100)), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 99, 100)), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 99)), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (100, 100, 110, 110)), true);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (150, 150, 160, 160)), false);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 300, 310, 310)), true);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 100, 310, 110)), true);
+ EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 400, 310, 410)), false);
+}
From ebe38912a6fdeb0cfd367945a0ffadd8bf6075fd Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 27 Oct 2021 23:36:58 +0200
Subject: [PATCH 19/52] Tests and documentation for dbTrans#is_complex?
---
src/db/db/gsiDeclDbTrans.cc | 7 ++++---
testdata/ruby/dbTransTest.rb | 3 +++
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc
index a14eaa66b..397378c17 100644
--- a/src/db/db/gsiDeclDbTrans.cc
+++ b/src/db/db/gsiDeclDbTrans.cc
@@ -926,10 +926,11 @@ struct cplx_trans_defs
"@param m The new magnification"
) +
method ("is_complex?", &C::is_complex,
- "@brief Return true if the transformation is a complex one\n"
+ "@brief Returns true if the transformation is a complex one\n"
"\n"
- "If this value is false, the transformation can safely be converted to a simple transformation.\n"
- "The return value is equivalent to 'is_mag || !is_ortho'.\n"
+ "If this predicate is false, the transformation can safely be converted to a simple transformation.\n"
+ "Otherwise, this conversion will be lossy.\n"
+ "The predicate value is equivalent to 'is_mag || !is_ortho'.\n"
"\n"
"This method has been introduced in version 0.27.5."
) +
diff --git a/testdata/ruby/dbTransTest.rb b/testdata/ruby/dbTransTest.rb
index d69244910..e98cc4d87 100644
--- a/testdata/ruby/dbTransTest.rb
+++ b/testdata/ruby/dbTransTest.rb
@@ -155,6 +155,7 @@ class DBTrans_TestClass < TestBase
assert_equal( c.is_unity?, false )
assert_equal( c.is_ortho?, true )
assert_equal( c.is_mag?, false )
+ assert_equal( c.is_complex?, false )
assert_equal( c.is_mirror?, true )
assert_equal( c.rot, RBA::DCplxTrans::M135.rot )
assert_equal( c.s_trans.to_s, "m135 0,0" )
@@ -179,6 +180,7 @@ class DBTrans_TestClass < TestBase
assert_equal( c.is_unity?, false )
assert_equal( c.is_ortho?, true )
assert_equal( c.is_mag?, true )
+ assert_equal( c.is_complex?, true )
assert_equal( c.is_mirror?, false )
assert_equal( c.rot, RBA::DCplxTrans::R0.rot )
assert_equal( c.s_trans.to_s, "r0 0,0" )
@@ -190,6 +192,7 @@ class DBTrans_TestClass < TestBase
assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" )
assert_equal( c.is_unity?, false )
assert_equal( c.is_ortho?, false )
+ assert_equal( c.is_complex?, true )
assert_equal( c.is_mag?, true )
assert_equal( c.rot, RBA::DCplxTrans::M0.rot )
assert_equal( c.s_trans.to_s, "m0 2.5,-12.5" )
From ea1bf40a1ee1c1c934e47a0020417503ab3d7e7e Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sat, 30 Oct 2021 18:45:44 +0200
Subject: [PATCH 20/52] Using Jenkins for PyPI deployment. Travis stopped their
generous open source support
---
Jenkinsfile-pypi | 43 +++++++++++++++++++++++
ci-scripts/docker/docker_build_jenkins.sh | 31 ++++++++++++++++
setup.py | 8 ++++-
3 files changed, 81 insertions(+), 1 deletion(-)
create mode 100644 Jenkinsfile-pypi
create mode 100755 ci-scripts/docker/docker_build_jenkins.sh
diff --git a/Jenkinsfile-pypi b/Jenkinsfile-pypi
new file mode 100644
index 000000000..72d16f7da
--- /dev/null
+++ b/Jenkinsfile-pypi
@@ -0,0 +1,43 @@
+
+@Library("platform") _
+
+properties([disableConcurrentBuilds()])
+
+// from shared library - uses tags to set the platform name
+// (this is a pretty stupid way to set a parameter, but I don't
+// know of a better way to parameterize a multibranch pipeline)
+platform = pypi_platform()
+py_version = pypi_py_version()
+
+currentBuild.description = "PyPI deployment " + platform
+
+docker_image = "jenkins-manylinux2014_x86_64-pypi"
+target = platform + ".whl"
+
+node("master") {
+
+ stage("Checkout sources") {
+ checkout scm
+ }
+
+ stage("Building target ${target}") {
+
+ sh("rm -rf wheelhouse ; mkdir -p wheelhouse")
+
+ withDockerContainer(image: docker_image, args: "-v " + pwd() + ":/io") {
+ sh("PY_VERSION=" + py_version + " /io/ci-scripts/docker/docker_build_jenkins.sh")
+ }
+
+ }
+
+ stage("Publish and test") {
+
+ // publish for release tags
+ if (BRANCH_NAME.startsWith('v')) {
+ sh("twine upload --skip-existing wheelhouse/klayout-*manylinux2014*.whl wheelhouse/*.zip")
+ }
+
+ }
+
+}
+
diff --git a/ci-scripts/docker/docker_build_jenkins.sh b/ci-scripts/docker/docker_build_jenkins.sh
new file mode 100755
index 000000000..838a02dc0
--- /dev/null
+++ b/ci-scripts/docker/docker_build_jenkins.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+
+if [[ -z $PY_VERSION ]]; then
+ echo '$PY_VERSION is not set'
+ exit 1
+fi
+
+echo PY_VERSION=$PY_VERSION
+
+# Use single cores only so we do not overload the Jenkins host
+export KLAYOUT_SETUP_MULTICORE=1
+
+# Compile wheel and build source distribution
+cd /io
+"/opt/python/$PY_VERSION/bin/python" setup.py bdist_wheel -d /io/wheelhouse/ || exit 1
+"/opt/python/$PY_VERSION/bin/python" setup.py sdist --formats=zip -d /io/wheelhouse || exit 1
+
+# Bundle external shared libraries into the wheels via auditwheel
+for whl in /io/wheelhouse/*linux_*.whl; do
+ auditwheel repair "$whl" -w /io/wheelhouse/ || exit 1
+done
+
+# Install packages and test
+TEST_HOME=/io/testdata
+"/opt/python/$PY_VERSION/bin/pip" install klayout --no-index -f /io/wheelhouse || exit 1
+"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/import_db.py || exit 1
+"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/import_rdb.py || exit 1
+"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/import_tl.py || exit 1
+"/opt/python/$PY_VERSION/bin/python" $TEST_HOME/pymod/pya_tests.py || exit 1
+
diff --git a/setup.py b/setup.py
index d6e47cd81..3b3b5a3d7 100644
--- a/setup.py
+++ b/setup.py
@@ -65,7 +65,13 @@ from distutils.errors import CompileError
import distutils.command.build_ext
import setuptools.command.build_ext
import multiprocessing
-N_cores = multiprocessing.cpu_count()
+
+# for Jenkins we do not want to be greedy
+multicore = os.getenv("KLAYOUT_SETUP_MULTICORE")
+if multicore:
+ N_cores = int(multicore)
+else:
+ N_cores = multiprocessing.cpu_count()
# monkey-patch for parallel compilation
From 455c40ced60c3c5dc00530bfb5e98660bc3d2bfe Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sat, 30 Oct 2021 21:31:05 +0200
Subject: [PATCH 21/52] More consistent verbosity levels for cell mapping
---
src/buddies/src/bd/strmxor.cc | 12 +++++----
src/db/db/dbCellMapping.cc | 42 ++++++++++++++++----------------
src/unit_tests/unit_test_main.cc | 3 ++-
3 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc
index 6072e1e73..ba165cd4b 100644
--- a/src/buddies/src/bd/strmxor.cc
+++ b/src/buddies/src/bd/strmxor.cc
@@ -598,6 +598,8 @@ bool run_deep_xor (const XORData &xor_data)
} else {
+ tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
+
db::RecursiveShapeIterator ri_a, ri_b;
if (ll->second.first >= 0) {
@@ -612,15 +614,14 @@ bool run_deep_xor (const XORData &xor_data)
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
db::Region xor_res;
- xor_res = in_a ^ in_b;
+ {
+ tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
+ xor_res = in_a ^ in_b;
+ }
int tol_index = 0;
for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
- if (tl::verbosity () >= 20) {
- tl::log << "Running XOR on layer " << ll->first.to_string () << " with tolerance " << *t;
- }
-
db::LayerProperties lp = ll->first;
if (lp.layer >= 0) {
lp.layer += tol_index * xor_data.tolerance_bump;
@@ -633,6 +634,7 @@ bool run_deep_xor (const XORData &xor_data)
result.top_cell = xor_data.output_cell;
if (*t > db::epsilon) {
+ tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
xor_res.size (-db::coord_traits::rounded (0.5 * *t / dbu));
xor_res.size (db::coord_traits::rounded (0.5 * *t / dbu));
}
diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc
index 4e138fb20..e945ca153 100644
--- a/src/db/db/dbCellMapping.cc
+++ b/src/db/db/dbCellMapping.cc
@@ -409,9 +409,9 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
void
CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
{
- tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Cell mapping")));
+ tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Cell mapping")));
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - first step: mapping instance count and instance identity";
}
@@ -449,7 +449,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
++a;
} else {
- if (tl::verbosity () >= 30) {
+ if (tl::verbosity () >= 50) {
size_t na = 0, nb = 0;
for (std::multimap::const_iterator aa = a; aa != cm_a.end () && aa->first == w; ++aa) {
++na;
@@ -497,7 +497,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
- if (tl::verbosity () >= 40) {
+ if (tl::verbosity () >= 60) {
tl::info << "Checked cell " << layout_a.cell_name (a->second) << ": " << candidates [a->second].size () << " candidates remaining.";
}
@@ -519,7 +519,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
++a;
}
- if (tl::verbosity () >= 40) {
+ if (tl::verbosity () >= 60) {
tl::info << "Mapping candidates:";
dump_mapping (candidates, layout_a, layout_b);
}
@@ -536,7 +536,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
reduction = false;
++iteration;
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - iteration " << iteration << ": cross-instance cone reduction";
}
@@ -553,7 +553,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
refined_cand.clear ();
refined_cand.insert (refined_cand.end (), cand->second.begin (), cand->second.end ());
- if (tl::verbosity () >= 50) {
+ if (tl::verbosity () >= 70) {
tl::info << "--- Cell: " << layout_a.cell_name (cand->first);
tl::info << "Before reduction: " << tl::noendl;
for (size_t i = 0; i < refined_cand.size (); ++i) {
@@ -582,7 +582,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
- if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
+ if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
tl::info << "Reduction because of caller mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
tl::info << " -> " << tl::noendl;
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
@@ -619,7 +619,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
- if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
+ if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
tl::info << "Reduction because of callee mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
tl::info << " -> " << tl::noendl;
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
@@ -646,7 +646,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
if (ed < uc->second.second) {
uc->second = std::make_pair (ca, ed);
- if (tl::verbosity () >= 40) {
+ if (tl::verbosity () >= 60) {
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as new unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
}
}
@@ -654,7 +654,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
} else {
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
unique_candidates.insert (std::make_pair (cb, std::make_pair (ca, ed)));
- if (tl::verbosity () >= 40) {
+ if (tl::verbosity () >= 60) {
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
}
}
@@ -679,12 +679,12 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
- if (tl::verbosity () >= 40) {
+ if (tl::verbosity () >= 60) {
tl::info << "Further refined candidates:";
dump_mapping (candidates, layout_a, layout_b);
}
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - iteration " << iteration << ": removal of uniquely mapped cells on B side";
}
@@ -709,15 +709,15 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
-
- if (tl::verbosity () >= 40) {
+
+ if (tl::verbosity () >= 60) {
tl::info << "After reduction of mapped cells on b side:";
dump_mapping (candidates, layout_a, layout_b);
}
}
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
int total = 0;
int not_mapped = 0;
@@ -747,7 +747,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
// Resolve mapping according to string match
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - string mapping as last resort";
}
@@ -784,7 +784,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
int total = 0;
int not_mapped = 0;
@@ -795,7 +795,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
for (std::map >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
++total;
if (cand->second.size () == 0) {
- if (tl::verbosity () >= 30) {
+ if (tl::verbosity () >= 50) {
tl::info << "Unmapped cell: " << layout_a.cell_name (cand->first);
}
++not_mapped;
@@ -823,12 +823,12 @@ CellMapping::extract_unique (std::map second.size () == 1) {
- if (tl::verbosity () >= 20) {
+ if (tl::verbosity () >= 40) {
tl::info << " (U) " << layout_a.cell_name (cand->first) << " -> " << layout_b.cell_name (cand->second.front ()) << " (" << cand->first << " -> " << cand->second.front () << ")";
}
unique_mapping.insert (std::make_pair (cand->second.front (), cand->first));
- } else if (tl::verbosity () >= 30) {
+ } else if (tl::verbosity () >= 50) {
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
int n = 5;
diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc
index b506ff893..51b675222 100644
--- a/src/unit_tests/unit_test_main.cc
+++ b/src/unit_tests/unit_test_main.cc
@@ -246,7 +246,8 @@ run_tests (const std::vector &selected_tests, bool editable, boo
timer.stop();
ut::noctrl << "Time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)";
- ut::ctrl << "";
+ ut::noctrl << "Memory: " << timer.memory_size () / 1024 << "k";
+ ut::ctrl << "";
} catch (tl::CancelException &) {
From 902375cc4d0ef312c2d3b8ef6d03a2af4a066558 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sun, 31 Oct 2021 00:59:42 +0200
Subject: [PATCH 22/52] WIP: trying to reduce the number of DSS-internal cell
mappings by caching cell maps
---
src/buddies/src/bd/strmxor.cc | 1 +
src/db/db/dbDeepShapeStore.cc | 51 +++++++++++++++++++++++++++++++----
src/db/db/dbDeepShapeStore.h | 31 +++++++++++++++++++++
3 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc
index ba165cd4b..876373056 100644
--- a/src/buddies/src/bd/strmxor.cc
+++ b/src/buddies/src/bd/strmxor.cc
@@ -554,6 +554,7 @@ bool run_deep_xor (const XORData &xor_data)
{
db::DeepShapeStore dss;
dss.set_threads (xor_data.threads);
+ dss.set_keep_layouts (true); // avoids excessive cell mapping
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc
index 74e8f6e8b..e62c2cfd9 100644
--- a/src/db/db/dbDeepShapeStore.cc
+++ b/src/db/db/dbDeepShapeStore.cc
@@ -154,17 +154,25 @@ DeepLayer::add_from (const DeepLayer &dl)
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);
+ // create or reuse a layout mapping
- // Actually copy the shapes
+ const db::CellMapping *cell_mapping = 0;
+ db::CellMapping cm;
+ if (store () == dl.store ()) {
+ cell_mapping = &const_cast (mp_store.get ())->internal_cell_mapping (layout_index (), dl.layout_index ());
+ } else {
+ cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
+ cell_mapping = &cm;
+ }
+
+ // actually copy the shapes
std::map lm;
lm.insert (std::make_pair (dl.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);
+ db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cell_mapping->table (), lm);
}
}
@@ -418,11 +426,13 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
}
DeepShapeStore::DeepShapeStore ()
+ : m_keep_layouts (false)
{
++s_instance_count;
}
DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu)
+ : m_keep_layouts (false)
{
++s_instance_count;
@@ -711,6 +721,16 @@ db::Layout &DeepShapeStore::layout (unsigned int n)
return m_layouts [n]->layout;
}
+unsigned int DeepShapeStore::layout_index (const db::Layout *layout) const
+{
+ for (std::vector::const_iterator i = m_layouts.begin (); i != m_layouts.end (); ++i) {
+ if (&(*i)->layout == layout) {
+ return (unsigned int) (i - m_layouts.begin ());
+ }
+ }
+ tl_assert (false);
+}
+
size_t DeepShapeStore::instance_count ()
{
return s_instance_count;
@@ -743,7 +763,7 @@ void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer)
}
- if ((m_layouts[layout]->refs -= 1) <= 0) {
+ if ((m_layouts[layout]->refs -= 1) <= 0 && ! m_keep_layouts) {
delete m_layouts[layout];
m_layouts[layout] = 0;
clear_breakout_cells (layout);
@@ -919,6 +939,7 @@ void
DeepShapeStore::invalidate_hier ()
{
m_delivery_mapping_cache.clear ();
+ m_internal_mapping_cache.clear ();
}
void
@@ -934,6 +955,26 @@ DeepShapeStore::issue_variants (unsigned int layout_index, const std::map, db::CellMapping>::iterator cm = m_internal_mapping_cache.find (std::make_pair (from_layout_index, into_layout_index));
+ if (cm == m_internal_mapping_cache.end ()) {
+
+ cm = m_internal_mapping_cache.insert (std::make_pair (std::make_pair (from_layout_index, into_layout_index), db::CellMapping ())).first;
+
+ db::Layout &into_layout = layout (into_layout_index);
+ db::cell_index_type into_cell = initial_cell (into_layout_index).cell_index ();
+ const db::Layout &source_layout = layout (from_layout_index);
+ db::cell_index_type source_cell = initial_cell (from_layout_index).cell_index ();
+
+ cm->second.create_from_geometry_full (into_layout, into_cell, source_layout, source_cell);
+
+ }
+
+ return cm->second;
+}
+
const db::CellMapping &
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 std::set *included_cells)
{
diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h
index 9532ffd2b..990ecfb2e 100644
--- a/src/db/db/dbDeepShapeStore.h
+++ b/src/db/db/dbDeepShapeStore.h
@@ -339,6 +339,25 @@ public:
*/
bool is_singular () const;
+ /**
+ * @brief Sets a value indicating whether to keep layouts
+ *
+ * If this value is set to true, layouts are not released when their reference count
+ * goes down to zero.
+ */
+ void set_keep_layouts (bool f)
+ {
+ m_keep_layouts = f;
+ }
+
+ /**
+ * @brief Gets a value indicating whether to keep layouts
+ */
+ bool keep_layouts () const
+ {
+ return m_keep_layouts;
+ }
+
/**
* @brief Creates a new layer from a flat region (or the region is made flat)
*
@@ -479,6 +498,11 @@ public:
*/
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, const std::set *included_cells = 0);
+ /**
+ * @brief Gets the cell mapping from one internal layout to another
+ */
+ const db::CellMapping &internal_cell_mapping (unsigned int from_layout_index, unsigned int into_layout_index);
+
/**
* @brief Create cell variants from the given variant collector
*
@@ -544,6 +568,11 @@ public:
*/
db::Cell &initial_cell (unsigned int n);
+ /**
+ * @brief Gets the layout index for a given internal layout
+ */
+ unsigned int layout_index (const db::Layout *layout) const;
+
/**
* @brief Gets the singular layout (const version)
*
@@ -752,6 +781,7 @@ private:
layout_map_type m_layout_map;
DeepShapeStoreState m_state;
std::list m_state_stack;
+ bool m_keep_layouts;
tl::Mutex m_lock;
struct DeliveryMappingCacheKey
@@ -781,6 +811,7 @@ private:
};
std::map m_delivery_mapping_cache;
+ std::map, db::CellMapping> m_internal_mapping_cache;
};
template
From 64a6b3acdcbedbc61e882152c88989cebdb8af58 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 1 Nov 2021 09:53:51 +0100
Subject: [PATCH 23/52] WIP
---
src/db/db/dbRecursiveShapeIterator.cc | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc
index 075752113..6c89c76f3 100644
--- a/src/db/db/dbRecursiveShapeIterator.cc
+++ b/src/db/db/dbRecursiveShapeIterator.cc
@@ -646,7 +646,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const
// skip insts outside the complex region
if (! m_inst.at_end ()) {
- if (! is_outside_complex_region (m_inst->bbox ())) {
+ if (! is_outside_complex_region (m_inst->bbox (m_box_convert))) {
break;
} else {
++m_inst;
@@ -775,7 +775,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
// compute the region inside the new cell
if (new_region != m_region) {
new_region = m_trans.inverted () * m_region;
- new_region &= cell ()->bbox ();
+ new_region &= m_box_convert (db::CellInst (cell ()->cell_index ()));
}
m_local_region_stack.push_back (new_region);
From d244d5aac66cee70c1e3e166d29a5217490770fd Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 1 Nov 2021 13:48:47 +0100
Subject: [PATCH 24/52] Debugging plus enhancments
Basic change: DeepShapeStore+CellMapping uses (new) hier_generation_id of Layout to figure out
if the hierarchy has changed and the cache needs update.
---
src/db/db/dbCellMapping.cc | 5 ++++
src/db/db/dbCellMapping.h | 5 ++++
src/db/db/dbDeepShapeStore.cc | 38 ++++++++++++---------------
src/db/db/dbDeepShapeStore.h | 41 +++++++++++++++++++++++++++---
src/db/db/dbLayout.h | 4 +--
src/db/db/dbLayoutStateModel.cc | 5 ++--
src/db/db/dbLayoutStateModel.h | 13 ++++++++++
src/db/unit_tests/dbLayoutTests.cc | 12 +++++++++
8 files changed, 94 insertions(+), 29 deletions(-)
diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc
index e945ca153..6184e740a 100644
--- a/src/db/db/dbCellMapping.cc
+++ b/src/db/db/dbCellMapping.cc
@@ -269,6 +269,11 @@ void CellMapping::clear ()
m_b2a_mapping.clear ();
}
+void CellMapping::swap (CellMapping &other)
+{
+ m_b2a_mapping.swap (other.m_b2a_mapping);
+}
+
std::vector CellMapping::source_cells () const
{
std::vector s;
diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h
index a17ef92aa..31f72be94 100644
--- a/src/db/db/dbCellMapping.h
+++ b/src/db/db/dbCellMapping.h
@@ -64,6 +64,11 @@ public:
*/
void clear ();
+ /**
+ * @brief Swaps the cell mapping with another one
+ */
+ void swap (CellMapping &other);
+
/**
* @brief Create a single cell mapping
*
diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc
index e62c2cfd9..89b7e84e1 100644
--- a/src/db/db/dbDeepShapeStore.cc
+++ b/src/db/db/dbDeepShapeStore.cc
@@ -788,7 +788,6 @@ DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db:
}
db::Layout &layout = m_layouts[layout_index]->layout;
- layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
@@ -812,7 +811,6 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive
m_layouts[layout_index] = new LayoutHolder (trans);
db::Layout &layout = m_layouts[layout_index]->layout;
- layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
@@ -935,18 +933,9 @@ DeepLayer DeepShapeStore::create_text_layer (const db::RecursiveShapeIterator &s
return create_custom_layer (si, &refs, trans);
}
-void
-DeepShapeStore::invalidate_hier ()
-{
- m_delivery_mapping_cache.clear ();
- m_internal_mapping_cache.clear ();
-}
-
void
DeepShapeStore::issue_variants (unsigned int layout_index, const std::map > &var_map)
{
- invalidate_hier ();
-
db::HierarchyBuilder &builder = m_layouts [layout_index]->builder;
for (std::map >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) {
for (std::map::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
@@ -956,19 +945,21 @@ DeepShapeStore::issue_variants (unsigned int layout_index, const std::map, db::CellMapping>::iterator cm = m_internal_mapping_cache.find (std::make_pair (from_layout_index, into_layout_index));
- if (cm == m_internal_mapping_cache.end ()) {
+ db::Layout &into_layout = layout (into_layout_index);
+ db::cell_index_type into_cell = initial_cell (into_layout_index).cell_index ();
+ const db::Layout &source_layout = layout (from_layout_index);
+ db::cell_index_type source_cell = initial_cell (from_layout_index).cell_index ();
- cm = m_internal_mapping_cache.insert (std::make_pair (std::make_pair (from_layout_index, into_layout_index), db::CellMapping ())).first;
+ std::map, CellMappingWithGenerationIds>::iterator cm = m_internal_mapping_cache.find (std::make_pair (from_layout_index, into_layout_index));
+ if (cm == m_internal_mapping_cache.end () || ! cm->second.is_valid (into_layout, source_layout)) {
- db::Layout &into_layout = layout (into_layout_index);
- db::cell_index_type into_cell = initial_cell (into_layout_index).cell_index ();
- const db::Layout &source_layout = layout (from_layout_index);
- db::cell_index_type source_cell = initial_cell (from_layout_index).cell_index ();
+ cm = m_internal_mapping_cache.insert (std::make_pair (std::make_pair (from_layout_index, into_layout_index), CellMappingWithGenerationIds ())).first;
+ cm->second.clear ();
cm->second.create_from_geometry_full (into_layout, into_cell, source_layout, source_cell);
+ cm->second.set_generation_ids (into_layout, source_layout);
}
@@ -994,10 +985,11 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
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 ()) {
+ std::map::iterator cm = m_delivery_mapping_cache.find (key);
+ if (cm == m_delivery_mapping_cache.end () || ! cm->second.is_valid (*into_layout, *source_layout)) {
- cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
+ cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first;
+ cm->second.clear ();
// collects the cell mappings we skip because they are variants (variant building or box variants)
std::map cm_skipped_variants;
@@ -1086,6 +1078,8 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
into_layout->delete_cells (cells_to_delete);
}
+ cm->second.set_generation_ids (*into_layout, *source_layout);
+
}
return cm->second;
diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h
index 990ecfb2e..877cd2bc9 100644
--- a/src/db/db/dbDeepShapeStore.h
+++ b/src/db/db/dbDeepShapeStore.h
@@ -32,6 +32,7 @@
#include "dbLayout.h"
#include "dbRecursiveShapeIterator.h"
#include "dbHierarchyBuilder.h"
+#include "dbCellMapping.h"
#include "gsiObject.h"
#include
@@ -291,6 +292,41 @@ struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy
}
};
+/**
+ * @brief An object holding a cell mapping with the hierarchy generation Ids of the involved layouts
+ */
+class DB_PUBLIC CellMappingWithGenerationIds
+ : public db::CellMapping
+{
+public:
+ CellMappingWithGenerationIds ()
+ : db::CellMapping (), m_into_generation_id (0), m_from_generation_id (0)
+ {
+ // .. nothing yet ..
+ }
+
+ void swap (CellMappingWithGenerationIds &other)
+ {
+ db::CellMapping::swap (other);
+ std::swap (m_into_generation_id, other.m_into_generation_id);
+ std::swap (m_from_generation_id, other.m_from_generation_id);
+ }
+
+ bool is_valid (const db::Layout &into_layout, const db::Layout &from_layout) const
+ {
+ return into_layout.hier_generation_id () == m_into_generation_id && from_layout.hier_generation_id () == m_from_generation_id;
+ }
+
+ void set_generation_ids (const db::Layout &into_layout, const db::Layout &from_layout)
+ {
+ m_into_generation_id = into_layout.hier_generation_id ();
+ m_from_generation_id = from_layout.hier_generation_id ();
+ }
+
+private:
+ size_t m_into_generation_id, m_from_generation_id;
+};
+
/**
* @brief The "deep shape store" is a working model for the hierarchical ("deep") processor
*
@@ -759,7 +795,6 @@ private:
struct LayoutHolder;
- void invalidate_hier ();
void add_ref (unsigned int layout, unsigned int layer);
void remove_ref (unsigned int layout, unsigned int layer);
@@ -810,8 +845,8 @@ private:
db::cell_index_type into_cell;
};
- std::map m_delivery_mapping_cache;
- std::map, db::CellMapping> m_internal_mapping_cache;
+ std::map m_delivery_mapping_cache;
+ std::map, CellMappingWithGenerationIds> m_internal_mapping_cache;
};
template
diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h
index e5ee39006..478620390 100644
--- a/src/db/db/dbLayout.h
+++ b/src/db/db/dbLayout.h
@@ -528,12 +528,12 @@ public:
*
* The editable mode will be taken from db::default_editable_mode.
*/
- Layout (db::Manager *manager = 0);
+ explicit Layout (db::Manager *manager = 0);
/**
* @brief Standard constructor which allows one to specify editable mode
*/
- Layout (bool editable, db::Manager *manager = 0);
+ explicit Layout (bool editable, db::Manager *manager = 0);
/**
* @brief The copy ctor
diff --git a/src/db/db/dbLayoutStateModel.cc b/src/db/db/dbLayoutStateModel.cc
index 4bcb0755f..63cc42177 100644
--- a/src/db/db/dbLayoutStateModel.cc
+++ b/src/db/db/dbLayoutStateModel.cc
@@ -29,13 +29,13 @@ namespace db
{
LayoutStateModel::LayoutStateModel (bool busy)
- : m_hier_dirty (false), m_all_bboxes_dirty (false), m_busy (busy)
+ : m_hier_dirty (false), m_hier_generation_id (0), m_all_bboxes_dirty (false), m_busy (busy)
{
// .. nothing yet ..
}
LayoutStateModel::LayoutStateModel (const LayoutStateModel &d)
- : m_hier_dirty (d.m_hier_dirty), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
+ : m_hier_dirty (d.m_hier_dirty), m_hier_generation_id (d.m_hier_generation_id), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
{
// .. nothing yet ..
}
@@ -44,6 +44,7 @@ LayoutStateModel &
LayoutStateModel::operator= (const LayoutStateModel &d)
{
m_hier_dirty = d.m_hier_dirty;
+ m_hier_generation_id = d.m_hier_generation_id;
m_bboxes_dirty = d.m_bboxes_dirty;
m_all_bboxes_dirty = d.m_all_bboxes_dirty;
m_busy = d.m_busy;
diff --git a/src/db/db/dbLayoutStateModel.h b/src/db/db/dbLayoutStateModel.h
index f99e04c32..20760a70d 100644
--- a/src/db/db/dbLayoutStateModel.h
+++ b/src/db/db/dbLayoutStateModel.h
@@ -85,6 +85,7 @@ public:
*/
void invalidate_hier ()
{
+ ++m_hier_generation_id;
if (! m_hier_dirty || m_busy) {
do_invalidate_hier (); // must be called before the hierarchy is invalidated (stopping of redraw thread requires this)
m_hier_dirty = true;
@@ -127,6 +128,17 @@ public:
return m_hier_dirty;
}
+ /**
+ * @brief Gets the hierarchy generation ID
+ *
+ * The hierarchy generation ID is a number which is incremented on every hierarchy
+ * change.
+ */
+ size_t hier_generation_id () const
+ {
+ return m_hier_generation_id;
+ }
+
/**
* @brief The "dirty bounding box" attribute
*
@@ -195,6 +207,7 @@ public:
private:
bool m_hier_dirty;
+ size_t m_hier_generation_id;
std::vector m_bboxes_dirty;
bool m_all_bboxes_dirty;
bool m_busy;
diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc
index c4630e5ae..303187ecb 100644
--- a/src/db/unit_tests/dbLayoutTests.cc
+++ b/src/db/unit_tests/dbLayoutTests.cc
@@ -261,12 +261,15 @@ TEST(2)
db::cell_index_type ci;
db::Cell *top;
+ EXPECT_EQ (g.hier_generation_id (), size_t (0));
+
ci = g.add_cell ("TOP");
EXPECT_EQ (el.flags, (unsigned int) 0);
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
+ EXPECT_EQ (g.hier_generation_id (), size_t (1));
el.reset ();
top = &g.cell (ci);
@@ -276,6 +279,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
+ EXPECT_EQ (g.hier_generation_id (), size_t (2));
el.reset ();
top->insert (db::CellInstArray (ci, db::Trans ()));
@@ -284,10 +288,12 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
+ EXPECT_EQ (g.hier_generation_id (), size_t (3));
g.clear ();
g.update ();
el.reset ();
+ EXPECT_EQ (g.hier_generation_id (), size_t (4));
ci = g.add_cell ("TOP");
@@ -295,6 +301,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
+ EXPECT_EQ (g.hier_generation_id (), size_t (5));
el.reset ();
g.update ();
@@ -305,6 +312,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
+ EXPECT_EQ (g.hier_generation_id (), size_t (6));
el.reset ();
g.update ();
@@ -314,6 +322,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
+ EXPECT_EQ (g.hier_generation_id (), size_t (7));
// busy mode will make events issued always
g.clear ();
@@ -326,6 +335,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
+ EXPECT_EQ (g.hier_generation_id (), size_t (9));
el.reset ();
top = &g.cell (ci);
@@ -335,6 +345,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
+ EXPECT_EQ (g.hier_generation_id (), size_t (10));
el.reset ();
top->insert (db::CellInstArray (ci, db::Trans ()));
@@ -343,6 +354,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
+ EXPECT_EQ (g.hier_generation_id (), size_t (11));
}
From a0367c15303c7632a05dc2b01a552204d4d229c1 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 1 Nov 2021 17:27:52 +0100
Subject: [PATCH 25/52] More specific clip variants after bounding boxes have
been reduced in RecursiveShapeIterator
---
src/db/db/dbHierarchyBuilder.cc | 8 +++++---
src/db/db/dbRecursiveShapeIterator.cc | 4 ++--
src/db/db/dbRecursiveShapeIterator.h | 10 ++++++++++
testdata/algo/hierarchy_builder_au2a.gds | Bin 11526 -> 10512 bytes
testdata/algo/hierarchy_builder_au2b.gds | Bin 11614 -> 10522 bytes
testdata/algo/hierarchy_builder_au2c.gds | Bin 11902 -> 10810 bytes
testdata/algo/hierarchy_builder_au2d.gds | Bin 11536 -> 10522 bytes
testdata/algo/hierarchy_builder_au2f.gds | Bin 11978 -> 10886 bytes
testdata/algo/hierarchy_builder_au4a.gds | Bin 82618 -> 65416 bytes
9 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc
index 838e93243..fe48fd086 100644
--- a/src/db/db/dbHierarchyBuilder.cc
+++ b/src/db/db/dbHierarchyBuilder.cc
@@ -381,14 +381,16 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
} else {
- db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox ();
+ db::cell_index_type inst_cell = inst.object ().cell_index ();
+
+ db::Box cell_bbox = iter->cell_bbox (inst_cell);
std::pair > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region);
if (! clip_variant.first) {
return false;
}
- CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), clip_variant.second);
- db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
+ CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second);
+ db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell));
// for a new cell, create this instance
if (m_cell_stack.back ().first) {
diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc
index 6c89c76f3..df95d9201 100644
--- a/src/db/db/dbRecursiveShapeIterator.cc
+++ b/src/db/db/dbRecursiveShapeIterator.cc
@@ -775,7 +775,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
// compute the region inside the new cell
if (new_region != m_region) {
new_region = m_trans.inverted () * m_region;
- new_region &= m_box_convert (db::CellInst (cell ()->cell_index ()));
+ new_region &= cell_bbox (cell_index ());
}
m_local_region_stack.push_back (new_region);
@@ -969,7 +969,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
// 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 ());
+ db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
if (! is_outside_complex_region (ia_box)) {
break;
} else {
diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h
index 5537619c6..ee70e8538 100644
--- a/src/db/db/dbRecursiveShapeIterator.h
+++ b/src/db/db/dbRecursiveShapeIterator.h
@@ -665,6 +665,16 @@ public:
return reinterpret_cast (c - (c & size_t (3)));
}
+ /**
+ * @brief Gets the current cell's bounding box
+ *
+ * The returned box is limited to the selected layer if applicable.
+ */
+ box_type cell_bbox (db::cell_index_type cell_index) const
+ {
+ return m_box_convert (db::CellInst (cell_index));
+ }
+
/**
* @brief Increments the iterator (operator version)
*/
diff --git a/testdata/algo/hierarchy_builder_au2a.gds b/testdata/algo/hierarchy_builder_au2a.gds
index 9d556aed68022a0a7ef97c024d1f7caf7f78fbac..0f6afd622a5fd0fa6ce463856be38a4444d9a2c8 100644
GIT binary patch
literal 10512
zcmeHMO=w(I6h8BlH#5n!HZc?`MXJPw5ZcV-pJGY0Vna$&(lJOV=+dQ&Qbbe)T~t>t
zTvZoEur9h21fi~6h-BqbP$V0dLZGXJHqY<=oVoYi`|f*hruh@Pm^s5e_uPBF^LNj^
zLlKqBZ_(LOx%LkYQHh4>G+nTNQ@tErU3!&hv@&pM@$=c`U%%g;zx>mMyI*f~=6_~QRWV#
zyu)i(m#)3NzPL6ve`q_+QMIlxMHc+cOfH~aYV`>LxSJu_)KAMl>j-94*z9PO21^G$p}
z<9ncg8_MH*n<|d9og8`Sy&>e`%NZeWD38-dpK&;K^g8>Pf$v4&+*CBa2f8P0rYMhx
zZ6@S|%@i%o2swN-(_x>*hhr)8b3`<=FVw*SX!*xoqe0
z<6F@ACDgrJ(Em@=)-RayxS)OJVe7#69MmVQiQ+K$JY<%IJfib;A#W&;({i?ahI+R9v7tO3
z-Ra3Yrqi-^Ld@X=u6&?qbsZEZFjF1}mAR