diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 8cb066c0b..bac80ca01 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -93,7 +93,7 @@ module DRC def transparent DRCShielded::new(false) end - + def projection_limits(*args) self._context("projection_limits") do if args.size == 0 @@ -211,6 +211,23 @@ module DRC end end + def tile_size(x, y = nil) + DRCTileSize::new(_make_numeric_value(x), _make_numeric_value(y || x)) + end + + def tile_step(x, y = nil) + DRCTileStep::new(_make_numeric_value(x), _make_numeric_value(y || x)) + end + + def tile_origin(x, y) + DRCTileOrigin::new(_make_numeric_value(x), _make_numeric_value(y)) + end + + def tile_boundary(b) + b.is_a?(DRCLayer) || raise("'tile_boundary' requires a layer argument") + DRCTileBoundary::new(b) + end + # %DRC% # @brief Defines SPICE output format (with options) # @name write_spice @@ -1857,7 +1874,9 @@ CODE log(desc) # enable progress + disable_progress = false if obj.is_a?(RBA::Region) || obj.is_a?(RBA::Edges) || obj.is_a?(RBA::EdgePairs) || obj.is_a?(RBA::Texts) + disable_progress = true obj.enable_progress(desc) end @@ -1867,32 +1886,38 @@ CODE res = yield t.stop - if @verbose + begin + + if @verbose + + # Report result statistics + if res.is_a?(RBA::Region) + info("Polygons (raw): #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) + elsif res.is_a?(RBA::Edges) + info("Edges: #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) + elsif res.is_a?(RBA::EdgePairs) + info("Edge pairs: #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) + elsif res.is_a?(RBA::Texts) + info("Texts: #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) + end + + mem = RBA::Timer::memory_size + if mem > 0 + info("Elapsed: #{'%.3f'%(t.sys+t.user)}s Memory: #{'%.2f'%(mem/(1024*1024))}M", 1) + else + info("Elapsed: #{'%.3f'%(t.sys+t.user)}s", 1) + end - # Report result statistics - if res.is_a?(RBA::Region) - info("Polygons (raw): #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) - elsif res.is_a?(RBA::Edges) - info("Edges: #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) - elsif res.is_a?(RBA::EdgePairs) - info("Edge pairs: #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) - elsif res.is_a?(RBA::Texts) - info("Texts: #{res.count} (flat) #{res.hier_count} (hierarchical)", 1) end - mem = RBA::Timer::memory_size - if mem > 0 - info("Elapsed: #{'%.3f'%(t.sys+t.user)}s Memory: #{'%.2f'%(mem/(1024*1024))}M", 1) - else - info("Elapsed: #{'%.3f'%(t.sys+t.user)}s", 1) + ensure + + # disable progress again + if disable_progress + obj.disable_progress end end - - # disable progress - if obj.is_a?(RBA::Region) || obj.is_a?(RBA::Edges) || obj.is_a?(RBA::EdgePairs) || obj.is_a?(RBA::Texts) - obj.disable_progress - end res @@ -1946,11 +1971,6 @@ CODE end - # disable progress again - if obj.is_a?(RBA::Region) - obj.disable_progress - end - res end @@ -2001,11 +2021,6 @@ CODE end - # disable progress again - if obj.is_a?(RBA::Region) - obj.disable_progress - end - res end @@ -2044,11 +2059,6 @@ CODE end - # disable progress again - if obj.is_a?(RBA::Region) - obj.disable_progress - end - res end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index b5fa7a3df..4300a8b72 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -3496,6 +3496,161 @@ CODE end CODE end + + # %DRC% + # @name with_density + # @brief Returns tiles whose density is within a given range + # @synopsis layer.with_density(min_value, max_value, [options]) + # @synopsis layer.with_density(min_value .. max_value, [options]) + # + # Runs a tiled analysis over the current layout. Reports the tiles whose density + # is between "min_value" and "max_value". "min_value" and "max_value" are given in + # relative units, i.e. within the range of 0 to 1.0 corresponding to a density of 0 to 100%. + # + # "min_value" or "max_value" can be nil or omitted in the ".." range notation. + # In this case, they are taken as "0" or "100%". + # + # The tile size can be specified with the "tile_size" option: + # + # @code + # # reports areas where layer 1/0 density is below 10% on 20x20 um tiles + # low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um)) + # @/code + # + # Anisotropic tiles can be specified by giving two values, like "tile_size(10.um, 20.um)". + # The first value is the horizontal tile dimension, the second value is the vertical tile + # dimension. + # + # A tile overlap can be specified using "tile_step". If the tile step is less than the + # tile size, the tiles will overlap. The layout window given by "tile_size" is moved + # in increments of the tile step: + # + # @code + # # reports areas where layer 1/0 density is below 10% on 30x30 um tiles + # # with a tile step of 20x20 um: + # low_density = input(1, 0).density(0.0 .. 0.1, tile_size(30.um), tile_step(20.um)) + # @/code + # + # For "tile_step", anisotropic values can be given by using two values: the first for the + # horizontal and the second for the vertical tile step. + # + # Another option is "tile_origin" which specifies the location of the first tile's position. + # This is the first tile's lower left corner. If no origin is given, the tiles are centered over the + # area to cover. + # + # By default, the tiles will cover the bounding box of the input layer. A separate layer + # can be used instead. This way, the layout's dimensions can be derived from some + # drawn boundary layer. To specify a separate boundary layer, use the "tile_boundary" option: + # + # @code + # # reports density of layer 1/0 below 10% on 20x20 um tiles. The layout's boundary is taken from + # # layer 0/0: + # cell_frame = input(0, 0) + # low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_boundary(cell_frame)) + # @/code + # + # The complementary version of "with_density" is "without_density". + # + + # @name without_density + # @brief Returns tiles whose density is not within a given range + # @synopsis layer.without_density(min_value, max_value, [options]) + # @synopsis layer.without_density(min_value .. max_value, [options]) + # + # 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. + + def _with_density(method, inverse, *args) + + requires_region + + limits = [ nil, nil ] + nlimits = 0 + tile_size = nil + tile_step = nil + tile_origin = nil + tile_boundary = nil + + n = 1 + args.each do |a| + if a.is_a?(DRCTileSize) + tile_size = a.get + elsif a.is_a?(DRCTileStep) + tile_step = a.get + elsif a.is_a?(DRCTileOrigin) + tile_origin = a.get + elsif a.is_a?(DRCTileBoundary) + tile_boundary = a.get + elsif a.is_a?(Float) || a.is_a?(1.class) || a == nil + nlimits < 2 || raise("Too many values specified") + limits[nlimits] = @engine._make_numeric_value_with_nil(a) + nlimits += 1 + elsif a.is_a?(Range) + nlimits == 0 || raise("Either a range or two limits have to be specified, not both") + limits = [ @engine._make_numeric_value_with_nil(a.begin), @engine._make_numeric_value_with_nil(a.end) ] + nlimits = 2 + else + raise("Parameter #" + n.to_s + " does not have an expected type") + end + n += 1 + end + + tile_size || raise("At least the tile_size option needs to be present") + tile_step ||= tile_size + + tp = RBA::TilingProcessor::new + tp.dbu = @engine.dbu + tp.scale_to_dbu = false + tp.tile_size(*tile_step) + if tile_size != tile_step + xb = 0.5 * (tile_size[0] - tile_step[0]) + yb = 0.5 * (tile_size[1] - tile_step[1]) + tp.tile_border(xb, yb) + tp.var("xoverlap", xb / tp.dbu) + tp.var("yoverlap", yb / tp.dbu) + else + tp.var("xoverlap", 0) + tp.var("yoverlap", 0) + end + if tile_origin + tp.tile_origin(*tile_origin) + end + + res = RBA::Region.new + tp.output("res", res) + tp.input("input", self.data) + tp.threads = (@engine.threads || 1) + if tile_boundary + tp.input("boundary", tile_boundary.data) + end + + tp.var("vmin", limits[0] || 0.0) + tp.var("vmax", limits[1] || 1.0) + tp.var("inverse", inverse) + tp.queue(<<"TP_SCRIPT") + _tile && ( + var bx = _tile.bbox.enlarged(xoverlap, yoverlap); + var d = to_f(input.area(bx)) / to_f(bx.area); + ((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false) + ) +TP_SCRIPT + + @engine.run_timed("\"#{method}\" in: #{@engine.src_line}", self.data) do + tp.execute("Tiled \"#{method}\" in: #{@engine.src_line}") + end + + DRCLayer::new(@engine, res) + + end + + def with_density(*args) + self._with_density("with_density", false, *args) + end + + def without_density(*args) + self._with_density("without_density", true, *args) + end + # %DRC% # @name scaled diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index fb9c051fc..f87af0b6a 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -180,6 +180,46 @@ module DRC @value end end + + # A wrapper for the tile_size option + class DRCTileSize + def initialize(*args) + @xy = args + end + def get + @xy + end + end + + # A wrapper for the tile_step option + class DRCTileStep + def initialize(*args) + @xy = args + end + def get + @xy + end + end + + # A wrapper for the tile_origin option + class DRCTileOrigin + def initialize(*args) + @xy = args + end + def get + @xy + end + end + + # A wrapper for the tile_boundary option + class DRCTileBoundary + def initialize(layer) + @b = layer + end + def get + @b + end + end end diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index b3215743f..b1508b821 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1163,3 +1163,8 @@ TEST(29d_holes) run_test (_this, "29", true); } +TEST(30_density) +{ + run_test (_this, "30", false); +} + diff --git a/testdata/drc/drcSimpleTests_30.drc b/testdata/drc/drcSimpleTests_30.drc new file mode 100644 index 000000000..ca278452d --- /dev/null +++ b/testdata/drc/drcSimpleTests_30.drc @@ -0,0 +1,17 @@ + +source $drc_test_source +target $drc_test_target + +b = input(0, 0) +a = input(1, 0) + +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.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..0.1, tile_size(100.um), tile_step(10.um)).output(110, 0) + diff --git a/testdata/drc/drcSimpleTests_30.gds b/testdata/drc/drcSimpleTests_30.gds new file mode 100644 index 000000000..14b2f86b8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_30.gds differ diff --git a/testdata/drc/drcSimpleTests_au30.gds b/testdata/drc/drcSimpleTests_au30.gds new file mode 100644 index 000000000..14b2f86b8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au30.gds differ