WIP: some bug fixes, tests for deep mode - some refinement needed.

This commit is contained in:
Matthias Koefferlein 2023-01-21 01:38:25 +01:00
parent 6c9d16c221
commit fe1d520d0c
10 changed files with 199 additions and 43 deletions

View File

@ -57,9 +57,10 @@ DeepShapeCollectionDelegateBase::apply_property_translator (const db::Properties
db::Shapes &shapes = c->shapes (m_deep_layer.layer ());
db::Shapes new_shapes (shapes.is_editable ());
new_shapes.assign (shapes, pt);
shapes.swap (new_shapes);
shapes.assign (new_shapes, pt);
}
}

View File

@ -172,6 +172,32 @@ static db::Region complex_region (const db::RecursiveShapeIterator *iter)
}
}
static void enable_properties (db::RecursiveShapeIterator *c)
{
c->apply_property_translator (db::PropertiesTranslator::make_pass_all ());
}
static void remove_properties (db::RecursiveShapeIterator *c)
{
c->apply_property_translator (db::PropertiesTranslator::make_remove_all ());
}
static void filter_properties (db::RecursiveShapeIterator *c, const std::vector<tl::Variant> &keys)
{
if (c->layout ()) {
std::set<tl::Variant> kf;
kf.insert (keys.begin (), keys.end ());
c->apply_property_translator (db::PropertiesTranslator::make_filter (const_cast<db::Layout *> (c->layout ())->properties_repository (), kf));
}
}
static void map_properties (db::RecursiveShapeIterator *c, const std::map<tl::Variant, tl::Variant> &map)
{
if (c->layout ()) {
c->apply_property_translator (db::PropertiesTranslator::make_key_mapper (const_cast<db::Layout *> (c->layout ())->properties_repository (), map));
}
}
Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveShapeIterator",
gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"),
"@brief Creates a recursive, single-layer shape iterator.\n"
@ -547,6 +573,20 @@ Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveS
"\n"
"This method has been introduced in version 0.25.3."
) +
gsi::method ("prop_id", &db::RecursiveShapeIterator::prop_id,
"@brief Gets the effective properties ID\n"
"The shape iterator supports property filtering and translation. This method will deliver "
"the effective property ID after translation. The original property ID can be obtained from "
"'shape.prop_id' and is not changed by installing filters or mappers.\n"
"\n"
"\\prop_id is evaluated by \\Region objects for example, when they are created "
"from a shape iterator.\n"
"\n"
"See \\enable_properties, \\filter_properties, \\remove_properties and \\map_properties for "
"details on this feature.\n"
"\n"
"This attribute has been introduced in version 0.28.4."
) +
gsi::method ("shape", &db::RecursiveShapeIterator::shape,
"@brief Gets the current shape\n"
"\n"
@ -592,6 +632,51 @@ Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveS
"@brief Comparison of iterators - inequality\n"
"\n"
"Two iterators are not equal if they do not point to the same shape.\n"
) +
gsi::method_ext ("enable_properties", &enable_properties,
"@brief Enables properties for the given iterator.\n"
"Afer enabling properties, \\prop_id will deliver the effective properties ID for the current shape. "
"By default, properties are not enabled and \\prop_id will always return 0 (no properties attached). "
"Alternatively you can apply \\filter_properties "
"or \\map_properties to enable properties with a specific name key.\n"
"\n"
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
"\n"
"This feature has been introduced in version 0.28.4."
) +
gsi::method_ext ("remove_properties", &remove_properties,
"@brief Removes properties for the given container.\n"
"This will remove all properties and \\prop_id will deliver 0 always (no properties attached).\n"
"Alternatively you can apply \\filter_properties "
"or \\map_properties to enable properties with a specific name key.\n"
"\n"
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
"So effectively after 'remove_properties' you cannot get them back.\n"
"\n"
"This feature has been introduced in version 0.28.4."
) +
gsi::method_ext ("filter_properties", &filter_properties, gsi::arg ("keys"),
"@brief Filters properties by certain keys.\n"
"Calling this method will reduce the properties to values with name keys from the 'keys' list.\n"
"As a side effect, this method enables properties.\n"
"As with \\enable_properties or \\remove_properties, this filter has an effect on the value returned "
"by \\prop_id, not on the properties ID attached to the shape directly.\n"
"\n"
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
"\n"
"This feature has been introduced in version 0.28.4."
) +
gsi::method_ext ("map_properties", &map_properties, gsi::arg ("key_map"),
"@brief Maps properties by name key.\n"
"Calling this method will reduce the properties to values with name keys from the 'keys' hash and "
"renames the properties. Property values with keys not listed in the key map will be removed.\n"
"As a side effect, this method enables properties.\n"
"As with \\enable_properties or \\remove_properties, this filter has an effect on the value returned "
"by \\prop_id, not on the properties ID attached to the shape directly.\n"
"\n"
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
"\n"
"This feature has been introduced in version 0.28.4."
),
"@brief An iterator delivering shapes recursively\n"
"\n"

