diff --git a/scripts/create_drc_samples.rb b/scripts/create_drc_samples.rb
index 02be4140d..f7963be5d 100644
--- a/scripts/create_drc_samples.rb
+++ b/scripts/create_drc_samples.rb
@@ -269,6 +269,7 @@ gen = Gen::new
run_demo gen, "input1.separation(input2, 1.2, euclidian)", "drc_separation1.png"
run_demo gen, "input1.drc(separation(input2, euclidian) < 1.2)", "drc_separation1u.png"
+run_demo gen, "input1.drc((separation(input2) >= 1.2).first_edges)", "drc_separation1un.png"
class Gen
def produce(s1, s2)
diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc
index 2be048149..7a7db29cb 100644
--- a/src/buddies/src/bd/bdWriterOptions.cc
+++ b/src/buddies/src/bd/bdWriterOptions.cc
@@ -38,6 +38,7 @@ GenericWriterOptions::GenericWriterOptions ()
m_gds2_max_vertex_count (8000),
m_gds2_no_zero_length_paths (false),
m_gds2_multi_xy_records (false),
+ m_gds2_resolve_skew_arrays (false),
m_gds2_max_cellname_length (32000),
m_gds2_libname ("LIB"),
m_gds2_user_units (1.0),
@@ -150,6 +151,12 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
"If this option is given, multiple XY records will be written to accommodate an unlimited number "
"of points per polygon or path. However, such files may not be compatible with some consumers."
)
+ << tl::arg (group +
+ "-ow|--resolve-skew-arrays", &m_gds2_resolve_skew_arrays, "Resolve skew (non-orthogonal) arrays",
+ "If this option is given, skew arrays are resolved into single instances. Skew arrays "
+ "are ones where the row or column vectors are not horizontal or vertical. Such arrays can cause problems "
+ "in legacy software. This option will eliminate them at the expense of bigger files and loss of the array instance property."
+ )
<< tl::arg (group +
"#--no-zero-length-paths", &m_gds2_no_zero_length_paths, "Converts zero-length paths to polygons",
"If this option is given, zero-length paths (such with one point) are not written as paths "
@@ -349,6 +356,7 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::
save_options.set_option_by_name ("gds2_max_vertex_count", m_gds2_max_vertex_count);
save_options.set_option_by_name ("gds2_no_zero_length_paths", m_gds2_no_zero_length_paths);
save_options.set_option_by_name ("gds2_multi_xy_records", m_gds2_multi_xy_records);
+ save_options.set_option_by_name ("gds2_resolve_skew_arrays", m_gds2_resolve_skew_arrays);
save_options.set_option_by_name ("gds2_max_cellname_length", m_gds2_max_cellname_length);
save_options.set_option_by_name ("gds2_libname", m_gds2_libname);
save_options.set_option_by_name ("gds2_user_units", m_gds2_user_units);
diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h
index 5413133ec..4980daa3c 100644
--- a/src/buddies/src/bd/bdWriterOptions.h
+++ b/src/buddies/src/bd/bdWriterOptions.h
@@ -116,6 +116,7 @@ private:
unsigned int m_gds2_max_vertex_count;
bool m_gds2_no_zero_length_paths;
bool m_gds2_multi_xy_records;
+ bool m_gds2_resolve_skew_arrays;
unsigned int m_gds2_max_cellname_length;
std::string m_gds2_libname;
double m_gds2_user_units;
diff --git a/src/db/db/db.pro b/src/db/db/db.pro
index b22167cff..58fe54140 100644
--- a/src/db/db/db.pro
+++ b/src/db/db/db.pro
@@ -24,6 +24,7 @@ SOURCES = \
dbCompoundOperation.cc \
dbEdge.cc \
dbEdgePair.cc \
+ dbEdgePairFilters.cc \
dbEdgePairRelations.cc \
dbEdgePairs.cc \
dbEdgeProcessor.cc \
@@ -233,6 +234,7 @@ HEADERS = \
dbCompoundOperation.h \
dbEdge.h \
dbEdgePair.h \
+ dbEdgePairFilters.h \
dbEdgePairRelations.h \
dbEdgePairs.h \
dbEdgeProcessor.h \
diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc
index 47e54c130..127409275 100644
--- a/src/db/db/dbAsIfFlatEdgePairs.cc
+++ b/src/db/db/dbAsIfFlatEdgePairs.cc
@@ -46,7 +46,17 @@ AsIfFlatEdgePairs::AsIfFlatEdgePairs ()
AsIfFlatEdgePairs::AsIfFlatEdgePairs (const AsIfFlatEdgePairs &other)
: EdgePairsDelegate (other), m_bbox_valid (other.m_bbox_valid), m_bbox (other.m_bbox)
{
- // .. nothing yet ..
+ operator= (other);
+}
+
+AsIfFlatEdgePairs &
+AsIfFlatEdgePairs::operator= (const AsIfFlatEdgePairs &other)
+{
+ if (this != &other) {
+ m_bbox_valid = other.m_bbox_valid;
+ m_bbox = other.m_bbox;
+ }
+ return *this;
}
AsIfFlatEdgePairs::~AsIfFlatEdgePairs ()
diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h
index 5fe1e3f1d..938beaeea 100644
--- a/src/db/db/dbAsIfFlatEdgePairs.h
+++ b/src/db/db/dbAsIfFlatEdgePairs.h
@@ -81,6 +81,8 @@ protected:
void invalidate_bbox ();
private:
+ friend class DeepEdgePairs;
+
AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other);
mutable bool m_bbox_valid;
diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc
index f2fa65d1b..c2e9f2f08 100644
--- a/src/db/db/dbAsIfFlatRegion.cc
+++ b/src/db/db/dbAsIfFlatRegion.cc
@@ -167,22 +167,6 @@ AsIfFlatRegion::in (const Region &other, bool invert) const
return new_region.release ();
}
-size_t
-AsIfFlatRegion::count () const
-{
- size_t n = 0;
- for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
- ++n;
- }
- return n;
-}
-
-size_t
-AsIfFlatRegion::hier_count () const
-{
- return count ();
-}
-
bool
AsIfFlatRegion::is_box () const
{
@@ -1077,8 +1061,14 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
others.push_back (begin_merged ());
} else {
foreign.push_back (false);
- others.push_back (other->begin ());
- other_is_merged = other->is_merged ();
+ if (options.whole_edges) {
+ // NOTE: whole edges needs both inputs merged
+ others.push_back (other->begin_merged ());
+ other_is_merged = true;
+ } else {
+ others.push_back (other->begin ());
+ other_is_merged = other->is_merged ();
+ }
has_other = true;
}
diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h
index d2b8ff9e3..e7b54659b 100644
--- a/src/db/db/dbAsIfFlatRegion.h
+++ b/src/db/db/dbAsIfFlatRegion.h
@@ -47,8 +47,6 @@ public:
virtual ~AsIfFlatRegion ();
virtual bool is_box () const;
- virtual size_t count () const;
- virtual size_t hier_count () const;
virtual area_type area (const db::Box &box) const;
virtual perimeter_type perimeter (const db::Box &box) const;
diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc
index 8792a6505..9f59c5da6 100644
--- a/src/db/db/dbDeepEdgePairs.cc
+++ b/src/db/db/dbDeepEdgePairs.cc
@@ -346,16 +346,87 @@ EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const
}
}
-EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
+EdgePairsDelegate *
+DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
{
- // TODO: implement
- return AsIfFlatEdgePairs::filter_in_place (filter);
+ // TODO: implement to be really in-place
+ *this = *apply_filter (filter);
+ return this;
}
-EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
+EdgePairsDelegate *
+DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
{
- // TODO: implement
- return AsIfFlatEdgePairs::filtered (filter);
+ return apply_filter (filter);
+}
+
+DeepEdgePairs *
+DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const
+{
+ const db::DeepLayer &edge_pairs = deep_layer ();
+
+ std::unique_ptr vars;
+ if (filter.vars ()) {
+
+ vars.reset (new db::VariantsCollectorBase (filter.vars ()));
+
+ vars->collect (edge_pairs.layout (), edge_pairs.initial_cell ());
+
+ if (filter.wants_variants ()) {
+ const_cast (edge_pairs).separate_variants (*vars);
+ }
+
+ }
+
+ db::Layout &layout = const_cast (edge_pairs.layout ());
+ std::map > to_commit;
+
+ std::unique_ptr res (new db::DeepEdgePairs (edge_pairs.derived ()));
+ for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
+
+ const db::Shapes &s = c->shapes (edge_pairs.layer ());
+
+ if (vars.get ()) {
+
+ const std::map &vv = vars->variants (c->cell_index ());
+ for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) {
+
+ db::Shapes *st;
+ if (vv.size () == 1) {
+ st = & c->shapes (res->deep_layer ().layer ());
+ } else {
+ st = & to_commit [c->cell_index ()] [v->first];
+ }
+
+ const db::ICplxTrans &tr = v->first;
+
+ for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) {
+ if (filter.selected (si->edge_pair ().transformed (tr))) {
+ st->insert (*si);
+ }
+ }
+
+ }
+
+ } else {
+
+ db::Shapes &st = c->shapes (res->deep_layer ().layer ());
+
+ for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) {
+ if (filter.selected (si->edge_pair ())) {
+ st.insert (*si);
+ }
+ }
+
+ }
+
+ }
+
+ if (! to_commit.empty () && vars.get ()) {
+ res->deep_layer ().commit_shapes (*vars, to_commit);
+ }
+
+ return res.release ();
}
RegionDelegate *
@@ -461,4 +532,14 @@ void DeepEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type
deep_layer ().insert_into_as_polygons (layout, into_cell, into_layer, enl);
}
+DeepEdgePairs &DeepEdgePairs::operator= (const DeepEdgePairs &other)
+{
+ if (this != &other) {
+ AsIfFlatEdgePairs::operator= (other);
+ DeepShapeCollectionDelegateBase::operator= (other);
+ }
+
+ return *this;
+}
+
}
diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h
index 75408ea94..b66d45420 100644
--- a/src/db/db/dbDeepEdgePairs.h
+++ b/src/db/db/dbDeepEdgePairs.h
@@ -104,6 +104,7 @@ private:
void init ();
EdgesDelegate *generic_edges (bool first, bool second) const;
+ DeepEdgePairs *apply_filter (const EdgePairFilterBase &filter) const;
};
}
diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc
index 9660c10b9..3d9504a9f 100644
--- a/src/db/db/dbDeepEdges.cc
+++ b/src/db/db/dbDeepEdges.cc
@@ -273,24 +273,33 @@ void DeepEdges::reserve (size_t)
// Not implemented for deep regions
}
-void DeepEdges::flatten ()
+static
+void flatten_layer (db::DeepLayer &deep_layer)
{
- db::Layout &layout = deep_layer ().layout ();
+ db::Layout &layout = deep_layer.layout ();
if (layout.begin_top_down () != layout.end_top_down ()) {
db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
db::Shapes flat_shapes (layout.is_editable ());
- for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) {
+ for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) {
flat_shapes.insert (iter->edge ().transformed (iter.trans ()));
}
- layout.clear_layer (deep_layer ().layer ());
- top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes);
+ layout.clear_layer (deep_layer.layer ());
+ top_cell.shapes (deep_layer.layer ()).swap (flat_shapes);
}
}
+void DeepEdges::flatten ()
+{
+ flatten_layer (deep_layer ());
+ if (m_merged_edges_valid) {
+ flatten_layer (const_cast (merged_deep_layer ()));
+ }
+}
+
EdgesIteratorDelegate *
DeepEdges::begin () const
{
@@ -518,7 +527,7 @@ DeepEdges::ensure_merged_edges_valid () const
m_merged_edges = deep_layer ().derived ();
- tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons");
+ tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged edges");
db::Layout &layout = const_cast (deep_layer ().layout ());
diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc
index c869ac5af..b412fedf5 100644
--- a/src/db/db/dbDeepRegion.cc
+++ b/src/db/db/dbDeepRegion.cc
@@ -284,26 +284,37 @@ void DeepRegion::reserve (size_t)
// Not implemented for deep regions
}
-void DeepRegion::flatten ()
+static void
+flatten_layer (db::DeepLayer &deep_layer)
{
- db::Layout &layout = deep_layer ().layout ();
+ db::Layout &layout = deep_layer.layout ();
if (layout.begin_top_down () != layout.end_top_down ()) {
db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
db::Shapes flat_shapes (layout.is_editable ());
- for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) {
- db::Polygon poly;
- iter->polygon (poly);
- flat_shapes.insert (poly.transformed (iter.trans ()));
+ for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) {
+ if (iter->is_polygon ()) {
+ db::Polygon poly;
+ iter->polygon (poly);
+ flat_shapes.insert (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ()));
+ }
}
- layout.clear_layer (deep_layer ().layer ());
- top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes);
+ layout.clear_layer (deep_layer.layer ());
+ top_cell.shapes (deep_layer.layer ()).swap (flat_shapes);
}
}
+void DeepRegion::flatten ()
+{
+ flatten_layer (deep_layer ());
+ if (m_merged_polygons_valid) {
+ flatten_layer (const_cast (merged_deep_layer ()));
+ }
+}
+
RegionIteratorDelegate *
DeepRegion::begin () const
{
@@ -651,6 +662,13 @@ DeepRegion::not_with (const Region &other) const
}
}
+RegionDelegate *
+DeepRegion::or_with (const Region &other) const
+{
+ // NOTE: this is somewhat different from the as if flat case because it does not merge
+ return add (other);
+}
+
std::pair
DeepRegion::andnot_with (const Region &other) const
{
@@ -1090,6 +1108,7 @@ public:
virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
db::EdgeProcessor ep;
+ ep.set_base_verbosity (50);
for (shape_interactions::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
ep.insert (s->second);
@@ -1637,8 +1656,14 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
if (! other_deep) {
return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options);
}
- other_layer = other_deep->deep_layer ().layer ();
- other_is_merged = other->is_merged ();
+ if (options.whole_edges) {
+ // NOTE: whole edges needs both inputs merged
+ other_layer = other_deep->merged_deep_layer ().layer ();
+ other_is_merged = true;
+ } else {
+ other_layer = other_deep->deep_layer ().layer ();
+ other_is_merged = other->is_merged ();
+ }
}
const db::DeepLayer &polygons = merged_deep_layer ();
diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h
index 38252620d..175255389 100644
--- a/src/db/db/dbDeepRegion.h
+++ b/src/db/db/dbDeepRegion.h
@@ -99,6 +99,7 @@ public:
virtual RegionDelegate *and_with (const Region &other) const;
virtual RegionDelegate *not_with (const Region &other) const;
virtual RegionDelegate *xor_with (const Region &other) const;
+ virtual RegionDelegate *or_with (const Region &other) const;
virtual std::pair andnot_with (const Region &) const;
virtual RegionDelegate *add_in_place (const Region &other);
diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc
index 733f8ec3e..74e8f6e8b 100644
--- a/src/db/db/dbDeepShapeStore.cc
+++ b/src/db/db/dbDeepShapeStore.cc
@@ -403,9 +403,15 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
{
unsigned int layer_index = layout.insert_layer ();
- if (si.layout () && si.layer () < si.layout ()->layers ()) {
+ if (si.layout ()) {
// try to preserve the layer properties
- layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ()));
+ if (! si.multiple_layers ()) {
+ if (si.layer () < si.layout ()->layers ()) {
+ layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ()));
+ }
+ } else if (! si.layers ().empty ()) {
+ layout.set_properties (layer_index, si.layout ()->get_properties (si.layers ().front ()));
+ }
}
return layer_index;
diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc
index f9d357535..389e81677 100644
--- a/src/db/db/dbDeviceClass.cc
+++ b/src/db/db/dbDeviceClass.cc
@@ -79,18 +79,6 @@ bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) cons
return false;
}
-bool EqualDeviceParameters::equal (const db::Device &a, const db::Device &b) const
-{
- for (std::vector > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) {
- int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second);
- if (cmp != 0) {
- return false;
- }
- }
-
- return true;
-}
-
EqualDeviceParameters &EqualDeviceParameters::operator+= (const EqualDeviceParameters &other)
{
for (std::vector > >::const_iterator c = other.m_compare_set.begin (); c != other.m_compare_set.end (); ++c) {
@@ -121,19 +109,6 @@ bool AllDeviceParametersAreEqual::less (const db::Device &a, const db::Device &b
return false;
}
-bool AllDeviceParametersAreEqual::equal (const db::Device &a, const db::Device &b) const
-{
- const std::vector ¶meters = a.device_class ()->parameter_definitions ();
- for (std::vector::const_iterator c = parameters.begin (); c != parameters.end (); ++c) {
- int cmp = compare_parameters (a.parameter_value (c->id ()), b.parameter_value (c->id ()), 0.0, m_relative);
- if (cmp != 0) {
- return false;
- }
- }
-
- return true;
-}
-
// --------------------------------------------------------------------------------
// DeviceClass class implementation
@@ -293,7 +268,7 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b)
}
if (pcd != 0) {
- return pcd->equal (a, b);
+ return ! pcd->less (a, b) && ! pcd->less (b, a);
} else {
const std::vector &pd = a.device_class ()->parameter_definitions ();
diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h
index 66ba8d5f6..24c8b5aec 100644
--- a/src/db/db/dbDeviceClass.h
+++ b/src/db/db/dbDeviceClass.h
@@ -297,7 +297,6 @@ public:
virtual DeviceParameterCompareDelegate *clone () const = 0;
virtual bool less (const db::Device &a, const db::Device &b) const = 0;
- virtual bool equal (const db::Device &a, const db::Device &b) const = 0;
};
/**
@@ -315,7 +314,6 @@ public:
EqualDeviceParameters (size_t parameter_id, double relative, double absolute);
virtual bool less (const db::Device &a, const db::Device &b) const;
- virtual bool equal (const db::Device &a, const db::Device &b) const;
virtual DeviceParameterCompareDelegate *clone () const
{
@@ -345,7 +343,6 @@ public:
AllDeviceParametersAreEqual (double relative);
virtual bool less (const db::Device &a, const db::Device &b) const;
- virtual bool equal (const db::Device &a, const db::Device &b) const;
virtual DeviceParameterCompareDelegate *clone () const
{
diff --git a/src/db/db/dbEdge.h b/src/db/db/dbEdge.h
index 27ed72d3f..39ddf359f 100644
--- a/src/db/db/dbEdge.h
+++ b/src/db/db/dbEdge.h
@@ -856,6 +856,9 @@ public:
* line through the edge. If the edge is degenerated, the distance
* is not defined.
*
+ * The distance is through as a distance of the point from the line
+ * through the edge.
+ *
* @param p The point to test.
*
* @return The distance
@@ -877,6 +880,27 @@ public:
}
}
+ /**
+ * @brief Gets the distance of the point from the edge.
+ *
+ * The distance is computed as the minimum distance of the point to any of the edge's
+ * points.
+ *
+ * @param p The point whose distance is to be computed
+ *
+ * @return The distance
+ */
+ distance_type euclidian_distance (const db::point &p)
+ {
+ if (db::sprod_sign (p - p1 (), d ()) < 0) {
+ return p1 ().distance (p);
+ } else if (db::sprod_sign (p - p2 (), d ()) > 0) {
+ return p2 ().distance (p);
+ } else {
+ return std::abs (distance (p));
+ }
+ }
+
/**
* @brief Side of the point
*
diff --git a/src/db/db/dbEdgePair.h b/src/db/db/dbEdgePair.h
index 805fb8cbe..0b6209725 100644
--- a/src/db/db/dbEdgePair.h
+++ b/src/db/db/dbEdgePair.h
@@ -204,6 +204,24 @@ public:
return !equal (b);
}
+ /**
+ * @brief Computes the distance of the edges in the edge pair
+ *
+ * The distance is the minimum distance of any of the points from
+ * each edge.
+ */
+ distance_type distance () const
+ {
+ db::edge e1 = first (), e2 = second ();
+ if (! e1.intersect (e2)) {
+ distance_type d12 = std::min (e2.euclidian_distance (e1.p1 ()), e2.euclidian_distance (e1.p2 ()));
+ distance_type d21 = std::min (e1.euclidian_distance (e2.p1 ()), e1.euclidian_distance (e2.p2 ()));
+ return std::min (d12, d21);
+ } else {
+ return 0;
+ }
+ }
+
/**
* @brief A method binding of operator* (mainly for automation purposes)
*/
diff --git a/src/db/db/dbEdgePairFilters.cc b/src/db/db/dbEdgePairFilters.cc
new file mode 100644
index 000000000..541f33731
--- /dev/null
+++ b/src/db/db/dbEdgePairFilters.cc
@@ -0,0 +1,81 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2021 Matthias Koefferlein
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+
+#include "dbCommon.h"
+
+#include "dbEdgePairFilters.h"
+#include "dbEdges.h"
+
+namespace db
+{
+
+// ---------------------------------------------------------------------------------------------------
+// EdgeFilterBasedEdgePairFilter implementation
+
+EdgeFilterBasedEdgePairFilter::EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match)
+ : mp_edge_filter (edge_filter), m_one_must_match (one_must_match)
+{
+ // .. nothing yet ..
+}
+
+EdgeFilterBasedEdgePairFilter::~EdgeFilterBasedEdgePairFilter ()
+{
+ // .. nothing yet ..
+}
+
+bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair) const
+{
+ if (m_one_must_match) {
+ return mp_edge_filter->selected (edge_pair.first ()) || mp_edge_filter->selected (edge_pair.second ());
+ } else {
+ return mp_edge_filter->selected (edge_pair.first ()) && mp_edge_filter->selected (edge_pair.second ());
+ }
+}
+
+const TransformationReducer *EdgeFilterBasedEdgePairFilter::vars () const
+{
+ return mp_edge_filter->vars ();
+}
+
+bool EdgeFilterBasedEdgePairFilter::wants_variants () const
+{
+ return mp_edge_filter->wants_variants ();
+}
+
+// ---------------------------------------------------------------------------------------------------
+// EdgePairFilterByDistance implementation
+
+EdgePairFilterByDistance::EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted)
+ : m_min_distance (min_distance), m_max_distance (max_distance), m_inverted (inverted)
+{
+ // .. nothing yet ..
+}
+
+bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const
+{
+ distance_type dist = edge_pair.distance ();
+ bool sel = (dist >= m_min_distance && dist < m_max_distance);
+ return m_inverted ? !sel : sel;
+}
+
+}
diff --git a/src/db/db/dbEdgePairFilters.h b/src/db/db/dbEdgePairFilters.h
new file mode 100644
index 000000000..9f25858e8
--- /dev/null
+++ b/src/db/db/dbEdgePairFilters.h
@@ -0,0 +1,82 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2021 Matthias Koefferlein
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+
+#ifndef HDR_dbEdgePairFilters
+#define HDR_dbEdgePairFilters
+
+#include "dbEdgePairs.h"
+
+namespace db
+{
+
+class EdgeFilterBase;
+
+/**
+ * @brief A base class for edge pair filters based on edge filters
+ *
+ * If "one_must_match" is true, it is sufficient for one edge to be selected.
+ * If it is false, both edges need to be selected to make the edge pair selected.
+ *
+ * NOTE: the edge filter is not owned by the edge pair filter object.
+ */
+class DB_PUBLIC EdgeFilterBasedEdgePairFilter
+ : public EdgePairFilterBase
+{
+public:
+ EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match);
+ virtual ~EdgeFilterBasedEdgePairFilter ();
+
+ virtual bool selected (const db::EdgePair &edge_pair) const;
+ virtual const TransformationReducer *vars () const;
+ virtual bool wants_variants () const;
+
+private:
+ EdgeFilterBase *mp_edge_filter;
+ bool m_one_must_match;
+};
+
+/**
+ * @brief Filters edge pairs based on the distance of the edges.
+ *
+ * The distance is measured as the smallest distance between each of the points of the two edges.
+ */
+class DB_PUBLIC EdgePairFilterByDistance
+ : public EdgePairFilterBase
+{
+public:
+ typedef db::coord_traits::distance_type distance_type;
+
+ EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted);
+
+ virtual bool selected (const db::EdgePair &edge_pair) const;
+ virtual const TransformationReducer *vars () const { return 0; }
+ virtual bool wants_variants () const { return false; }
+
+private:
+ distance_type m_min_distance, m_max_distance;
+ bool m_inverted;
+};
+
+}
+
+#endif
diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc
index 9412eb815..449131cbd 100644
--- a/src/db/db/dbEdgePairs.cc
+++ b/src/db/db/dbEdgePairs.cc
@@ -38,6 +38,9 @@
namespace db
{
+// ---------------------------------------------------------------------------------------------------
+// EdgePairs implementation
+
EdgePairs::EdgePairs ()
: mp_delegate (new EmptyEdgePairs ())
{
diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h
index e2200e465..73a82c40a 100644
--- a/src/db/db/dbEdgePairs.h
+++ b/src/db/db/dbEdgePairs.h
@@ -42,17 +42,13 @@ class Edges;
class Region;
class DeepShapeStore;
class TransformationReducer;
+class EdgeFilterBase;
/**
* @brief An edge pair set iterator
*
* The iterator delivers the edge pairs of the edge pair set
*/
-/**
- * @brief An edge set iterator
- *
- * The iterator delivers the edges of the edge set
- */
class DB_PUBLIC EdgePairsIterator
: public generic_shape_iterator
{
@@ -117,7 +113,7 @@ public:
EdgePairFilterBase () { }
virtual ~EdgePairFilterBase () { }
- virtual bool selected (const db::EdgePair &edge) const = 0;
+ virtual bool selected (const db::EdgePair &edge_pair) const = 0;
virtual const TransformationReducer *vars () const = 0;
virtual bool wants_variants () const = 0;
};
diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h
index 2954f81e9..4944ef3ed 100644
--- a/src/db/db/dbEdgesDelegate.h
+++ b/src/db/db/dbEdgesDelegate.h
@@ -32,6 +32,7 @@
#include "dbShapeCollection.h"
#include "dbShapeCollectionUtils.h"
#include "dbGenericShapeIterator.h"
+#include "dbHash.h"
#include
#include
diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc
index ac8d0adfe..205c3f892 100644
--- a/src/db/db/dbLayoutToNetlist.cc
+++ b/src/db/db/dbLayoutToNetlist.cc
@@ -32,6 +32,7 @@
#include "dbLayoutVsSchematic.h"
#include "dbLayoutToNetlistFormatDefs.h"
#include "dbLayoutVsSchematicFormatDefs.h"
+#include "dbShapeProcessor.h"
#include "tlGlobPattern.h"
namespace db
@@ -246,11 +247,21 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co
extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling);
}
-void LayoutToNetlist::connect (const db::Region &l)
+void LayoutToNetlist::reset_extracted ()
{
if (m_netlist_extracted) {
- throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
+
+ m_net_clusters.clear ();
+ mp_netlist.reset (0);
+
+ m_netlist_extracted = false;
+
}
+}
+
+void LayoutToNetlist::connect (const db::Region &l)
+{
+ reset_extracted ();
if (! is_persisted (l)) {
register_layer (l, make_new_name ());
@@ -265,9 +276,8 @@ void LayoutToNetlist::connect (const db::Region &l)
void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b)
{
- if (m_netlist_extracted) {
- throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
- }
+ reset_extracted ();
+
if (! is_persisted (a)) {
register_layer (a, make_new_name ());
}
@@ -286,9 +296,8 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap
size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
{
- if (m_netlist_extracted) {
- throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
- }
+ reset_extracted ();
+
if (! is_persisted (l)) {
register_layer (l, make_new_name ());
}
@@ -1293,6 +1302,93 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
}
}
+namespace
+{
+
+class PolygonAreaAndPerimeterCollector
+ : public db::PolygonSink
+{
+public:
+ typedef db::Polygon polygon_type;
+ typedef polygon_type::perimeter_type perimeter_type;
+ typedef polygon_type::area_type area_type;
+
+ PolygonAreaAndPerimeterCollector ()
+ : m_area (0), m_perimeter (0)
+ { }
+
+ area_type area () const
+ {
+ return m_area;
+ }
+
+ perimeter_type perimeter () const
+ {
+ return m_perimeter;
+ }
+
+ virtual void put (const db::Polygon &poly)
+ {
+ m_area += poly.area ();
+ m_perimeter += poly.perimeter ();
+ }
+
+public:
+ area_type m_area;
+ perimeter_type m_perimeter;
+};
+
+}
+
+static void
+compute_area_and_perimeter_of_net_shapes (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Polygon::area_type &area, db::Polygon::perimeter_type &perimeter)
+{
+ db::EdgeProcessor ep;
+
+ // count vertices and reserve space
+ size_t n = 0;
+ for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
+ n += rci->polygon_ref ().vertices ();
+ }
+ ep.reserve (n);
+
+ size_t p = 0;
+ for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
+ ep.insert (rci.trans () * rci->polygon_ref (), ++p);
+ }
+
+ PolygonAreaAndPerimeterCollector ap_collector;
+ db::PolygonGenerator pg (ap_collector, false);
+ db::SimpleMerge op;
+ ep.process (pg, op);
+
+ area = ap_collector.area ();
+ perimeter = ap_collector.perimeter ();
+}
+
+static void
+get_merged_shapes_of_net (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Shapes &shapes)
+{
+ db::EdgeProcessor ep;
+
+ // count vertices and reserve space
+ size_t n = 0;
+ for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
+ n += rci->polygon_ref ().vertices ();
+ }
+ ep.reserve (n);
+
+ size_t p = 0;
+ for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
+ ep.insert (rci.trans () * rci->polygon_ref (), ++p);
+ }
+
+ db::ShapeGenerator sg (shapes);
+ db::PolygonGenerator pg (sg, false);
+ db::SimpleMerge op;
+ ep.process (pg, op);
+}
+
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes)
{
// TODO: that's basically too much .. we only need the clusters
@@ -1318,56 +1414,65 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
continue;
}
- db::Region rgate, rmetal;
-
- deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0);
- deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0);
-
- double agate = 0.0;
- if (fabs (gate_area_factor) > 1e-6) {
- agate += rgate.area () * dbu * dbu * gate_area_factor;
- }
- if (fabs (gate_perimeter_factor) > 1e-6) {
- agate += rgate.perimeter () * dbu * gate_perimeter_factor;
- }
-
- double ametal = 0.0;
- if (fabs (metal_area_factor) > 1e-6) {
- ametal += rmetal.area () * dbu * dbu * metal_area_factor;
- }
- if (fabs (metal_perimeter_factor) > 1e-6) {
- ametal += rmetal.perimeter () * dbu * metal_perimeter_factor;
- }
-
double r = ratio;
bool skip = false;
for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
- db::Region rdiode;
- deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 0);
+ db::Polygon::area_type adiode_int = 0;
+ db::Polygon::perimeter_type pdiode_int = 0;
+
+ compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (*d->first), adiode_int, pdiode_int);
if (fabs (d->second) < db::epsilon) {
- if (rdiode.area () > 0) {
+ if (adiode_int > 0) {
skip = true;
}
} else {
- r += rdiode.area () * dbu * dbu * d->second;
+ r += adiode_int * dbu * dbu * d->second;
}
}
if (! skip) {
- if (tl::verbosity () >= 50) {
- tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r);
+ db::Polygon::area_type agate_int = 0;
+ db::Polygon::perimeter_type pgate_int = 0;
+
+ compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (gate), agate_int, pgate_int);
+
+ double agate = 0.0;
+ if (fabs (gate_area_factor) > 1e-6) {
+ agate += agate_int * dbu * dbu * gate_area_factor;
+ }
+ if (fabs (gate_perimeter_factor) > 1e-6) {
+ agate += pgate_int * dbu * gate_perimeter_factor;
}
- if (agate > dbu * dbu && ametal / agate > r + db::epsilon) {
- db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
- for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) {
- shapes.insert (*r);
+ if (agate > dbu * dbu) {
+
+ db::Polygon::area_type ametal_int = 0;
+ db::Polygon::perimeter_type pmetal_int = 0;
+
+ compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (metal), ametal_int, pmetal_int);
+
+ double ametal = 0.0;
+ if (fabs (metal_area_factor) > 1e-6) {
+ ametal += ametal_int * dbu * dbu * metal_area_factor;
}
+ if (fabs (metal_perimeter_factor) > 1e-6) {
+ ametal += pmetal_int * dbu * metal_perimeter_factor;
+ }
+
+ if (tl::verbosity () >= 50) {
+ tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r);
+ }
+
+ if (ametal / agate > r + db::epsilon) {
+ db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
+ get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes);
+ }
+
}
}
diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h
index 08cb3eb46..00cacb774 100644
--- a/src/db/db/dbLayoutToNetlist.h
+++ b/src/db/db/dbLayoutToNetlist.h
@@ -359,6 +359,14 @@ public:
*/
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers);
+ /**
+ * @brief Resets the extracted netlist
+ *
+ * This method will invalidate the netlist and extraction. It is called automatically when
+ * cone of the connect methods is called.
+ */
+ void reset_extracted ();
+
/**
* @brief Defines an intra-layer connection for the given layer.
* The layer is either an original layer created with "make_layer" and it's variants or
@@ -532,6 +540,14 @@ public:
*/
void set_netlist_extracted ();
+ /**
+ * @brief Gets a value indicating whether the netlist has been extracted
+ */
+ bool is_netlist_extracted () const
+ {
+ return m_netlist_extracted;
+ }
+
/**
* @brief Gets the internal DeepShapeStore object
*
diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc
index 1e1d3140d..a59d2ec35 100644
--- a/src/db/db/dbLocalOperation.cc
+++ b/src/db/db/dbLocalOperation.cc
@@ -56,7 +56,6 @@ void local_operation::compute_local (db::Layout *layout, const shape
}
for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
-
const TS &subject_shape = interactions.subject_shape (i->first);
shape_interactions single_interactions;
diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc
index 94d26a944..ed68821fa 100644
--- a/src/db/db/dbNetlistCompare.cc
+++ b/src/db/db/dbNetlistCompare.cc
@@ -3052,7 +3052,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
db::DeviceClass *db = const_cast (i->second.second);
const db::DeviceParameterCompareDelegate *cmp = da->parameter_compare_delegate ();
- db->set_parameter_compare_delegate (cmp ? cmp->clone () : 0);
+ db->set_parameter_compare_delegate (const_cast (cmp));
}
diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc
index b1a1c84ed..5bf03d678 100644
--- a/src/db/db/dbNetlistSpiceWriter.cc
+++ b/src/db/db/dbNetlistSpiceWriter.cc
@@ -130,7 +130,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << format_name (dev.device_class ()->name ());
}
- } else if (res) {
+ } else if (res || res3) {
os << "R";
os << format_name (dev.expanded_name ());
diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc
index 57b2a4380..2671686ac 100644
--- a/src/db/db/dbOriginalLayerRegion.cc
+++ b/src/db/db/dbOriginalLayerRegion.cc
@@ -29,6 +29,7 @@
#include "dbDeepEdges.h"
#include "dbDeepRegion.h"
#include "dbDeepShapeStore.h"
+#include "dbCellGraphUtils.h"
#include "tlGlobPattern.h"
namespace db
@@ -186,6 +187,96 @@ OriginalLayerRegion::min_coherence_changed ()
m_merged_polygons_valid = false;
}
+size_t
+OriginalLayerRegion::count () const
+{
+ // NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon
+ // layout changes
+ db::RecursiveShapeIterator iter = m_iter;
+
+ if (iter.has_complex_region () || iter.region () != db::Box::world () || ! iter.enables ().empty () || ! iter.disables ().empty ()) {
+
+ // complex case with a search region - use the iterator to determine the count (expensive)
+ size_t n = 0;
+ for (db::RecursiveShapeIterator i = iter; ! i.at_end (); ++i) {
+ ++n;
+ }
+
+ return n;
+
+ } else {
+
+ // otherwise we can utilize the CellCounter
+
+ size_t n = 0;
+
+ const db::Layout &layout = *iter.layout ();
+
+ std::set cells;
+ iter.top_cell ()->collect_called_cells (cells);
+ cells.insert (iter.top_cell ()->cell_index ());
+
+ db::CellCounter cc (&layout);
+ for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
+ if (cells.find (*c) == cells.end ()) {
+ continue;
+ }
+ size_t nn = 0;
+ if (iter.multiple_layers ()) {
+ for (std::vector::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) {
+ nn += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions);
+ }
+ } else if (iter.layer () < layout.layers ()) {
+ nn += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions);
+ }
+ n += cc.weight (*c) * nn;
+ }
+
+ return n;
+
+ }
+}
+
+size_t
+OriginalLayerRegion::hier_count () const
+{
+ // NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon
+ // layout changes
+ db::RecursiveShapeIterator iter = m_iter;
+
+ if (iter.has_complex_region () || iter.region () != db::Box::world ()) {
+
+ // TODO: how to establish a "hierarchical" interpretation in this case?
+ return count ();
+
+ } else {
+
+ size_t n = 0;
+
+ const db::Layout &layout = *iter.layout ();
+
+ std::set cells;
+ iter.top_cell ()->collect_called_cells (cells);
+ cells.insert (iter.top_cell ()->cell_index ());
+
+ for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
+ if (cells.find (*c) == cells.end ()) {
+ continue;
+ }
+ if (iter.multiple_layers ()) {
+ for (std::vector::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) {
+ n += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions);
+ }
+ } else if (iter.layer () < layout.layers ()) {
+ n += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions);
+ }
+ }
+
+ return n;
+
+ }
+}
+
RegionIteratorDelegate *
OriginalLayerRegion::begin () const
{
diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h
index 23af03bc9..1ea81d966 100644
--- a/src/db/db/dbOriginalLayerRegion.h
+++ b/src/db/db/dbOriginalLayerRegion.h
@@ -58,6 +58,8 @@ public:
virtual bool empty () const;
virtual bool is_merged () const;
+ virtual size_t count () const;
+ virtual size_t hier_count () const;
virtual const db::Polygon *nth (size_t n) const;
virtual bool has_valid_polygons () const;
diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h
index 95c2b0e99..e96512e2d 100644
--- a/src/db/db/dbRecursiveShapeIterator.h
+++ b/src/db/db/dbRecursiveShapeIterator.h
@@ -550,11 +550,13 @@ public:
}
/**
- * @brief Get the layer of the current shape
+ * @brief Gets the layer of the current shape
*/
unsigned int layer () const
{
- validate (0);
+ if (m_has_layers) {
+ validate (0);
+ }
return m_layer;
}
@@ -562,7 +564,7 @@ public:
* @brief Gets the layers from which the shapes are taken from
*
* The returned layers are useful only if \multiple_layers is
- * true.
+ * true. Otherwise use \layer to get the iterated layer.
*/
const std::vector &layers () const
{
diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h
index a5178f606..3924f7ab4 100644
--- a/src/db/db/dbRegionDelegate.h
+++ b/src/db/db/dbRegionDelegate.h
@@ -34,8 +34,10 @@
#include "dbShapeCollection.h"
#include "dbGenericShapeIterator.h"
#include "dbRegionLocalOperations.h"
+#include "dbHash.h"
#include
+#include
namespace db {
diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc
index 4ce824442..3fcc09bf5 100644
--- a/src/db/db/dbRegionLocalOperations.cc
+++ b/src/db/db/dbRegionLocalOperations.cc
@@ -240,6 +240,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape
// the processor. Reason: the search range is limited, hence not all necessary components may have been
// captured.
db::EdgeProcessor ep;
+ ep.set_base_verbosity (50);
ep.clear ();
size_t i = 0;
@@ -561,6 +562,7 @@ void interacting_local_operation::do_compute_local (db::Layout * /*l
}
db::EdgeProcessor ep;
+ ep.set_base_verbosity (50);
std::set others;
for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
@@ -597,6 +599,7 @@ void interacting_local_operation::do_compute_local (db::Layout * /*l
// in counted mode we need to merge the shapes because they might overlap
db::EdgeProcessor ep_merge;
+ ep_merge.set_base_verbosity (50);
size_t i = 0;
for (typename std::set::const_iterator o = others.begin (); o != others.end (); ++o) {
@@ -730,6 +733,7 @@ void pull_local_operation::do_compute_local (db::Layout * /*layout*/
std::unordered_set &result = results.front ();
db::EdgeProcessor ep;
+ ep.set_base_verbosity (50);
std::set others;
for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc
index 9134dc12f..89c90d84c 100644
--- a/src/db/db/dbRegionProcessors.cc
+++ b/src/db/db/dbRegionProcessors.cc
@@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo
db::Point pn = ctr [j];
if (m_checker (pt - pp, pn - pt)) {
- delivery.make_point (pt);
+ delivery.make_point (pt, db::Edge (pp, pt), db::Edge (pt, pn));
}
pp = pt;
diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h
index d91613148..516529144 100644
--- a/src/db/db/dbRegionProcessors.h
+++ b/src/db/db/dbRegionProcessors.h
@@ -41,7 +41,7 @@ namespace db
class DB_PUBLIC CornerPointDelivery
{
public:
- virtual void make_point (const db::Point &pt) const = 0;
+ virtual void make_point (const db::Point &pt, const db::Edge &e1, const db::Edge &e2) const = 0;
};
/**
@@ -55,7 +55,7 @@ public:
: m_d (dim, dim), mp_result (&result)
{ }
- virtual void make_point (const db::Point &pt) const
+ virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
{
mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d)));
}
@@ -76,7 +76,7 @@ public:
: mp_result (&result)
{ }
- virtual void make_point (const db::Point &pt) const
+ virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
{
mp_result->push_back (db::Edge (pt, pt));
}
@@ -86,6 +86,26 @@ private:
std::vector *mp_result;
};
+/**
+ * @brief An interface to accept corners and turns them into edge pairs
+ */
+class DB_PUBLIC CornerEdgePairDelivery
+ : public CornerPointDelivery
+{
+public:
+ CornerEdgePairDelivery (std::vector &result)
+ : mp_result (&result)
+ { }
+
+ virtual void make_point (const db::Point &, const db::Edge &e1, const db::Edge &e2) const
+ {
+ mp_result->push_back (db::EdgePair (e1, e2));
+ }
+
+private:
+ std::vector *mp_result;
+};
+
/**
* @brief A helper class implementing the core corner detection algorithm
*/
@@ -155,6 +175,31 @@ public:
virtual bool wants_variants () const { return false; }
};
+/**
+ * @brief A corner detector delivering edge pairs for the corners
+ */
+class DB_PUBLIC CornersAsEdgePairs
+ : public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore
+{
+public:
+ CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
+ : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
+ {
+ // .. nothing yet ..
+ }
+
+ void process (const db::Polygon &poly, std::vector &result) const
+ {
+ detect_corners (poly, CornerEdgePairDelivery (result));
+ }
+
+ virtual const TransformationReducer *vars () const { return 0; }
+ virtual bool result_is_merged () const { return false; }
+ virtual bool result_must_not_be_merged () const { return true; } // to preserve dots
+ virtual bool requires_raw_input () const { return false; }
+ virtual bool wants_variants () const { return false; }
+};
+
// -----------------------------------------------------------------------------------
// Extents
diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc
index 9f0e6f513..b5f60cb05 100644
--- a/src/db/db/dbRegionUtils.cc
+++ b/src/db/db/dbRegionUtils.cc
@@ -154,13 +154,16 @@ Edge2EdgeCheckBase::finish (const Edge *o, const size_t &p)
}
}
-void
+bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner)
{
if (m_pass == 1) {
for (std::set >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
scanner.insert (&e->first, e->second);
}
+ return ! m_pseudo_edges.empty ();
+ } else {
+ return false;
}
}
@@ -442,11 +445,33 @@ poly2poly_check::add (const PolygonType *o1, size_t p1, const Polyg
enter (*o1, p1, *o2, p2);
}
+static bool interact (const db::Box &box, const db::Edge &e)
+{
+ if (! e.bbox ().touches (box)) {
+ return false;
+ } else if (e.is_ortho ()) {
+ return true;
+ } else {
+ return e.clipped (box).first;
+ }
+}
+
template
void
poly2poly_check::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2)
{
- if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
+ if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
+
+ bool take_all = mp_output->has_negative_edge_output ();
+
+ db::Box common_box;
+ if (! take_all) {
+ db::Vector e (mp_output->distance (), mp_output->distance ());
+ common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e);
+ if (common_box.empty ()) {
+ return;
+ }
+ }
m_scanner.clear ();
m_scanner.reserve (vertices (o1) + vertices (o2));
@@ -454,20 +479,30 @@ poly2poly_check::enter (const PolygonType &o1, size_t p1, const Pol
m_edges.clear ();
m_edges.reserve (vertices (o1) + vertices (o2));
+ bool any_o1 = false, any_o2 = false;
+
for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) {
- m_edges.push_back (*e);
- m_scanner.insert (& m_edges.back (), p1);
+ if (take_all || interact (common_box, *e)) {
+ m_edges.push_back (*e);
+ m_scanner.insert (& m_edges.back (), p1);
+ any_o1 = true;
+ }
}
for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) {
- m_edges.push_back (*e);
- m_scanner.insert (& m_edges.back (), p2);
+ if (take_all || interact (common_box, *e)) {
+ m_edges.push_back (*e);
+ m_scanner.insert (& m_edges.back (), p2);
+ any_o2 = true;
+ }
+ }
+
+ if (! take_all && (! any_o1 || ! any_o2)) {
+ return;
}
mp_output->feed_pseudo_edges (m_scanner);
- tl_assert (m_edges.size () == vertices (o1) + vertices (o2));
-
// temporarily disable intra-polygon check in that step .. we do that later in finish()
// if required (#650).
bool no_intra = mp_output->different_polygons ();
diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h
index a7e67ce61..cd759cb4f 100644
--- a/src/db/db/dbRegionUtils.h
+++ b/src/db/db/dbRegionUtils.h
@@ -601,7 +601,7 @@ public:
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (required for negative edge output - cancellation of perpendicular edges)
*/
- void feed_pseudo_edges (db::box_scanner &scanner);
+ bool feed_pseudo_edges (db::box_scanner &scanner);
/**
* @brief Reimplementation of the box_scanner_receiver interface
@@ -641,6 +641,14 @@ public:
m_has_negative_edge_output = f;
}
+ /**
+ * @brief Gets a flag indicating that this class wants negative edge output
+ */
+ bool has_negative_edge_output () const
+ {
+ return m_has_negative_edge_output;
+ }
+
/**
* @brief Sets a flag indicating that this class wants normal edge pair output
*/
@@ -649,6 +657,14 @@ public:
m_has_edge_pair_output = f;
}
+ /**
+ * @brief Gets a flag indicating that this class wants normal edge pair output
+ */
+ bool has_edge_pair_output () const
+ {
+ return m_has_edge_pair_output;
+ }
+
/**
* @brief Gets the distance value
*/
@@ -828,9 +844,13 @@ protected:
if (layer == 0) {
edge2edge_check
+
+When "larger than" constraints are used, this function will produce the edges from the
+first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
+the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
+actual edges from the first input (see separation for an example).
"error" - Prints an error
@@ -1216,6 +1222,11 @@ errors = in.drc(overlap(other) < 0.2.um)
 |
+
+When "larger than" constraints are used, this function will produce the edges from the
+first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
+the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
+actual edges from the first input (see separation for an example).
"overlapping" - Selects shapes overlapping with other shapes
@@ -1511,6 +1522,23 @@ work in generating error markers.
 |
+
+When "larger than" constraints are used, this function will produce the edges from the
+first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
+the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
+actual edges from the first input:
+
+
+l1_edges_without_l2 = l1.drc((separation(l2) >= 1.0).first_edges)
+
+
+The following image shows the effect of such a negative-output separation check:
+
+
+
+ |
+
+
"silent" - Resets verbose mode
@@ -1863,7 +1891,7 @@ errors = in.drc(0.1.um <= width < 0.2.um)
With a lower and upper limit or with the "equal" condition, the results are edges marking the positions on the
primary shape where the condition is met.
-With a lower limit alone, these markers are formed by two, identical but opposite edges attached to
+With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
the primary shape. Without an upper limit only, both edges are attached to different sides of the primary
shape.
diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml
index 4b9b0554c..24b008f5d 100644
--- a/src/lay/lay/doc/about/drc_ref_layer.xml
+++ b/src/lay/lay/doc/about/drc_ref_layer.xml
@@ -214,7 +214,7 @@ new_layer = layer.collect { |polygon| polygon.transformed(t) }
Usage:
-- layer.collect { |object| ... }
+- layer.collect_to_edge_pairs { |object| ... }
This method is similar to collect, but creates an edge pair layer. It expects the block to
@@ -224,7 +224,7 @@ deliver EdgePair,
Usage:
-- layer.collect { |object| ... }
+- layer.collect_to_edges { |object| ... }
This method is similar to collect, but creates an edge layer. It expects the block to
@@ -235,7 +235,7 @@ contours will be turned into edge sequences.
Usage:
-- layer.collect { |object| ... }
+- layer.collect_to_region { |object| ... }
This method is similar to collect, but creates a polygon layer. It expects the block to
@@ -263,6 +263,8 @@ The options available are:
- as_boxes : with this option, small boxes will be produced as markers
- as_dots : with this option, point-like edges will be produced instead of small boxes
+- as_edge_pairs : with this option, an edge pair is produced for each corner selected. The first edge
+is the incoming edge to the corner, the second edge the outgoing edge.
The following images show the effect of this method:
@@ -275,6 +277,21 @@ The following images show the effect of this method:
+"count" - Returns the number of objects on the layer
+
+Usage:
+
+
+The count is the number of raw objects, not merged
+regions or edges. This is the flat count - the number of polygons,
+edges or edge pairs seen from the top cell.
+"count" can be computationally expensive for original layers with
+clip regions or cell tree filters.
+
+See hier_count for a hierarchical (each cell counts once) count.
+
"covering" - Selects shapes or regions of self which completely cover (enclose) one or more shapes from the other region
Usage:
@@ -1187,6 +1204,21 @@ l = nil
By setting the layer to nil, it is ensured that it can no longer be accessed.
+"hier_count" - Returns the hierarchical number of objects on the layer
+
+Usage:
+
+
+The hier_count is the number of raw objects, not merged
+regions or edges, with each cell counting once.
+A high count to hier_count (flat to hierarchical) ratio is an indication
+of a good hierarchical compression.
+"hier_count" applies only to original layers without clip regions or
+cell filters and to layers in deep mode. Otherwise, hier_count gives
+the same value than count.
+
"holes" - Selects all polygon holes from the input
Usage:
@@ -1438,7 +1470,7 @@ See clean for a discussion of the raw state.
See isolated for a description of that method
-"isolated" - An isolation check
+"isolated" - An inter-polygon isolation check
Usage:
@@ -1449,8 +1481,8 @@ See isolated for a description of that method
Note: "isolated" and "iso" are available as operators for the "universal DRC" function Layer#drc within
the DRC framework. These variants have more options and are more intuitive to use. See isolated for more details.
-See space for a description of this method.
-In contrast to space, this
+See space for a description of this method. "isolated" is the space check variant which checks different polygons only.
+In contrast to space, the "isolated"
method is available for polygon layers only, since only on such layers
different polygons can be identified.
@@ -1845,7 +1877,7 @@ This method is available for polygons only.
It returns a new layer containing the selected shapes. A version which modifies self
is select_not_overlapping.
-"notch" - An intra-region spacing check
+"notch" - An intra-polygon spacing check
Usage:
@@ -1855,8 +1887,9 @@ is select_not_overlapping.
Note: "notch" is available as an operator for the "universal DRC" function Layer#drc within
the DRC framework. This variant has more options and is more intuitive to use. See notch for more details.
-See space for a description of this method.
-In contrast to space, this
+See space for a description of this method.
+"notch" is the space check variant which finds space violations within a single polygon, but not against other polygons.
+In contrast to space, the "notch"
method is available for polygon layers only, since only on such layers
different polygons can be identified. Also, opposite and rectangle error
filtering is not available for this method.
@@ -1918,6 +1951,9 @@ on polygons and edges (input1: red, input2: blue):
 |
+
+In deep mode, "or" or "|" does not imply merging. In deep mode,
+"or" is an alias for "+" ("add").
"output" - Outputs the content of the layer
@@ -2688,12 +2724,17 @@ The following images show the effect of various forms of the "sized" method:
Usage:
- layer.smoothed(d)
+- layer.smoothed(d, hv_keep)
"Smoothing" returns a simplified version of the polygons. Simplification is
achieved by removing vertices unless the resulting polygon deviates by more
than the given distance d from the original polygon.
+"hv_keep" is a boolean parameter which makes the smoothing function maintain
+horizontal or vertical edges. The default is false, meaning horizontal or
+vertical edges may be changed into tilted ones.
+
This method return a layer wit the modified polygons. Merged semantics applies for this
method (see raw and clean).
@@ -3119,6 +3160,7 @@ Shielding is enabled by default, but can be switched off with the "transparent"
- layer.with_angle(min .. max)
- layer.with_angle(value)
- layer.with_angle(min, max)
+- edge_pairlayer.with_angle(min, max [, both])
When called on an edge layer, the method selects edges by their angle,
@@ -3137,6 +3179,20 @@ When called on a polygon layer, this method selects corners which match the
given angle or is within the given angle interval. The angle is measured between the edges forming the corner.
For each corner, an edge pair containing the edges forming in the angle is returned.
+When called on an edge pair layer, this method selects edge pairs with one or both edges
+meeting the angle criterion. In this case an additional argument is accepted which can be
+either "both" (plain word) to indicate that both edges have to be within the given interval.
+Without this argument, it is sufficient for one edge to meet the criterion.
+
+Here are examples for "with_angle" on edge pair layers:
+
+
+# at least one edge needs to be horizontal
+ep1 = edge_pairs.with_angle(0)
+# both edges need to vertical
+ep2 = edge_pairs.with_angle(90, both)
+
+
A method delivering all objects not matching the angle criterion is without_angle.
The following images demonstrate some use cases of with_angle and without_angle:
@@ -3328,8 +3384,43 @@ direction. With the "tile_origin" option this allows full control over the area
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150))
+The "padding mode" indicates how the area outside the layout's bounding box is considered.
+There are two modes:
+
+
+- padding_zero : the outside area is considered zero density. This is the default mode.
+- padding_ignore : the outside area is ignored for the density computation.
+
+
+Example:
+
+
+low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), padding_ignore)
+
+
The complementary version of "with_density" is without_density.
+"with_distance" - Selects edge pairs by the distance of the edges
+
+Usage:
+
+- layer.with_distance(min .. max)
+- layer.with_distance(value)
+- layer.with_distance(min, max)
+
+
+The method selects edge pairs by the distance of their edges. The first version selects
+edge pairs with a distance larger or equal to min and less than max (but not equal).
+The second version selects edge pairs with exactly the given distance. The third
+version is similar to the first one, but allows specification of nil for min or
+max indicating that there is no lower or upper limit.
+
+The distance of the edges is defined by the minimum distance of all points from the
+edges involved. For edge pairs generated in geometrical checks this is equivalent
+to the actual distance of the original edges.
+
+This method is available for edge pair layers only.
+
"with_holes" - Selects all polygons with the specified number of holes
Usage:
@@ -3349,15 +3440,30 @@ which have the specified number of holes.
layer.with_length(min .. max)
layer.with_length(value)
layer.with_length(min, max)
+edge_pairlayer.with_length(min, max [, both])
-The method selects edges by their length. The first version selected
+The method selects edges by their length. The first version selects
edges with a length larger or equal to min and less than max (but not equal).
The second version selects edges with exactly the given length. The third
version is similar to the first one, but allows specification of nil for min or
max indicating that there is no lower or upper limit.
-This method is available for edge layers only.
+This method is available for edge and edge pair layers.
+
+When called on an edge pair layer, this method will select edge pairs if one
+or both of the edges meet the length criterion. Use the additional argument
+and pass "both" (plain word) to specify that both edges need to be within the given interval.
+By default, it's sufficient for one edge to meet the criterion.
+
+Here are examples for "with_length" on edge pair layers:
+
+
+# at least one edge needs to have a length of 1.0 <= l < 2.0
+ep1 = edge_pairs.with_length(1.um .. 2.um)
+# both edges need to have a length of exactly 2 um
+ep2 = edge_pairs.with_length(2.um, both)
+
"with_perimeter" - Selects polygons by perimeter
@@ -3402,11 +3508,21 @@ This method is available for polygon layers only.
layer.without_angle(min .. max)
layer.without_angle(value)
layer.without_angle(min, max)
+edge_pairlayer.without_angle(min, max [, both])
The method basically is the inverse of with_angle. It selects all edges
of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
is not inside the given interval (first and third form).
+
+A note on the "both" modifier (without_angle called on edge pairs): "both" means that
+both edges need to be "without_angle". For example
+
+
+# both edges are not horizontal or:
+# the edge pair is skipped if one edge is horizontal
+ep = edge_pairs.without_angle(0, both)
+
"without_area" - Selects polygons by area
@@ -3507,6 +3623,21 @@ This method is available for polygon layers only.
For details about the operations and the operation see with_density. This version will return the
tiles where the density is not within the given range.
+"without_distance" - Selects edge pairs by the distance of the edges
+
+Usage:
+
+- layer.without_distance(min .. max)
+- layer.without_distance(value)
+- layer.without_distance(min, max)
+
+
+The method basically is the inverse of with_distance. It selects all edge pairs
+of the edge pair layer which do not have the given distance (second form) or are
+not inside the given interval (first and third form).
+
+This method is available for edge pair layers only.
+
"without_holes" - Selects all polygons with the specified number of holes
Usage:
@@ -3526,13 +3657,23 @@ which do not have the specified number of holes.
layer.without_length(min .. max)
layer.without_length(value)
layer.without_length(min, max)
+edge_pairlayer.with_length(min, max [, both])
The method basically is the inverse of with_length. It selects all edges
of the edge layer which do not have the given length (second form) or are
not inside the given interval (first and third form).
-This method is available for edge layers only.
+This method is available for edge and edge pair layers.
+
+A note on the "both" modifier (without_length called on edge pairs): "both" means that
+both edges need to be "without_length". For example
+
+
+# both edges are not exactly 1 um in length, or:
+# the edge pair is skipped if one edge has a length of exactly 1 um
+ep = edge_pairs.without_length(1.um, both)
+
"without_perimeter" - Selects polygons by perimeter
diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml
index 25d84a583..8e8cf429d 100644
--- a/src/lay/lay/doc/about/drc_ref_netter.xml
+++ b/src/lay/lay/doc/about/drc_ref_netter.xml
@@ -99,6 +99,25 @@ connect(contact, metal1)
errors = antenna_check(gate, metal1, 50.0)
+Usually antenna checks apply to multiple metal layers. In this case,
+the connectivity needs to be extended after the first check to include
+the next metal layers. This can be achieved with incremental connects:
+
+
+# provide connections up to metal1
+connect(gate, poly)
+connect(poly, contact)
+connect(contact, metal1)
+metal1_errors = antenna_check(gate, metal1, 50.0)
+
+# now *add* connections up to metal2
+connect(metal1, via1)
+connect(via1, metal2)
+metal2_errors = antenna_check(gate, metal2, 50.0)
+
+... continue this scheme with further metal layers ...
+
+
Plasma induced damage can be rectified by including diodes
which create a safe current path for discharging the metal
islands. Such diodes can be identified with a recognition layer
diff --git a/src/lay/lay/doc/images/drc_separation1un.png b/src/lay/lay/doc/images/drc_separation1un.png
new file mode 100644
index 000000000..26a5fbd91
Binary files /dev/null and b/src/lay/lay/doc/images/drc_separation1un.png differ
diff --git a/src/lay/lay/layDRCLVSHelpResources.qrc b/src/lay/lay/layDRCLVSHelpResources.qrc
index cdc914da3..d11b89b5e 100644
--- a/src/lay/lay/layDRCLVSHelpResources.qrc
+++ b/src/lay/lay/layDRCLVSHelpResources.qrc
@@ -18,6 +18,7 @@
doc/images/drc_space3u.png
doc/images/drc_separation1.png
doc/images/drc_separation1u.png
+ doc/images/drc_separation1un.png
doc/images/drc_separation2.png
doc/images/drc_separation3.png
doc/images/drc_separation4.png
diff --git a/src/lay/lay/layLogViewerDialog.cc b/src/lay/lay/layLogViewerDialog.cc
index c5e3abe33..2ae7fc61b 100644
--- a/src/lay/lay/layLogViewerDialog.cc
+++ b/src/lay/lay/layLogViewerDialog.cc
@@ -120,9 +120,8 @@ LogFile::LogFile (size_t max_entries, bool register_global)
{
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
- m_timer.setSingleShot (false);
- m_timer.setInterval (100);
- m_timer.start ();
+ m_timer.setSingleShot (true);
+ m_timer.setInterval (0);
if (register_global) {
tl::info.add (&m_info_receiver, false);
@@ -253,30 +252,10 @@ LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continu
void
LogFile::yield ()
{
-#if 0
- // This looked like a good idea, but in fact it introduces a hell lot of instability
- // as it potentially leads to a recursion of events inside innocent functions. Remember
- // that log output may be generated from every function called in response of an event
- // and not every such function may process further events
-
- bool can_yield = false;
-
- {
- QMutexLocker locker (&m_lock);
-
- if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread () && (tl::Clock::current () - m_last_yield).seconds () > 0.1) {
- m_last_yield = tl::Clock::current ();
- can_yield = true;
- }
+ // will update on next processEvents
+ if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread ()) {
+ m_timer.start ();
}
-
- // use this opportunity to process events
- // NOTE: as process events may trigger further log output, it's necessary to do process events outside any other
- // method (e.g. add) which is subject to locking. Hence we avoid deadlocks.
- if (can_yield) {
- lay::ApplicationBase::instance ()->process_events (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers, true /*silent*/);
- }
-#endif
}
int
diff --git a/src/lay/lay/layLogViewerDialog.h b/src/lay/lay/layLogViewerDialog.h
index a449c7c08..55c9737a6 100644
--- a/src/lay/lay/layLogViewerDialog.h
+++ b/src/lay/lay/layLogViewerDialog.h
@@ -26,7 +26,6 @@
#include "ui_LogViewerDialog.h"
#include "tlLog.h"
-#include "tlTimer.h"
#include "layCommon.h"
#include
@@ -233,7 +232,6 @@ private:
size_t m_last_generation_id;
bool m_has_errors, m_has_warnings;
bool m_last_attn;
- tl::Clock m_last_yield;
/**
* @brief Adds an error
diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc
index 48e64cede..6d054e461 100644
--- a/src/lvs/unit_tests/lvsSimpleTests.cc
+++ b/src/lvs/unit_tests/lvsSimpleTests.cc
@@ -206,3 +206,8 @@ TEST(23_issue709)
run_test (_this, "empty_subcells", "empty_subcells.gds");
}
+// empty gds
+TEST(24_issue806)
+{
+ run_test (_this, "custom_compare", "custom_compare.gds");
+}
diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc
index c7aea7d6b..50fb183c5 100644
--- a/src/lvs/unit_tests/lvsTests.cc
+++ b/src/lvs/unit_tests/lvsTests.cc
@@ -152,7 +152,7 @@ TEST(16_private)
TEST(17_private)
{
test_is_long_runner ();
- run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17.lvsdb");
+ run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17b.lvsdb");
}
TEST(18_private)
@@ -170,6 +170,6 @@ TEST(19_private)
TEST(20_private)
{
// test_is_long_runner ();
- run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20.lvsdb");
+ run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20b.lvsdb");
}
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc
index cb1db602d..21f25c7e1 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc
@@ -74,6 +74,7 @@ class GDS2FormatDeclaration
tl::make_member (&db::GDS2WriterOptions::write_file_properties, "write-file-properties") +
tl::make_member (&db::GDS2WriterOptions::no_zero_length_paths, "no-zero-length-paths") +
tl::make_member (&db::GDS2WriterOptions::multi_xy_records, "multi-xy-records") +
+ tl::make_member (&db::GDS2WriterOptions::resolve_skew_arrays, "resolve-skew-arrays") +
tl::make_member (&db::GDS2WriterOptions::max_vertex_count, "max-vertex-count") +
tl::make_member (&db::GDS2WriterOptions::max_cellname_length, "max-cellname-length") +
tl::make_member (&db::GDS2WriterOptions::libname, "libname")
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h
index 79592d62a..30941c142 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h
@@ -109,6 +109,7 @@ public:
: max_vertex_count (8000),
no_zero_length_paths (false),
multi_xy_records (false),
+ resolve_skew_arrays (false),
max_cellname_length (32000),
libname ("LIB"),
user_units (1.0),
@@ -146,6 +147,13 @@ public:
*/
bool multi_xy_records;
+ /**
+ * @brief Resolve skew arrays into single instances
+ *
+ * Setting this property to true will resolve skew (non-orthogonal) arrays into single instances.
+ */
+ bool resolve_skew_arrays;
+
/**
* @brief Maximum length of cell names
*
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc
index fad889519..0ed5d14ae 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc
@@ -130,6 +130,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8);
size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4);
bool no_zero_length_paths = gds2_options.no_zero_length_paths;
+ bool resolve_skew_arrays = gds2_options.resolve_skew_arrays;
m_cell_name_map = db::WriterCellNameMap (max_cellname_length);
m_cell_name_map.replacement ('$');
@@ -281,7 +282,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint ();
- write_inst (sf, *inst, true /*normalize*/, layout, inst->prop_id ());
+ write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ());
}
@@ -346,14 +347,24 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
progress_checkpoint ();
}
+static bool is_orthogonal (const db::Vector &rv, const db::Vector &cv)
+{
+ return (rv.x () == 0 && cv.y () == 0) || (rv.y () == 0 && cv.x () == 0);
+}
+
void
-GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id)
+GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, bool resolve_skew_arrays, const db::Layout &layout, db::properties_id_type prop_id)
{
db::Vector a, b;
unsigned long amax, bmax;
bool is_reg = instance.is_regular_array (a, b, amax, bmax);
+ // skew arrays are resolved if required
+ if (is_reg && ! is_orthogonal (a, b) != 0 && resolve_skew_arrays) {
+ is_reg = false;
+ }
+
for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) {
db::Trans t = *ii;
diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h
index cc15680e6..3dada7332 100644
--- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h
+++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h
@@ -122,7 +122,7 @@ protected:
/**
* @brief Write an instance
*/
- void write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id);
+ void write_inst (double sf, const db::Instance &instance, bool normalize, bool resolve_skew_arrays, const db::Layout &layout, db::properties_id_type prop_id);
/**
* @brief Write a shape as box
diff --git a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc
index 8b732eba1..c8e62120e 100644
--- a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc
+++ b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc
@@ -65,6 +65,16 @@ static bool get_gds2_multi_xy_records (const db::SaveLayoutOptions *options)
return options->get_options ().multi_xy_records;
}
+static void set_gds2_resolve_skew_arrays (db::SaveLayoutOptions *options, bool n)
+{
+ options->get_options ().resolve_skew_arrays = n;
+}
+
+static bool get_gds2_resolve_skew_arrays (const db::SaveLayoutOptions *options)
+{
+ return options->get_options ().resolve_skew_arrays;
+}
+
static void set_gds2_write_file_properties (db::SaveLayoutOptions *options, bool n)
{
options->get_options ().write_file_properties = n;
@@ -129,7 +139,7 @@ static double get_gds2_user_units (const db::SaveLayoutOptions *options)
static
gsi::ClassExt gds2_writer_options (
gsi::method_ext ("gds2_max_vertex_count=", &set_gds2_max_vertex_count, gsi::arg ("count"),
- "@brief Set the maximum number of vertices for polygons to write\n"
+ "@brief Sets the maximum number of vertices for polygons to write\n"
"This property describes the maximum number of point for polygons in GDS2 files.\n"
"Polygons with more points will be split.\n"
"The minimum value for this property is 4. The maximum allowed value is about 4000 or 8000, depending on the\n"
@@ -138,24 +148,37 @@ gsi::ClassExt gds2_writer_options (
"\nThis property has been added in version 0.18.\n"
) +
gsi::method_ext ("gds2_max_vertex_count", &get_gds2_max_vertex_count,
- "@brief Get the maximum number of vertices for polygons to write\n"
+ "@brief Gets the maximum number of vertices for polygons to write\n"
"See \\gds2_max_vertex_count= method for a description of the maximum vertex count."
"\nThis property has been added in version 0.18.\n"
) +
gsi::method_ext ("gds2_multi_xy_records=", &set_gds2_multi_xy_records, gsi::arg ("flag"),
- "@brief Use multiple XY records in BOUNDARY elements for unlimited large polygons\n"
+ "@brief Uses multiple XY records in BOUNDARY elements for unlimited large polygons\n"
"\n"
"Setting this property to true allows producing polygons with an unlimited number of points \n"
"at the cost of incompatible formats. Setting it to true disables the \\gds2_max_vertex_count setting.\n"
"\nThis property has been added in version 0.18.\n"
) +
gsi::method_ext ("gds2_multi_xy_records?", &get_gds2_multi_xy_records,
- "@brief Get the property enabling multiple XY records for BOUNDARY elements\n"
+ "@brief Gets the property enabling multiple XY records for BOUNDARY elements\n"
"See \\gds2_multi_xy_records= method for a description of this property."
"\nThis property has been added in version 0.18.\n"
) +
+ gsi::method_ext ("gds2_resolve_skew_arrays=", &set_gds2_resolve_skew_arrays, gsi::arg ("flag"),
+ "@brief Resolves skew arrays into single instances\n"
+ "\n"
+ "Setting this property to true will make skew (non-orthongonal) arrays being resolved into single instances.\n"
+ "Skew arrays happen if either the row or column vector isn't paralell to x or y axis. Such arrays can cause problems with "
+ "some legacy software and can be disabled with this option.\n"
+ "\nThis property has been added in version 0.27.1.\n"
+ ) +
+ gsi::method_ext ("gds2_resolve_skew_arrays?", &get_gds2_resolve_skew_arrays,
+ "@brief Gets a value indicating whether to resolve skew arrays into single instances\n"
+ "See \\gds2_resolve_skew_arrays= method for a description of this property."
+ "\nThis property has been added in version 0.27.1.\n"
+ ) +
gsi::method_ext ("gds2_write_timestamps=", &set_gds2_write_timestamps, gsi::arg ("flag"),
- "@brief Write the current time into the GDS2 timestamps if set to true\n"
+ "@brief Writes the current time into the GDS2 timestamps if set to true\n"
"\n"
"If this property is set to false, the time fields will all be zero. This somewhat simplifies compare and diff "
"applications.\n"
diff --git a/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui b/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui
index 4cc503688..72c6020f4 100644
--- a/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui
+++ b/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui
@@ -50,58 +50,13 @@
6
- -
-
-
- Max. vertices
-
-
-
- -
+
-
Write current time to time stamps (BGNLIB, BGNSTR)
- -
-
-
- -
-
-
- (<4000 recommended, absolute limit 8191)
-
-
-
- -
-
-
- -
-
-
- (keep empty for unspecified limit)
-
-
-
- -
-
-
- Multi-XY record mode for boundaries
-(enables infinitely large polygons/paths at the cost of compatibility)
-
-
-
- -
-
-
- -
-
-
- Library name
-
-
-
-
@@ -109,13 +64,16 @@
- -
-
+
-
+
- Eliminate zero-length paths (convert to BOUNDARY)
+ (<4000 recommended, absolute limit 8191)
+ -
+
+
-
@@ -181,6 +139,55 @@
+ -
+
+
+ -
+
+
+ (keep empty for unspecified limit)
+
+
+
+ -
+
+
+ Multi-XY record mode for boundaries
+(enables infinitely large polygons/paths at the cost of compatibility)
+
+
+
+ -
+
+
+ Library name
+
+
+
+ -
+
+
+ Max. vertices
+
+
+
+ -
+
+
+ -
+
+
+ Eliminate zero-length paths (convert to BOUNDARY)
+
+
+
+ -
+
+
+ Resolve skew (non-orthogonal) arrays into single instances
+
+
+
diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc
index c24eda9b8..948b211a7 100644
--- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc
+++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc
@@ -61,6 +61,7 @@ GDS2WriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db:
mp_ui->write_file_properties->setChecked (options->write_file_properties);
mp_ui->no_zero_length_paths->setChecked (options->no_zero_length_paths);
mp_ui->multi_xy_cbx->setChecked (options->multi_xy_records);
+ mp_ui->resolve_skew_arrays_cbx->setChecked (options->resolve_skew_arrays);
mp_ui->max_vertex_le->setEnabled (! options->multi_xy_records);
mp_ui->max_vertex_le->setText (tl::to_qstring (tl::to_string (options->max_vertex_count)));
mp_ui->cell_name_length_le->setText (tl::to_qstring (tl::to_string (options->max_cellname_length)));
@@ -76,6 +77,7 @@ GDS2WriterOptionPage::commit (db::FormatSpecificWriterOptions *o, const db::Tech
unsigned int n;
options->multi_xy_records = mp_ui->multi_xy_cbx->isChecked ();
+ options->resolve_skew_arrays = mp_ui->resolve_skew_arrays_cbx->isChecked ();
options->write_timestamps = mp_ui->write_timestamps->isChecked ();
options->write_cell_properties = mp_ui->write_cell_properties->isChecked ();
options->write_file_properties = mp_ui->write_file_properties->isChecked ();
diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc
index 77c902223..35e25883c 100644
--- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc
+++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc
@@ -83,6 +83,19 @@ TEST(1)
run_test (_this, "arefs.gds", "arefs_ref.gds");
}
+TEST(1a)
+{
+ db::GDS2WriterOptions opt;
+ run_test (_this, "arefs_skew.gds", "arefs_skew1.gds", false, opt);
+}
+
+TEST(1b)
+{
+ db::GDS2WriterOptions opt;
+ opt.resolve_skew_arrays = true;
+ run_test (_this, "arefs_skew.gds", "arefs_skew2.gds", false, opt);
+}
+
TEST(2)
{
db::Manager m (false);
diff --git a/src/rba/rba/rbaInspector.cc b/src/rba/rba/rbaInspector.cc
index 65a32a9f8..25979b66d 100644
--- a/src/rba/rba/rbaInspector.cc
+++ b/src/rba/rba/rbaInspector.cc
@@ -338,7 +338,7 @@ public:
: m_obj (obj), mp_cls (0), m_members (Qnil)
{
rb_gc_register_address (&m_obj);
- mp_cls = find_cclass (rb_class_of (m_obj));
+ mp_cls = find_cclass_maybe_null (rb_class_of (m_obj));
m_members = rb_obj_instance_variables (m_obj);
rb_gc_register_address (&m_members);
diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc
index cfab92efe..65624843d 100644
--- a/src/rba/rba/rbaInternal.cc
+++ b/src/rba/rba/rbaInternal.cc
@@ -940,6 +940,13 @@ bool is_registered (const gsi::ClassBase *cls)
}
const gsi::ClassBase *find_cclass (VALUE k)
+{
+ const gsi::ClassBase *cls = find_cclass_maybe_null (k);
+ tl_assert (cls != 0);
+ return cls;
+}
+
+const gsi::ClassBase *find_cclass_maybe_null (VALUE k)
{
std::map ::const_iterator cls;
@@ -954,8 +961,7 @@ const gsi::ClassBase *find_cclass (VALUE k)
}
}
- tl_assert (cls != cls_map.end ());
- return cls->second;
+ return cls != cls_map.end () ? cls->second : 0;
}
}
diff --git a/src/rba/rba/rbaInternal.h b/src/rba/rba/rbaInternal.h
index ca885968d..73a1a0011 100644
--- a/src/rba/rba/rbaInternal.h
+++ b/src/rba/rba/rbaInternal.h
@@ -218,6 +218,11 @@ void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls);
*/
const gsi::ClassBase *find_cclass (VALUE k);
+/**
+ * @brief Find the class declaration from the Ruby object
+ */
+const gsi::ClassBase *find_cclass_maybe_null (VALUE k);
+
/**
* @brief Finds the Ruby class for a gsi class
*/
diff --git a/testdata/algo/lvs_test1_au.lvsdb.1 b/testdata/algo/lvs_test1_au.lvsdb.1
index a16b88d6d..a73ab07a0 100644
--- a/testdata/algo/lvs_test1_au.lvsdb.1
+++ b/testdata/algo/lvs_test1_au.lvsdb.1
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test1_au.lvsdb.2 b/testdata/algo/lvs_test1_au.lvsdb.2
index 42c2187d5..20133d556 100644
--- a/testdata/algo/lvs_test1_au.lvsdb.2
+++ b/testdata/algo/lvs_test1_au.lvsdb.2
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test1b_au.lvsdb.1 b/testdata/algo/lvs_test1b_au.lvsdb.1
index 83dedd5e2..3b206b741 100644
--- a/testdata/algo/lvs_test1b_au.lvsdb.1
+++ b/testdata/algo/lvs_test1b_au.lvsdb.1
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test1b_au.lvsdb.2 b/testdata/algo/lvs_test1b_au.lvsdb.2
index a8c999396..455337d3b 100644
--- a/testdata/algo/lvs_test1b_au.lvsdb.2
+++ b/testdata/algo/lvs_test1b_au.lvsdb.2
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test2_au.lvsdb.1 b/testdata/algo/lvs_test2_au.lvsdb.1
index 52e53e669..cecb9d910 100644
--- a/testdata/algo/lvs_test2_au.lvsdb.1
+++ b/testdata/algo/lvs_test2_au.lvsdb.1
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test2_au.lvsdb.2 b/testdata/algo/lvs_test2_au.lvsdb.2
index a73de534d..0ce0bf4d9 100644
--- a/testdata/algo/lvs_test2_au.lvsdb.2
+++ b/testdata/algo/lvs_test2_au.lvsdb.2
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test2b_au.lvsdb.1 b/testdata/algo/lvs_test2b_au.lvsdb.1
index a3bbd637b..47263e0e2 100644
--- a/testdata/algo/lvs_test2b_au.lvsdb.1
+++ b/testdata/algo/lvs_test2b_au.lvsdb.1
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/algo/lvs_test2b_au.lvsdb.2 b/testdata/algo/lvs_test2b_au.lvsdb.2
index 8a9639fac..cbe6175f5 100644
--- a/testdata/algo/lvs_test2b_au.lvsdb.2
+++ b/testdata/algo/lvs_test2b_au.lvsdb.2
@@ -9,7 +9,7 @@ layout(
# This section lists the mask layers (drawing or derived) and their connections.
# Mask layers
- layer(bulk '1/0')
+ layer(bulk)
layer(nwell '1/0')
layer(poly '3/0')
layer(poly_lbl '3/1')
diff --git a/testdata/drc/drcGenericTests_6.drc b/testdata/drc/drcGenericTests_6.drc
index 7e345876f..34eeb7d0c 100644
--- a/testdata/drc/drcGenericTests_6.drc
+++ b/testdata/drc/drcGenericTests_6.drc
@@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0)
l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners
l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners
l1.drc(corners(as_boxes) <= -90).output(104, 0)
+l1.drc(corners(as_edge_pairs) == 90).output(105, 0)
l1.drc(middle).output(110, 0)
l1.drc(middle(as_dots)).output(111, 0)
diff --git a/testdata/drc/drcGenericTests_au6.gds b/testdata/drc/drcGenericTests_au6.gds
index 1cdcbf50a..6674e7552 100644
Binary files a/testdata/drc/drcGenericTests_au6.gds and b/testdata/drc/drcGenericTests_au6.gds differ
diff --git a/testdata/drc/drcGenericTests_au6d.gds b/testdata/drc/drcGenericTests_au6d.gds
index 0eedf1c58..8bf276f1d 100644
Binary files a/testdata/drc/drcGenericTests_au6d.gds and b/testdata/drc/drcGenericTests_au6d.gds differ
diff --git a/testdata/drc/drcSimpleTests_1.drc b/testdata/drc/drcSimpleTests_1.drc
index 28f5bd3a5..35e022c1d 100644
--- a/testdata/drc/drcSimpleTests_1.drc
+++ b/testdata/drc/drcSimpleTests_1.drc
@@ -3,7 +3,17 @@ dbu 0.001
target($drc_test_target, "TOP")
+def self.expect_count(layer, c, hc, where)
+ if layer.count != c
+ raise(where + ": Layer count #{layer.count} does not equal #{c}")
+ end
+ if layer.hier_count != hc
+ raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}")
+ end
+end
+
x = polygon_layer
+expect_count(x, 0, 0, "empty layer")
x.is_empty? == true || raise("unexpected value")
x.is_box? == false || raise("unexpected value")
x.insert(box(4.0, 0, 4.7, 0.7))
@@ -12,6 +22,7 @@ x.is_box? == true || raise("unexpected value")
x.insert(polygon([ p(0, 0), p(2.0, 0), p(1.0, 1.0) ]))
x.insert(polygon([ p(0, -5.0), p(2.0, -5.0), p(1.0, -6.0) ]))
x.insert(path([ p(0, -2), p(2.0, -2) ], 0.2))
+expect_count(x, 4, 4, "after 3x insert")
x.is_box? == false || raise("unexpected value")
x.output(10, 0)
diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc
index 66faa4c55..89de3aa2e 100644
--- a/testdata/drc/drcSimpleTests_2.drc
+++ b/testdata/drc/drcSimpleTests_2.drc
@@ -2,6 +2,15 @@
target($drc_test_target, "TOP")
source($drc_test_source, "TOP")
+def self.expect_count(layer, c, hc, where)
+ if layer.count != c
+ raise(where + ": Layer count #{layer.count} does not equal #{c}")
+ end
+ if layer.hier_count != hc
+ raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}")
+ end
+end
+
a1 = input(1)
b1 = input(2)
c1 = input(3)
@@ -57,6 +66,9 @@ a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0)
a1.corners.sized(0.05).output(1060, 0)
a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0)
a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0)
+a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0)
+a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0)
+a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0)
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
@@ -89,3 +101,6 @@ a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplx
a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0)
a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0)
+expect_count(a1.edges, 9, 9, "a1.edges")
+expect_count(a1.width(1.5), 5, 5, "a1.width(1.5)")
+
diff --git a/testdata/drc/drcSimpleTests_30.drc b/testdata/drc/drcSimpleTests_30.drc
index de3b78fee..09fb0ea68 100644
--- a/testdata/drc/drcSimpleTests_30.drc
+++ b/testdata/drc/drcSimpleTests_30.drc
@@ -9,10 +9,18 @@ b.output(0, 0)
a.output(1, 0)
a.with_density(0..0.1, tile_size(10.um), tile_boundary(b)).output(100, 0)
-a.without_density(0..0.1, tile_size(10.um)).output(101, 0)
+a.without_density(0..0.1, tile_size(10.um), padding_zero).output(101, 0)
a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um)).output(102, 0)
a.with_density(0.1, nil, tile_size(10.um, 20.um)).output(103, 0)
a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um)).output(104, 0)
a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15)).output(105, 0)
-a.with_density(0..0.1, tile_size(100.um), tile_step(10.um)).output(110, 0)
+a.with_density(0..0.1, tile_size(100.um), tile_step(10.um), padding_zero).output(110, 0)
+
+a.with_density(0..0.1, tile_size(10.um), tile_boundary(b), padding_ignore).output(200, 0)
+a.without_density(0..0.1, tile_size(10.um), padding_ignore).output(201, 0)
+a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), padding_ignore).output(202, 0)
+a.with_density(0.1, nil, tile_size(10.um, 20.um), padding_ignore).output(203, 0)
+a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um), padding_ignore).output(204, 0)
+a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15), padding_ignore).output(205, 0)
+a.with_density(0..0.1, tile_size(100.um), tile_step(10.um), padding_ignore).output(210, 0)
diff --git a/testdata/drc/drcSimpleTests_30.gds b/testdata/drc/drcSimpleTests_30.gds
index 14b2f86b8..da42ce5a8 100644
Binary files a/testdata/drc/drcSimpleTests_30.gds and b/testdata/drc/drcSimpleTests_30.gds differ
diff --git a/testdata/drc/drcSimpleTests_4.drc b/testdata/drc/drcSimpleTests_4.drc
index b729ee968..37f2e53f5 100644
--- a/testdata/drc/drcSimpleTests_4.drc
+++ b/testdata/drc/drcSimpleTests_4.drc
@@ -4,6 +4,15 @@
source($drc_test_source, "TOPTOP_SMALL")
target($drc_test_target)
+def self.expect_count(layer, c, hc, where)
+ if layer.count != c
+ raise(where + ": Layer count #{layer.count} does not equal #{c}")
+ end
+ if layer.hier_count != hc
+ raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}")
+ end
+end
+
cell("TOPTOP_SMALL")
l1_flat = input(1)
@@ -66,12 +75,16 @@ r.output(1023, 0)
r.extents.output(1123, 0)
r = l1.space(0.5)
+expect_count(r, 3, 1, "r on l1")
r.output(1010, 0)
r.extents.output(1110, 0)
+expect_count(l1, 15, 5, "l1 before flatten")
l1.flatten
+expect_count(l1, 15, 15, "l1 after flatten")
r = l1.space(0.5)
r.output(1011, 0)
r.extents.output(1111, 0)
+expect_count(r, 3, 3, "r on l1.flatten")
diff --git a/testdata/drc/drcSimpleTests_48.drc b/testdata/drc/drcSimpleTests_48.drc
new file mode 100644
index 000000000..672fe9270
--- /dev/null
+++ b/testdata/drc/drcSimpleTests_48.drc
@@ -0,0 +1,44 @@
+
+source $drc_test_source
+target $drc_test_target
+
+if $drc_test_deep
+ deep
+end
+
+l1 = input(1, 0)
+l2 = input(2, 0)
+
+l1.output(1, 0)
+l2.output(2, 0)
+
+l1.drc(separation(l2, projection) < 1.0).output(100, 0)
+l1.drc(separation(l2, whole_edges, projection) < 1.0).output(101, 0)
+l1.drc(separation(l2, projection) >= 1.0).output(102, 0)
+l1.separation(l2, projection, 1.0).output(110, 0)
+l1.separation(l2, projection, 1.0, whole_edges).output(111, 0)
+
+l2.drc(separation(l1, projection) < 1.0).output(200, 0)
+l2.drc(separation(l1, whole_edges, projection) < 1.0).output(201, 0)
+l2.drc(separation(l1, projection) >= 1.0).output(202, 0)
+l2.separation(l1, projection, 1.0).output(210, 0)
+l2.separation(l1, projection, 1.0, whole_edges).output(211, 0)
+
+(l1 + l2).drc(space(projection) < 1.0).output(300, 0)
+(l1 + l2).drc(space(whole_edges, projection) < 1.0).output(301, 0)
+(l1 + l2).drc(space(projection) >= 1.0).output(302, 0)
+(l1 + l2).space(projection, 1.0).output(310, 0)
+(l1 + l2).space(projection, 1.0, whole_edges).output(311, 0)
+
+l1.drc(enclosing(l2, projection) < 1.0).output(400, 0)
+l1.drc(enclosing(l2, whole_edges, projection) < 1.0).output(401, 0)
+l1.drc(enclosing(l2, projection) >= 1.0).output(402, 0)
+l1.enclosing(l2, projection, 1.0).output(410, 0)
+l1.enclosing(l2, projection, 1.0, whole_edges).output(411, 0)
+
+l1.drc(overlap(l2, projection) < 1.0).output(500, 0)
+l1.drc(overlap(l2, whole_edges, projection) < 1.0).output(501, 0)
+l1.drc(overlap(l2, projection) >= 1.0).output(502, 0)
+l1.overlap(l2, projection, 1.0).output(510, 0)
+l1.overlap(l2, projection, 1.0, whole_edges).output(511, 0)
+
diff --git a/testdata/drc/drcSimpleTests_48.gds b/testdata/drc/drcSimpleTests_48.gds
new file mode 100644
index 000000000..8742b1c65
Binary files /dev/null and b/testdata/drc/drcSimpleTests_48.gds differ
diff --git a/testdata/drc/drcSimpleTests_49.drc b/testdata/drc/drcSimpleTests_49.drc
new file mode 100644
index 000000000..706171bc8
--- /dev/null
+++ b/testdata/drc/drcSimpleTests_49.drc
@@ -0,0 +1,41 @@
+
+source $drc_test_source
+target $drc_test_target
+
+if $drc_test_deep
+ deep
+end
+
+ep = input(1, 0).drc(space(projection) < 0.5)
+
+ep.polygons(0).output(100, 0)
+
+ep.with_distance(0..0.1).polygons(0).output(110, 0)
+ep.with_distance(0.25, nil).polygons(0).output(111, 0)
+ep.without_distance(0..0.1).polygons(0).output(120, 0)
+ep.without_distance(0.25, nil).polygons(0).output(121, 0)
+
+ep.with_angle(45.0).polygons(0).output(200, 0)
+ep.with_angle(0.0).polygons(0).output(201, 0)
+ep.with_angle(45.0..91.0).polygons(0).output(202, 0)
+ep.with_angle(45.0, both).polygons(0).output(210, 0)
+ep.with_angle(0.0, both).polygons(0).output(211, 0)
+ep.with_angle(45.0..91.0, both).polygons(0).output(212, 0)
+
+ep.without_angle(45.0).polygons(0).output(220, 0)
+ep.without_angle(0.0).polygons(0).output(221, 0)
+ep.without_angle(45.0..91.0).polygons(0).output(222, 0)
+ep.without_angle(45.0, both).polygons(0).output(230, 0)
+ep.without_angle(0.0, both).polygons(0).output(231, 0)
+ep.without_angle(45.0..91.0, both).polygons(0).output(232, 0)
+
+ep.with_length(0.5).polygons(0).output(300, 0)
+ep.with_length(0.4..0.51).polygons(0).output(301, 0)
+ep.with_length(0.5, both).polygons(0).output(310, 0)
+ep.with_length(0.4..0.51, both).polygons(0).output(311, 0)
+
+ep.without_length(0.5).polygons(0).output(320, 0)
+ep.without_length(0.4..0.51).polygons(0).output(321, 0)
+ep.without_length(0.5, both).polygons(0).output(330, 0)
+ep.without_length(0.4..0.51, both).polygons(0).output(331, 0)
+
diff --git a/testdata/drc/drcSimpleTests_49.gds b/testdata/drc/drcSimpleTests_49.gds
new file mode 100644
index 000000000..7c45ebc9e
Binary files /dev/null and b/testdata/drc/drcSimpleTests_49.gds differ
diff --git a/testdata/drc/drcSimpleTests_5.drc b/testdata/drc/drcSimpleTests_5.drc
index 8f46068f5..28b655ea6 100644
--- a/testdata/drc/drcSimpleTests_5.drc
+++ b/testdata/drc/drcSimpleTests_5.drc
@@ -1,5 +1,5 @@
-# Hierarchical antenna check
+# Flat antenna check
source($drc_test_source, "RINGO")
target($drc_test_target)
@@ -14,6 +14,17 @@ metal2 = input(8, 0)
gate = diff & poly
+connect(gate, poly)
+connect(poly, poly_cont)
+connect(poly_cont, metal1)
+
+antenna_check(gate, metal1, 1.0).output(201)
+antenna_check(gate, metal1, 2.0).output(202)
+antenna_check(gate, metal1, 3.0).output(203)
+antenna_check(gate, metal1, 4.0).output(204)
+
+clear_connections
+
connect(gate, poly)
connect(poly, poly_cont)
connect(poly_cont, metal1)
@@ -24,3 +35,4 @@ antenna_check(gate, metal2, 1.0).output(101)
antenna_check(gate, metal2, 5.0).output(105)
antenna_check(gate, metal2, 10.0).output(110)
antenna_check(gate, metal2, 50.0).output(150)
+
diff --git a/testdata/drc/drcSimpleTests_5i.drc b/testdata/drc/drcSimpleTests_5i.drc
new file mode 100644
index 000000000..973cdfa7e
--- /dev/null
+++ b/testdata/drc/drcSimpleTests_5i.drc
@@ -0,0 +1,33 @@
+
+# Flat antenna check
+
+source($drc_test_source, "RINGO")
+target($drc_test_target)
+
+diff = input(2, 0)
+poly = input(3, 0)
+contact = input(4, 0)
+poly_cont = input(5, 0)
+metal1 = input(6, 0)
+via1 = input(7, 0)
+metal2 = input(8, 0)
+
+gate = diff & poly
+
+connect(gate, poly)
+connect(poly, poly_cont)
+connect(poly_cont, metal1)
+
+antenna_check(gate, metal1, 1.0).output(201)
+antenna_check(gate, metal1, 2.0).output(202)
+antenna_check(gate, metal1, 3.0).output(203)
+antenna_check(gate, metal1, 4.0).output(204)
+
+connect(metal1, via1)
+connect(via1, metal2)
+
+antenna_check(gate, metal2, 1.0).output(101)
+antenna_check(gate, metal2, 5.0).output(105)
+antenna_check(gate, metal2, 10.0).output(110)
+antenna_check(gate, metal2, 50.0).output(150)
+
diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds
index 76c8b8afe..b916b5cae 100644
Binary files a/testdata/drc/drcSimpleTests_au2.gds and b/testdata/drc/drcSimpleTests_au2.gds differ
diff --git a/testdata/drc/drcSimpleTests_au30.gds b/testdata/drc/drcSimpleTests_au30.gds
index f09ab94b3..7caad645c 100644
Binary files a/testdata/drc/drcSimpleTests_au30.gds and b/testdata/drc/drcSimpleTests_au30.gds differ
diff --git a/testdata/drc/drcSimpleTests_au4.gds b/testdata/drc/drcSimpleTests_au4.gds
index 6920b9a33..5f4090148 100644
Binary files a/testdata/drc/drcSimpleTests_au4.gds and b/testdata/drc/drcSimpleTests_au4.gds differ
diff --git a/testdata/drc/drcSimpleTests_au48.gds b/testdata/drc/drcSimpleTests_au48.gds
new file mode 100644
index 000000000..4f81f169a
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au48.gds differ
diff --git a/testdata/drc/drcSimpleTests_au48d.gds b/testdata/drc/drcSimpleTests_au48d.gds
new file mode 100644
index 000000000..4f81f169a
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au48d.gds differ
diff --git a/testdata/drc/drcSimpleTests_au49.gds b/testdata/drc/drcSimpleTests_au49.gds
new file mode 100644
index 000000000..74b0bf9ca
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au49.gds differ
diff --git a/testdata/drc/drcSimpleTests_au49d.gds b/testdata/drc/drcSimpleTests_au49d.gds
new file mode 100644
index 000000000..cbbd8c6cb
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au49d.gds differ
diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds
index 3578cc1c0..064e177b1 100644
Binary files a/testdata/drc/drcSimpleTests_au5.gds and b/testdata/drc/drcSimpleTests_au5.gds differ
diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas
index ccb803594..ca77bf7fc 100644
Binary files a/testdata/drc/drcSuiteTests_au5.oas and b/testdata/drc/drcSuiteTests_au5.oas differ
diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas
index e9a693a4d..9ef67198b 100644
Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ
diff --git a/testdata/gds/arefs_skew.gds b/testdata/gds/arefs_skew.gds
new file mode 100644
index 000000000..e478b422d
Binary files /dev/null and b/testdata/gds/arefs_skew.gds differ
diff --git a/testdata/gds/arefs_skew1.gds b/testdata/gds/arefs_skew1.gds
new file mode 100644
index 000000000..a7738e3c7
Binary files /dev/null and b/testdata/gds/arefs_skew1.gds differ
diff --git a/testdata/gds/arefs_skew2.gds b/testdata/gds/arefs_skew2.gds
new file mode 100644
index 000000000..6183fcbd8
Binary files /dev/null and b/testdata/gds/arefs_skew2.gds differ
diff --git a/testdata/lvs/custom_compare.cir b/testdata/lvs/custom_compare.cir
new file mode 100644
index 000000000..e558a4c04
--- /dev/null
+++ b/testdata/lvs/custom_compare.cir
@@ -0,0 +1,7 @@
+* Extracted by KLayout
+
+* cell TOP
+.SUBCKT TOP
+* device instance $1 r0 *1 7.52,4.175 RES
+R$1 2 1 51 RES
+.ENDS TOP
diff --git a/testdata/lvs/custom_compare.gds b/testdata/lvs/custom_compare.gds
new file mode 100644
index 000000000..7f476cbca
Binary files /dev/null and b/testdata/lvs/custom_compare.gds differ
diff --git a/testdata/lvs/custom_compare.lvs b/testdata/lvs/custom_compare.lvs
new file mode 100644
index 000000000..25e60364c
--- /dev/null
+++ b/testdata/lvs/custom_compare.lvs
@@ -0,0 +1,72 @@
+
+source($lvs_test_source)
+report_lvs($lvs_test_target_lvsdb, true)
+target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout")
+
+schematic("custom_compare_sch.cir")
+
+deep
+
+# -------------------------------------------------------------------
+# Layers
+
+# Drawing layers
+nwell = input(1, 0)
+active = input(2, 0)
+pplus = input(3, 0)
+nplus = input(4, 0)
+poly = input(5, 0)
+thickox = input(6, 0)
+polyres = input(7, 0)
+contact = input(8, 0)
+metal1 = input(9, 0) # includes labels
+via1 = input(10, 0)
+metal2 = input(11, 0) # includes labels
+
+# Bulk layer for terminal provisioning
+bulk = polygon_layer
+
+# Computed layers
+poly_not_res = poly - polyres
+poly_in_res = poly & polyres
+
+# Resistor Definition
+res_ex = resistor("RES", 1.0)
+extract_devices(res_ex, { "C" => poly_not_res, "R" => poly_in_res })
+
+
+# -------------------------------------------------------------------
+# Connectivity
+
+# Inter-layer
+connect(poly_not_res, contact)
+
+# -------------------------------------------------------------------
+# Netlist and compare
+
+class ResistorComparator < RBA::GenericDeviceParameterCompare
+
+ def less(device_a, device_b)
+ delta = 1
+ param_id = RBA::DeviceClassResistor::PARAM_R
+ param = "R"
+ if (device_a.parameter(param_id) - device_b.parameter(param_id)).abs > delta
+ result = device_a.parameter(param_id) < device_b.parameter(param_id)
+ return result
+ else
+ return false
+ end
+ end
+
+end
+
+netlist.device_class_by_name("RES").equal_parameters = ResistorComparator::new()
+
+# Netlist normalization
+netlist.simplify
+
+# Hierarchy alignment (flatten out unmatched cells)
+align
+
+# Netlist vs. netlist
+compare
diff --git a/testdata/lvs/custom_compare.lvsdb b/testdata/lvs/custom_compare.lvsdb
new file mode 100644
index 000000000..c54b0fb98
--- /dev/null
+++ b/testdata/lvs/custom_compare.lvsdb
@@ -0,0 +1,111 @@
+#%lvsdb-klayout
+
+# Layout
+layout(
+ top(TOP)
+ unit(0.001)
+
+ # Layer section
+ # This section lists the mask layers (drawing or derived) and their connections.
+
+ # Mask layers
+ layer(l3 '8/0')
+ layer(l1)
+
+ # Mask layer connectivity
+ connect(l3 l3 l1)
+ connect(l1 l3 l1)
+
+ # Device class section
+ class(RES RES)
+
+ # Device abstracts section
+ # Device abstracts list the pin shapes of the devices.
+ device(D$RES RES
+ terminal(A
+ rect(l1 (2225 -675) (200 250))
+ )
+ terminal(B
+ rect(l1 (-2425 425) (200 250))
+ )
+ )
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(TOP
+
+ # Circuit boundary
+ rect((0 0) (10255 5900))
+
+ # Nets with their geometries
+ net(1
+ rect(l3 (4850 4600) (180 180))
+ rect(l1 (-245 -250) (310 320))
+ rect(l1 (0 -250) (200 250))
+ )
+ net(2
+ rect(l3 (10010 3500) (180 180))
+ rect(l1 (-245 -250) (310 320))
+ rect(l1 (-510 -250) (200 250))
+ )
+
+ # Devices and their connections
+ device(1 D$RES
+ location(7520 4175)
+ param(R 51)
+ param(L 25.5)
+ param(W 0.5)
+ param(A 3.1875)
+ param(P 26)
+ terminal(A 2)
+ terminal(B 1)
+ )
+
+ )
+)
+
+# Reference netlist
+reference(
+
+ # Device class section
+ class(RES RES)
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(TOP
+
+ # Nets
+ net(1 name(R1))
+ net(2 name(R2))
+
+ # Outgoing pins and their connections to nets
+ pin(1 name(R1))
+ pin(2 name(R2))
+
+ # Devices and their connections
+ device(1 RES
+ name('5')
+ param(R 50.1)
+ param(L 0)
+ param(W 0)
+ param(A 0)
+ param(P 0)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Cross reference
+xref(
+ circuit(TOP TOP match
+ xref(
+ net(1 1 warning)
+ net(2 2 warning)
+ pin(() 0 match)
+ pin(() 1 match)
+ device(1 1 match)
+ )
+ )
+)
diff --git a/testdata/lvs/custom_compare_sch.cir b/testdata/lvs/custom_compare_sch.cir
new file mode 100644
index 000000000..3424c602d
--- /dev/null
+++ b/testdata/lvs/custom_compare_sch.cir
@@ -0,0 +1,3 @@
+.subckt top r1 r2
+r5 r1 r2 50.1
+.ends
diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb
index b75ed63bc..6922163e3 100644
--- a/testdata/lvs/double_height2.lvsdb
+++ b/testdata/lvs/double_height2.lvsdb
@@ -5,7 +5,7 @@ J(
L(l3 '3/0')
L(l11 '3/1')
L(l6 '4/0')
- L(l7 '2/0')
+ L(l7)
L(l8 '6/0')
L(l12 '6/1')
L(l9 '7/0')
diff --git a/testdata/lvs/double_height2_texts.lvsdb b/testdata/lvs/double_height2_texts.lvsdb
index d62e4e90c..4aab34e12 100644
--- a/testdata/lvs/double_height2_texts.lvsdb
+++ b/testdata/lvs/double_height2_texts.lvsdb
@@ -5,7 +5,7 @@ J(
L(l3 '3/0')
L(l11 '3/1')
L(l6 '4/0')
- L(l7 '2/0')
+ L(l7)
L(l8 '6/0')
L(l12 '6/1')
L(l9 '7/0')
diff --git a/testdata/lvs/floating.lvsdb b/testdata/lvs/floating.lvsdb
index e57702118..29042b22a 100644
--- a/testdata/lvs/floating.lvsdb
+++ b/testdata/lvs/floating.lvsdb
@@ -13,8 +13,8 @@ layout(
layer(l4 '5/0')
layer(l8 '8/0')
layer(l11 '9/0')
- layer(l12 '5/0')
- layer(l13 '5/0')
+ layer(l12)
+ layer(l13)
layer(l7)
layer(l2)
layer(l9)
diff --git a/testdata/python/dbLayoutToNetlist.py b/testdata/python/dbLayoutToNetlist.py
index 05f0e62d0..669398afe 100644
--- a/testdata/python/dbLayoutToNetlist.py
+++ b/testdata/python/dbLayoutToNetlist.py
@@ -576,6 +576,54 @@ end;
self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "")
self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "")
+ # --- simple incremental antenna check with metal1 + metal2
+
+ l2n._destroy()
+ l2n = pya.LayoutToNetlist(dss)
+
+ l2n.register(rdiode, "diode")
+ l2n.register(rpoly, "poly")
+ l2n.register(rcont, "cont")
+ l2n.register(rmetal1, "metal1")
+ l2n.register(rvia1, "via1")
+ l2n.register(rmetal2, "metal2")
+
+ l2n.connect(rpoly)
+ l2n.connect(rcont)
+ l2n.connect(rmetal1)
+ l2n.connect(rmetal2)
+ l2n.connect(rpoly, rcont)
+ l2n.connect(rcont, rmetal1)
+
+ self.assertEqual(l2n.is_extracted(), False)
+ l2n.extract_netlist()
+ self.assertEqual(l2n.is_extracted(), True)
+
+ a1_3 = l2n.antenna_check(rpoly, rmetal1, 3)
+ a1_10 = l2n.antenna_check(rpoly, rmetal1, 10)
+ a1_30 = l2n.antenna_check(rpoly, rmetal1, 30)
+
+ # Note: flatten.merged performs some normalization
+ self.assertEqual(str(a1_3.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(100, 0)))), "")
+ self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "")
+ self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "")
+
+ l2n.connect(rmetal1, rvia1)
+ l2n.connect(rvia1, rmetal2)
+
+ self.assertEqual(l2n.is_extracted(), False)
+ l2n.extract_netlist()
+ self.assertEqual(l2n.is_extracted(), True)
+
+ a2_5 = l2n.antenna_check(rpoly, rmetal2, 5)
+ a2_10 = l2n.antenna_check(rpoly, rmetal2, 10)
+ a2_17 = l2n.antenna_check(rpoly, rmetal2, 17)
+
+ # Note: flatten.merged performs some normalization
+ self.assertEqual(str(a2_5.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(200, 0)))), "")
+ self.assertEqual(str(a2_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(201, 0)))), "")
+ self.assertEqual(str(a2_17.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(202, 0)))), "")
+
# --- simple antenna check with metal2
l2n._destroy()
diff --git a/testdata/python/dbReaders.py b/testdata/python/dbReaders.py
index 426b6f17f..d7f2d5c44 100644
--- a/testdata/python/dbReaders.py
+++ b/testdata/python/dbReaders.py
@@ -64,6 +64,11 @@ class DBReadersTests(unittest.TestCase):
opt.gds2_allow_multi_xy_records = False
self.assertEqual(opt.gds2_allow_multi_xy_records, False)
+ opt.gds2_resolve_skew_arrays = True
+ self.assertEqual(opt.gds2_resolve_skew_arrays, True)
+ opt.gds2_resolve_skew_arrays = False
+ self.assertEqual(opt.gds2_resolve_skew_arrays, False)
+
opt.gds2_allow_big_records = True
self.assertEqual(opt.gds2_allow_big_records, True)
opt.gds2_allow_big_records = False
diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb
index 7b179a732..d2beb1c9e 100644
--- a/testdata/ruby/dbLayoutToNetlist.rb
+++ b/testdata/ruby/dbLayoutToNetlist.rb
@@ -170,7 +170,8 @@ class DBLayoutToNetlist_TestClass < TestBase
# Perform netlist extraction
l2n.extract_netlist
- assert_equal(l2n.netlist.to_s, <