Implemented density check: 'with_density', 'without_density'

This commit is contained in:
Matthias Koefferlein 2021-02-28 18:21:05 +01:00
parent 77356cd877
commit cfc86ad62f
7 changed files with 263 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1163,3 +1163,8 @@ TEST(29d_holes)
run_test (_this, "29", true);
}
TEST(30_density)
{
run_test (_this, "30", false);
}

17
testdata/drc/drcSimpleTests_30.drc vendored Normal file
View File

@ -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)

BIN
testdata/drc/drcSimpleTests_30.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au30.gds vendored Normal file

Binary file not shown.