View File

@ -323,6 +323,30 @@ module DRC
def negative
DRCNegative::new
end
def enable_props
DRCPropertySelector::new(:enable_properties)
end
def remove_props
DRCPropertySelector::new(:remove_properties)
end
def select_props(*keys)
self._context("select_props") do
keys.each do |k|
k.is_a?(String) || k.is_a?(1.class) || raise("Key values need to be integers or strings (got '#{k.inspect}')")
end
DRCPropertySelector::new(:filter_properties, keys)
end
end
def map_props(hash)
self._context("map_props") do
hash.is_a?(Hash) || raise("Argument needs to be a hash (got '#{hash.inspect}')")
DRCPropertySelector::new(:map_properties, hash)
end
end
def pattern(p)
self._context("pattern") do
@ -2806,7 +2830,7 @@ CODE
end
end
def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, cls)
def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, prop_sel, cls)
if layers.empty? && ! @deep
@ -2819,6 +2843,7 @@ CODE
else
iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers)
end
prop_sel.each { |p| p.apply_to(iter) }
iter.shape_flags = shape_flags
iter.global_dtrans = global_trans

View File

@ -4783,37 +4783,35 @@ CODE
# %DRC%
# @name select_props
# @brief Enables or selects properties from original or property-annotated layers
# @synopsis layer.select_props
# @brief Enables or selects properties from a property-annotated layer
# @synopsis layer.select_props(keys)
#
# This method will enable user properties or select specific property keys
# from layers. It returns a new layer with properties enabled. The
# This method will select specific property keys
# from layers. It returns a new layer with the new properties. The
# original layer is not modified.
#
# When used on original layers, this method will enable properties on input.
# By default, properties are not read:
#
# @code
# layer1 = input(1, 0)
# layer1_with_props = input(1, 0).select_props
# @/code
#
# You can specify the user property keys (names) to use. As user properties
# in general are a set of key/value pairs and may carry multiple information
# in general are a set of key/value pairs and may carry multiple values
# under different keys, this feature can be handy to filter out a specific
# aspect. To get only the values from key 1 (integer), use:
#
# @code
# layer1_with_props = input(1, 0).select_props(1)
# layer1 = input(1, 0, enable_properties)
# layer1_filtered = layer1.select_props(1)
# @/code
#
# To get the combined key 1 and 2 properties, use:
#
# @code
# layer1_with_props = input(1, 0).select_props(1, 2)
# layer1 = input(1, 0, enable_properties)
# layer1_filtered = layer1.select_props(1, 2)
# @/code
#
# Without any arguments, this method will remove all properties.
# Note that you can directly filter or map properties on input
# which is more efficient than first loading all and then selecting some
# properties. See \DRCSource#input for details.
#
# \map_props is a way to change property keys and \remove_props
# will entirely remove all user properties.
@ -4822,7 +4820,7 @@ CODE
# @brief Selects properties with certain keys and allows key mapping
# @synopsis layer.map_props({ key => key_new, .. })
#
# Similar to \select_props, this method will enable user properties
# Similar to \select_props, this method will map or filter properties and
# and take the values from certain keys. In addition, this method allows
# mapping keys to new keys. Specify a hash argument with old to new keys.
#
@ -4835,7 +4833,8 @@ CODE
# use:
#
# @code
# layer1_with_props = input(1, 0).map_props({ 2 => 1 })
# layer1 = input(1, 0, enable_properties)
# layer1_mapped = layer1.map_props({ 2 => 1 })
# @/code
#
# See also \select_props and \remove_props.

View File

