From a833dd57fefc8a545f5a903130b2638d33fdcb6e Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sun, 6 Dec 2020 18:25:57 +0100
Subject: [PATCH] Implemented rectangle and opposite filters for DRC functions.
---
scripts/create_drc_samples.rb | 110 ++++++++++++
src/db/db/dbEdgePairRelations.h | 6 +-
src/db/db/dbRegionLocalOperations.cc | 8 +-
src/db/db/dbRegionLocalOperations.h | 14 +-
src/db/db/gsiDeclDbRegion.cc | 8 +
src/drc/drc/built-in-macros/_drc_engine.rb | 32 ++++
src/drc/drc/built-in-macros/_drc_layer.rb | 190 ++++++++++++--------
src/drc/drc/built-in-macros/_drc_tags.rb | 18 ++
src/lay/lay/doc/about/drc_ref_global.xml | 2 +
src/lay/lay/doc/about/drc_ref_layer.xml | 86 ++++++++-
src/lay/lay/doc/images/drc_separation10.png | Bin 0 -> 6276 bytes
src/lay/lay/doc/images/drc_separation11.png | Bin 0 -> 6554 bytes
src/lay/lay/doc/images/drc_separation2.png | Bin 0 -> 6971 bytes
src/lay/lay/doc/images/drc_separation3.png | Bin 0 -> 7154 bytes
src/lay/lay/doc/images/drc_separation4.png | Bin 0 -> 7589 bytes
src/lay/lay/doc/images/drc_separation5.png | Bin 0 -> 5998 bytes
src/lay/lay/doc/images/drc_separation6.png | Bin 0 -> 6218 bytes
src/lay/lay/doc/images/drc_separation7.png | Bin 0 -> 6082 bytes
src/lay/lay/doc/images/drc_separation8.png | Bin 0 -> 6400 bytes
src/lay/lay/doc/images/drc_separation9.png | Bin 0 -> 6188 bytes
src/lay/lay/layDRCLVSHelpResources.qrc | 10 ++
21 files changed, 396 insertions(+), 88 deletions(-)
create mode 100644 src/lay/lay/doc/images/drc_separation10.png
create mode 100644 src/lay/lay/doc/images/drc_separation11.png
create mode 100644 src/lay/lay/doc/images/drc_separation2.png
create mode 100644 src/lay/lay/doc/images/drc_separation3.png
create mode 100644 src/lay/lay/doc/images/drc_separation4.png
create mode 100644 src/lay/lay/doc/images/drc_separation5.png
create mode 100644 src/lay/lay/doc/images/drc_separation6.png
create mode 100644 src/lay/lay/doc/images/drc_separation7.png
create mode 100644 src/lay/lay/doc/images/drc_separation8.png
create mode 100644 src/lay/lay/doc/images/drc_separation9.png
diff --git a/scripts/create_drc_samples.rb b/scripts/create_drc_samples.rb
index 196c6bf19..15de2e5ed 100644
--- a/scripts/create_drc_samples.rb
+++ b/scripts/create_drc_samples.rb
@@ -240,6 +240,116 @@ gen = Gen::new
run_demo gen, "input1.separation(input2, 1.2, euclidian)", "drc_separation1.png"
+class Gen
+ def produce(s1, s2)
+ pts = [
+ RBA::Point::new(1000, 0),
+ RBA::Point::new(1000, 6000),
+ RBA::Point::new(2000, 6000),
+ RBA::Point::new(2000, 0),
+ ];
+ s2.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(5000, 2000),
+ RBA::Point::new(5000, 4000),
+ RBA::Point::new(6000, 4000),
+ RBA::Point::new(6000, 2000),
+ ];
+ s2.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(3000, 0),
+ RBA::Point::new(3000, 6000),
+ RBA::Point::new(4000, 6000),
+ RBA::Point::new(4000, 0)
+ ];
+ s1.insert(RBA::Polygon::new(pts))
+ end
+end
+
+gen = Gen::new
+
+run_demo gen, "input1.sep(input2, 1.2, projection)", "drc_separation2.png"
+run_demo gen, "input1.sep(input2, 1.2, projection, not_opposite)", "drc_separation3.png"
+run_demo gen, "input1.sep(input2, 1.2, projection, only_opposite)", "drc_separation4.png"
+
+class Gen
+ def produce(s1, s2)
+ pts = [
+ RBA::Point::new(0, 3000),
+ RBA::Point::new(0, 4000),
+ RBA::Point::new(1000, 4000),
+ RBA::Point::new(1000, 3000)
+ ]
+ s2.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(3000, 3000),
+ RBA::Point::new(3000, 4000),
+ RBA::Point::new(4000, 4000),
+ RBA::Point::new(4000, 3000)
+ ]
+ s2.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(6000, 3000),
+ RBA::Point::new(6000, 4000),
+ RBA::Point::new(7000, 4000),
+ RBA::Point::new(7000, 3000)
+ ]
+ s2.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(4500, 1500),
+ RBA::Point::new(4500, 2500),
+ RBA::Point::new(5500, 2500),
+ RBA::Point::new(5500, 1500)
+ ]
+ s2.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(1500, 3000),
+ RBA::Point::new(1500, 4000),
+ RBA::Point::new(2500, 4000),
+ RBA::Point::new(2500, 3000)
+ ]
+ s1.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(3000, 4500),
+ RBA::Point::new(3000, 5500),
+ RBA::Point::new(4000, 5500),
+ RBA::Point::new(4000, 4500)
+ ]
+ s1.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(3000, 1500),
+ RBA::Point::new(3000, 2500),
+ RBA::Point::new(4000, 2500),
+ RBA::Point::new(4000, 1500)
+ ]
+ s1.insert(RBA::Polygon::new(pts))
+ pts = [
+ RBA::Point::new(4500, 3000),
+ RBA::Point::new(4500, 4000),
+ RBA::Point::new(5500, 4000),
+ RBA::Point::new(5500, 3000)
+ ]
+ s1.insert(RBA::Polygon::new(pts))
+ end
+end
+
+gen = Gen::new
+
+run_demo gen, "input1.sep(input2, 1.0, projection)", "drc_separation5.png"
+run_demo gen, "input1.sep(input2, 1.0, projection,\n" +
+ " one_side_allowed)", "drc_separation6.png"
+run_demo gen, "input1.sep(input2, 1.0, projection,\n" +
+ " two_sides_allowed)", "drc_separation7.png"
+run_demo gen, "input1.sep(input2, 1.0, projection,\n" +
+ " two_opposite_sides_allowed)", "drc_separation8.png"
+run_demo gen, "input1.sep(input2, 1.0, projection,\n" +
+ " two_connected_sides_allowed)", "drc_separation9.png"
+run_demo gen, "input1.sep(input2, 1.0, projection,\n" +
+ " three_sides_allowed)", "drc_separation10.png"
+run_demo gen, "input1.sep(input2, 1.0, projection,\n" +
+ " one_side_allowed,\n" +
+ " two_opposite_sides_allowed)", "drc_separation11.png"
+
# ...
class Gen
diff --git a/src/db/db/dbEdgePairRelations.h b/src/db/db/dbEdgePairRelations.h
index a2d26b461..b7e0dece3 100644
--- a/src/db/db/dbEdgePairRelations.h
+++ b/src/db/db/dbEdgePairRelations.h
@@ -257,9 +257,9 @@ private:
// Internal methods exposed for testing purposes
-DB_PUBLIC bool projected_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &g, db::Edge *output);
-DB_PUBLIC bool square_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &g, db::Edge *output);
-DB_PUBLIC bool euclidian_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &g, db::Edge *output);
+DB_PUBLIC bool projected_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output);
+DB_PUBLIC bool square_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output);
+DB_PUBLIC bool euclidian_near_part_of_edge (bool include_zero, db::coord_traits::distance_type d, const db::Edge &e, const db::Edge &g, db::Edge *output);
DB_PUBLIC db::Edge::distance_type edge_projection (const db::Edge &a, const db::Edge &b);
}
diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc
index 584ef8da1..e2998637e 100644
--- a/src/db/db/dbRegionLocalOperations.cc
+++ b/src/db/db/dbRegionLocalOperations.cc
@@ -352,9 +352,11 @@ check_local_operation::compute_local (db::Layout *layout, const shape_in
uint32_t p4 = p32 & 0xf;
p32 >>= 4;
- for (unsigned int r = 0; r < 4 && ! can_be_waived; ++r) {
- can_be_waived = (error_pattern == p4);
- p4 = ((p4 << 1) & 0xf) | ((p4 & 0x8) >> 3);
+ if (p4 > 0) {
+ for (unsigned int r = 0; r < 4 && ! can_be_waived; ++r) {
+ can_be_waived = (error_pattern == p4);
+ p4 = ((p4 << 1) & 0xf) | ((p4 & 0x8) >> 3);
+ }
}
}
diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h
index 95cefa485..6fc454f3f 100644
--- a/src/db/db/dbRegionLocalOperations.h
+++ b/src/db/db/dbRegionLocalOperations.h
@@ -39,7 +39,9 @@ namespace db
* of groups of 4 bits each specifying an allowed pattern. Rotation is implicit, so it's just
* required to give on incarnation.
*
- * For example: 0x1953 would be one- or two-sided.
+ * For example: 0x153 would be one- or two-sided.
+ *
+ * The bitmaps are choosen such that they can be or-combined.
*/
enum RectFilter
{
@@ -56,27 +58,27 @@ enum RectFilter
/**
* @brief Allow errors on two sides (not specified which)
*/
- TwoSidesAllowed = 0x953,
+ TwoSidesAllowed = 0x530,
/**
* @brief Allow errors on two sides ("L" configuration)
*/
- TwoConnectedSidesAllowed = 0x3,
+ TwoConnectedSidesAllowed = 0x30,
/**
* @brief Allow errors on two opposite sides
*/
- TwoOppositeSidesAllowed = 0x5,
+ TwoOppositeSidesAllowed = 0x500,
/**
* @brief Allow errors on three sides
*/
- ThreeSidesAllowed = 0x7,
+ ThreeSidesAllowed = 0x7000,
/**
* @brief Allow errors when on all sides
*/
- FourSidesAllowed = 0xf
+ FourSidesAllowed = 0xf0000
};
/**
diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc
index 68e9ad177..07eb2fd27 100644
--- a/src/db/db/gsiDeclDbRegion.cc
+++ b/src/db/db/gsiDeclDbRegion.cc
@@ -2510,6 +2510,9 @@ gsi::EnumIn decl_Region_Metrics ("db", "Metrics",
"This enum has been introduced in version 0.27."
);
+// Inject the Region::Metrics declarations into Region:
+gsi::ClassExt inject_Metrics_in_parent (decl_Region_Metrics.defs ());
+
gsi::EnumIn decl_Region_RectFilter ("db", "RectFilter",
gsi::enum_const ("NoRectFilter", db::RectFilter::NoSideAllowed,
"@brief Specifies no filtering"
@@ -2537,6 +2540,8 @@ gsi::EnumIn decl_Region_RectFilter ("db", "RectFilte
"This enum has been introduced in version 0.27."
);
+// Inject the Region::RectFilter declarations into Region:
+gsi::ClassExt inject_RectFilter_in_parent (decl_Region_RectFilter.defs ());
gsi::EnumIn decl_Region_OppositeFilter ("db", "OppositeFilter",
gsi::enum_const ("NoOppositeFilter", db::OppositeFilter::NoOppositeFilter,
@@ -2553,5 +2558,8 @@ gsi::EnumIn decl_Region_OppositeFilter ("db", "O
"This enum has been introduced in version 0.27."
);
+// Inject the Region::OppositeFilter declarations into Region:
+gsi::ClassExt inject_OppositeFilter_in_parent (decl_Region_OppositeFilter.defs ());
+
}
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index bcacad3f0..58f39e071 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -115,6 +115,38 @@ module DRC
DRCMetrics::new(RBA::Region::Projection)
end
+ def not_opposite
+ DRCOppositeErrorFilter::new(RBA::Region::NotOpposite)
+ end
+
+ def only_opposite
+ DRCOppositeErrorFilter::new(RBA::Region::OnlyOpposite)
+ end
+
+ def one_side_allowed
+ DRCRectangleErrorFilter::new(RBA::Region::OneSideAllowed)
+ end
+
+ def two_sides_allowed
+ DRCRectangleErrorFilter::new(RBA::Region::TwoSidesAllowed)
+ end
+
+ def two_connected_sides_allowed
+ DRCRectangleErrorFilter::new(RBA::Region::TwoConnectedSidesAllowed)
+ end
+
+ def two_opposite_sides_allowed
+ DRCRectangleErrorFilter::new(RBA::Region::TwoOppositeSidesAllowed)
+ end
+
+ def three_sides_allowed
+ DRCRectangleErrorFilter::new(RBA::Region::ThreeSidesAllowed)
+ end
+
+ def four_sides_allowed
+ DRCRectangleErrorFilter::new(RBA::Region::FourSidesAllowed)
+ end
+
def pattern(p)
DRCPattern::new(true, p)
end
diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb
index 4fd5e452a..b54ef5689 100644
--- a/src/drc/drc/built-in-macros/_drc_layer.rb
+++ b/src/drc/drc/built-in-macros/_drc_layer.rb
@@ -2609,8 +2609,8 @@ CODE
# method will only report space violations to other polygons. \separation is a two-layer
# space check where space is checked against polygons of another layer.
#
- # The options available are the same than for the \width method. Like for the \width
- # method, merged semantics applies.
+ # Like for the \width method, merged semantics applies.
+ #
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@@ -2621,17 +2621,21 @@ CODE
# @td @img(/images/drc_space1.png) @/td
# @/tr
# @/table
+ #
# %DRC%
# @name isolated
# @brief An isolation check
# @synopsis layer.isolated(value [, options])
+ # @synopsis layer.iso(value [, options])
#
# See \space for a description of this method.
# In contrast to \space, this
# method is available for polygon layers only, since only on such layers
# different polygons can be identified.
#
+ # "iso" is the short form of this method.
+ #
# The following image shows the effect of the isolated check:
#
# @table
@@ -2648,7 +2652,8 @@ CODE
# See \space for a description of this method.
# In contrast to \space, this
# method is available for polygon layers only, since only on such layers
- # different polygons can be identified.
+ # different polygons can be identified. Also, opposite and rectangle error
+ # filtering is not available for this method.
#
# The following image shows the effect of the notch check:
#
@@ -2662,12 +2667,15 @@ CODE
# @name separation
# @brief A two-layer spacing check
# @synopsis layer.separation(other_layer, value [, options])
+ # @synopsis layer.sep(other_layer, value [, options])
#
# This method performs a two-layer spacing check. Like \space, this method
# can be applied to edge or polygon layers. Locations where edges of the layer
# are closer than the specified distance to the other layer are reported
# as edge pair error markers.
- #
+ #
+ # "sep" is the short form of this method.
+ #
# In contrast to the \space and related methods, locations where both
# layers touch are also reported. More specifically, the case of zero spacing
# will also trigger an error while for \space it will not.
@@ -2684,6 +2692,72 @@ CODE
# @td @img(/images/drc_separation1.png) @/td
# @/tr
# @/table
+ #
+ # The options for the separation check are those available for the \width or \space
+ # method plus opposite and rectangle error filtering.
+ #
+ # Opposite error filtering will waive errors that are on opposite sides of the original
+ # figure. The inverse is selection of errors only when there is an error present on
+ # the opposite side of the original figure. Opposite error waiving or selection is achieved
+ # through these options inside the DRC function call:
+ #
+ # @ul
+ # @li @b not_opposite @/b will waive opposite errors @/li
+ # @li @b only_opposite @/b will select errors only if there is an opposite one @/li
+ # @/ul
+ #
+ # These modes imply partial waiving or selection if "opposite" only applies to a section
+ # of an error.
+ #
+ # The following images shows the effect of these options:
+ #
+ # @table
+ # @tr
+ # @td @img(/images/drc_separation2.png) @/td
+ # @/tr
+ # @tr
+ # @td @img(/images/drc_separation3.png) @/td
+ # @td @img(/images/drc_separation4.png) @/td
+ # @/tr
+ # @/table
+ #
+ # Rectangle error filtering allows waiving errors based on how they cover the
+ # sides of an original rectangular figure. This selection only applies to errors
+ # covering the full edge of the rectangle. Errors covering parts of the rectangle
+ # edges are not considered in this scheme.
+ #
+ # The rectangle filter option is enabled by these modes:
+ #
+ # @ul
+ # @li @b one_side_allowed @/b will waive errors when they appear on one side of the rectangle only @/li
+ # @li @b two_sides_allowed @/b will waive errors when they appear on two sides of the rectangle @/li
+ # @li @b two_connected_sides_allowed @/b will waive errors when they appear on two connected sides of the rectangle ("L" configuration) @/li
+ # @li @b two_opposite_sides_allowed @/b will waive errors when they appear on two opposite sides of the rectangle @/li
+ # @li @b three_sides_allowed @/b will waive errors when they appear on three sides of the rectangle @/li
+ # @li @b four_sides_allowed @/b will waive errors when they appear on four sides of the rectangle @/li
+ # @/ul
+ #
+ # Multiple of these options can be given, which will make errors waived if one of these conditions is met.
+ #
+ # The following images shows the effect of some rectangle filter modes:
+ #
+ # @table
+ # @tr
+ # @td @img(/images/drc_separation5.png) @/td
+ # @/tr
+ # @tr
+ # @td @img(/images/drc_separation6.png) @/td
+ # @td @img(/images/drc_separation7.png) @/td
+ # @/tr
+ # @tr
+ # @td @img(/images/drc_separation8.png) @/td
+ # @td @img(/images/drc_separation9.png) @/td
+ # @/tr
+ # @tr
+ # @td @img(/images/drc_separation10.png) @/td
+ # @td @img(/images/drc_separation11.png) @/td
+ # @/tr
+ # @/table
# %DRC%
# @name overlap
@@ -2697,6 +2771,8 @@ CODE
# such locations form an overlap with a value of 0. Locations, where both regions
# do not overlap or touch will not be reported. Such regions can be detected
# with \outside or by a boolean "not".
+ #
+ # The options are the same as for \separation.
#
# Formally, the overlap method is a two-layer width check. In contrast to the single-
# layer width method (\width), the zero value also triggers an error and separate
@@ -2725,6 +2801,7 @@ CODE
# @name enclosing
# @brief An enclosing check
# @synopsis layer.enclosing(other_layer, value [, options])
+ # @synopsis layer.enc(other_layer, value [, options])
#
# This method checks whether layer encloses (is bigger than) other_layer by the
# given dimension. Locations, where this is not the case will be reported in form
@@ -2734,6 +2811,10 @@ CODE
# extends outside layer will not be reported as errors. Such regions can be detected
# by \not_inside or a boolean "not" operation.
#
+ # "enc" is the short form of this method.
+ #
+ # The options are the same as for \separation.
+ #
# The enclosing method can be applied to both edge or polygon layers. On edge layers
# the orientation of the edges matters and only edges looking into the same direction
# are checked.
@@ -2752,20 +2833,26 @@ CODE
# @/tr
# @/table
- %w(width space overlap enclosing separation).each do |f|
+ %w(width space overlap enclosing separation isolated notch).each do |f|
eval <<"CODE"
def #{f}(*args)
- requires_edges_or_region("#{f}")
+ if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosing || :#{f} == :separation
+ requires_edges_or_region("#{f}")
+ else
+ requires_region("#{f}")
+ end
value = nil
- metrics = nil
+ metrics = RBA::Region::Euclidian
minp = nil
maxp = nil
alim = nil
whole_edges = false
other = nil
shielded = nil
+ opposite_filter = RBA::Region::NoOppositeFilter
+ rect_filter = RBA::Region::NoRectFilter
n = 1
args.each do |a|
@@ -2773,6 +2860,10 @@ CODE
metrics = a.value
elsif a.is_a?(DRCWholeEdges)
whole_edges = a.value
+ elsif a.is_a?(DRCOppositeErrorFilter)
+ opposite_filter = a.value
+ elsif a.is_a?(DRCRectangleErrorFilter)
+ rect_filter = RBA::Region::RectFilter::new(a.value.to_i | rect_filter.to_i)
elsif a.is_a?(DRCAngleLimit)
alim = a.value
elsif a.is_a?(DRCLayer)
@@ -2796,24 +2887,35 @@ CODE
end
args = [ value, whole_edges, metrics, alim, minp, maxp ]
- if shielded != nil
- if self.data.is_a?(RBA::Region)
- args << shielded
- else
- raise("#{f}: shielding can only be used for polygon layers")
+
+ if self.data.is_a?(RBA::Region)
+ args << shielded
+ if :#{f} != :width && :#{f} != :notch
+ args << opposite_filter
+ args << rect_filter
+ elsif opposite_filter != RBA::Region::NoOppositeFilter
+ raise("#{f}: an opposite error filter cannot be used with this check")
+ elsif rect_filter != RBA::Region::NoRectFilter
+ raise("#{f}: a rectangle error filter cannot be used with this check")
end
+ elsif shielded != nil
+ raise("#{f}: shielding can only be used for polygon layers")
+ elsif opposite_filter != RBA::Region::NoOppositeFilter
+ raise("#{f}: an opposite error filter can only be used for polygon layers")
+ elsif rect_filter != RBA::Region::NoRectFilter
+ raise("#{f}: a rectangle error filter can only be used for polygon layers")
end
border = (metrics == RBA::Region::Square ? value * 1.5 : value)
- if "#{f}" == "width" || "#{f}" == "space" || "#{f}" == "notch" || "#{f}" == "isolated"
+ if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated
if other
- raise("No other layer must be specified for single-layer checks (i.e. width)")
+ raise("No other layer must be specified for a single-layer check (here: #{f})")
end
DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, *args))
else
if !other
- raise("The other layer must be specified for two-layer checks (i.e. overlap)")
+ raise("The other layer must be specified for a two-layer check (here: #{f})")
end
requires_same_type(other, "#{f}")
DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, other.data, *args))
@@ -2823,64 +2925,6 @@ CODE
CODE
end
- %w(isolated notch).each do |f|
- eval <<"CODE"
- def #{f}(*args)
-
- requires_region("#{f}")
-
- value = nil
- metrics = nil
- minp = nil
- maxp = nil
- alim = nil
- whole_edges = false
- other = nil
-
- n = 1
- args.each do |a|
- if a.is_a?(DRCMetrics)
- metrics = a.value
- elsif a.is_a?(DRCWholeEdges)
- whole_edges = a.value
- elsif a.is_a?(DRCAngleLimit)
- alim = a.value
- elsif a.is_a?(DRCLayer)
- other = a
- elsif a.is_a?(DRCProjectionLimits)
- minp = @engine._prep_value(a.min)
- maxp = @engine._prep_value(a.max)
- elsif a.is_a?(Float) || a.is_a?(1.class)
- value && raise("Value already specified")
- value = @engine._prep_value(a)
- else
- raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
- end
- n += 1
- end
-
- if !value
- raise("A check value must be specified")
- end
-
- border = (metrics == RBA::Region::Square ? value * 1.5 : value)
-
- if "#{f}" == "width" || "#{f}" == "space" || "#{f}" == "notch" || "#{f}" == "isolated"
- if other
- raise("#{f}: No other layer must be specified for single-layer checks (i.e. width)")
- end
- DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, value, whole_edges, metrics, alim, minp, maxp))
- else
- if !other
- raise("#{f}: The other layer must be specified for two-layer checks (i.e. overlap)")
- end
- DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, other.data, value, whole_edges, metrics, alim, minp, maxp))
- end
-
- end
-CODE
- end
-
# %DRC%
# @name scaled
# @brief Scales a layer
diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb
index 30f460f2f..eaf58624b 100644
--- a/src/drc/drc/built-in-macros/_drc_tags.rb
+++ b/src/drc/drc/built-in-macros/_drc_tags.rb
@@ -72,6 +72,24 @@ module DRC
end
end
+ # A wrapper for a rectangle error filter mode
+ # The purpose of this wrapper is to identify the error filter mode
+ class DRCRectangleErrorFilter
+ attr_accessor :value
+ def initialize(v)
+ self.value = v
+ end
+ end
+
+ # A wrapper for a opposite error filter mode
+ # The purpose of this wrapper is to identify the error filter mode
+ class DRCOppositeErrorFilter
+ attr_accessor :value
+ def initialize(v)
+ self.value = v
+ end
+ end
+
# A wrapper for a glob-pattern style text selection for
# some DRC functions. The purpose of this class
# is to identify the value by the class.
diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml
index 6f80c8b5a..38cc38445 100644
--- a/src/lay/lay/doc/about/drc_ref_global.xml
+++ b/src/lay/lay/doc/about/drc_ref_global.xml
@@ -422,6 +422,7 @@ Disables tiling mode. Tiling mode can be enabled again with til
Usage:
- info(message)
+- info(message, indent)
Prints the message to the log window in verbose mode.
@@ -520,6 +521,7 @@ For further methods on the source object see Source.
Usage:
- log(message)
+- log(message, indent)
Prints the message to the log window.
diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml
index 192e50857..08d96c467 100644
--- a/src/lay/lay/doc/about/drc_ref_layer.xml
+++ b/src/lay/lay/doc/about/drc_ref_layer.xml
@@ -357,6 +357,7 @@ See enclosing for a description of that method
Usage:
- layer.enclosing(other_layer, value [, options])
+- layer.enc(other_layer, value [, options])
This method checks whether layer encloses (is bigger than) other_layer by the
@@ -367,6 +368,10 @@ such locations form an enclosure with a distance of 0. Locations, where other_la
extends outside layer will not be reported as errors. Such regions can be detected
by not_inside or a boolean "not" operation.
+"enc" is the short form of this method.
+
+The options are the same as for separation.
+
The enclosing method can be applied to both edge or polygon layers. On edge layers
the orientation of the edges matters and only edges looking into the same direction
are checked.
@@ -915,6 +920,7 @@ See isolated for a description of that method
Usage:
- layer.isolated(value [, options])
+- layer.iso(value [, options])
See space for a description of this method.
@@ -922,6 +928,8 @@ In contrast to space, this
method is available for polygon layers only, since only on such layers
different polygons can be identified.
+"iso" is the short form of this method.
+
The following image shows the effect of the isolated check:
@@ -1281,7 +1289,8 @@ is select_not_overlapping.
See space for a description of this method.
In contrast to space, this
method is available for polygon layers only, since only on such layers
-different polygons can be identified.
+different polygons can be identified. Also, opposite and rectangle error
+filtering is not available for this method.
The following image shows the effect of the notch check:
@@ -1417,6 +1426,8 @@ such locations form an overlap with a value of 0. Locations, where both regions
do not overlap or touch will not be reported. Such regions can be detected
with outside or by a boolean "not".
+The options are the same as for separation.
+
Formally, the overlap method is a two-layer width check. In contrast to the single-
layer width method (width), the zero value also triggers an error and separate
polygons are checked against each other, while for the single-layer width, only
@@ -1880,6 +1891,7 @@ See separation for a description of that method
Usage:
- layer.separation(other_layer, value [, options])
+- layer.sep(other_layer, value [, options])
This method performs a two-layer spacing check. Like space, this method
@@ -1887,6 +1899,8 @@ can be applied to edge or polygon layers. Locations where edges of the layer
are closer than the specified distance to the other layer are reported
as edge pair error markers.
+"sep" is the short form of this method.
+
In contrast to the space and related methods, locations where both
layers touch are also reported. More specifically, the case of zero spacing
will also trigger an error while for space it will not.
@@ -1903,6 +1917,72 @@ The following image shows the effect of the separation check (input1: red, input
 |
+
+The options for the separation check are those available for the width or space
+method plus opposite and rectangle error filtering.
+
+Opposite error filtering will waive errors that are on opposite sides of the original
+figure. The inverse is selection of errors only when there is an error present on
+the opposite side of the original figure. Opposite error waiving or selection is achieved
+through these options inside the DRC function call:
+
+
+- not_opposite will waive opposite errors
+- only_opposite will select errors only if there is an opposite one
+
+
+These modes imply partial waiving or selection if "opposite" only applies to a section
+of an error.
+
+The following images shows the effect of these options:
+
+
+
+Rectangle error filtering allows waiving errors based on how they cover the
+sides of an original rectangular figure. This selection only applies to errors
+covering the full edge of the rectangle. Errors covering parts of the rectangle
+edges are not considered in this scheme.
+
+The rectangle filter option is enabled by these modes:
+
+
+- one_side_allowed will waive errors when they appear on one side of the rectangle only
+- two_sides_allowed will waive errors when they appear on two sides of the rectangle
+- two_connected_sides_allowed will waive errors when they appear on two connected sides of the rectangle ("L" configuration)
+- two_opposite_sides_allowed will waive errors when they appear on two opposite sides of the rectangle
+- three_sides_allowed will waive errors when they appear on three sides of the rectangle
+- four_sides_allowed will waive errors when they appear on four sides of the rectangle
+
+
+Multiple of these options can be given, which will make errors waived if one of these conditions is met.
+
+The following images shows the effect of some rectangle filter modes:
+
+
+
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
"size" - Polygon sizing (per-edge biasing, modifies the layer)
@@ -2037,8 +2117,8 @@ The notch method is similar, but will only report self-spac
method will only report space violations to other polygons. separation is a two-layer
space check where space is checked against polygons of another layer.
-The options available are the same than for the width method. Like for the width
-method, merged semantics applies.
+Like for the width method, merged semantics applies.
+
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
diff --git a/src/lay/lay/doc/images/drc_separation10.png b/src/lay/lay/doc/images/drc_separation10.png
new file mode 100644
index 0000000000000000000000000000000000000000..041e66910282d562a5bbdcd144e7fd8fb1c164dc
GIT binary patch
literal 6276
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
W&$rxp(~+$PZ}(2G9ThIee)u2a`O;MY
literal 0
HcmV?d00001
diff --git a/src/lay/lay/doc/images/drc_separation11.png b/src/lay/lay/doc/images/drc_separation11.png
new file mode 100644
index 0000000000000000000000000000000000000000..bfa3d688f99d3aba09fed4db84d80b7163afff7b
GIT binary patch
literal 6554
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