@ -374,6 +374,7 @@ CODE
# @synopsis source.input(layer, datatype)
# @synopsis source.input(layer_into)
# @synopsis source.input(filter, ...)
# @synopsis source.input(props_spec, ...)
# Creates a layer with the shapes from the given layer of the source.
# The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo
# object or by a sequence of filters.
@ -410,12 +411,27 @@ CODE
#
# "input" without any arguments will create a new, empty original layer.
#
# If you want to use user properties - for example with properties constraints in DRC checks -
# you need to enable properties on input:
#
# @code
# input1_with_props = input(1, 0, enable_props)
# @/code
#
# You can also filter or map property keys, similar to the functions available on
# layers (\DRCLayer#map_props, \DRCLayer#select_props). For example to select
# property values with key 17 (numerical) only, use:
#
# @code
# input1_with_props = input(1, 0, select_props(17))
# @/code
#
# Use the global version of "input" without a source object to address the default source.
def input(*args)
@engine._context("input") do
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region))
layers, prop_selectors = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, prop_selectors, RBA::Region))
end
end
@ -441,8 +457,8 @@ CODE
def labels(*args)
@engine._context("labels") do
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, RBA::Texts))
layers, prop_selectors = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, prop_selectors, RBA::Texts))
end
end
@ -467,8 +483,8 @@ CODE
def polygons(*args)
@engine._context("polygons") do
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, RBA::Region))
layers, prop_selectors = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, prop_selectors, RBA::Region))
end
end
@ -496,8 +512,8 @@ CODE
def edges(*args)
@engine._context("edges") do
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, RBA::Edges))
layers, prop_selectors = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, prop_selectors, RBA::Edges))
end
end
@ -525,8 +541,8 @@ CODE
def edge_pairs(*args)
@engine._context("edge_pairs") do
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, RBA::EdgePairs))
layers, prop_selectors = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, prop_selectors, RBA::EdgePairs))
end
end
@ -581,7 +597,10 @@ CODE
def parse_input_layers(*args)
layers = []
prop_selectors = args.select { |a| a.is_a?(DRCPropertySelector) }
args = args.select { |a| !a.is_a?(DRCPropertySelector) }
if args.size == 0
li = @layout.insert_layer(RBA::LayerInfo::new)
@ -615,7 +634,7 @@ CODE
end
layers
[ layers, prop_selectors ]
end

View File

@ -158,6 +158,19 @@ module DRC
def initialize
end
end
# Property selector for "input"
class DRCPropertySelector
attr_accessor :method
attr_accessor :args
def apply_to(iter)
iter.send(self.method, *self.args)
end
def initialize(method, *args)
self.method = method
self.args = args
end
end
# A wrapper for a pair of limit values
# This class is used to identify projection limits for DRC

View File

@ -1401,3 +1401,8 @@ TEST(70_props)
{
run_test (_this, "70", false);
}
TEST(70d_props)
{
run_test (_this, "70", true);
}

View File

@ -8,28 +8,37 @@ if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l4 = input(4, 0)
# properties on input
l1 = input(1, 0)
l2 = input(2, 0)
l3_wp = input(3, 0, enable_props)
l3_wp1_input = input(3, 0, select_props(1))
l3_wp2as1_input = input(3, 0, map_props({ 2 => 1 }))
l3 = input(3, 0)
l4_wp = input(4, 0, enable_props)
l4 = input(4, 0)
# derived properties
l3_wp1 = l3_wp.select_props(1)
l3_wp2as1 = l3_wp.map_props({ 2 => 1 })
l3_nowp = l3_wp.remove_props
# dump to output
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l4.output(4, 0)
l3_wp = l3.select_props
l3_wp1 = l3.select_props(1)
l3_wp2as1 = l3.map_props({ 2 => 1 })
l3_nowp = l3_wp.remove_props
l4_wp = l4.select_props
l3_wp.output(10, 0)
l3_wp1.output(11, 0)
l3_wp2as1.output(12, 0)
l3_nowp.output(13, 0)
l4_wp.output(14, 0)
l3_wp1_input.output(14, 0)
l3_wp2as1_input.output(15, 0)
l4_wp.output(16, 0)
# booleans with properties constraints
l3_wp.and(l4_wp, props_eq).output(20, 0)
l3_wp1.and(l4_wp, props_eq).output(21, 0)

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au70d.gds vendored Normal file

Binary file not shown.