diff --git a/generate_and_test.py b/generate_and_test.py index 9282b8e4..bb54972a 100644 --- a/generate_and_test.py +++ b/generate_and_test.py @@ -222,6 +222,7 @@ def build_logging_cong(logfilepath:str): skip_library_copy=not CommandLineArgs.copy_libraries, legacy_block_access=CommandLineArgs.legacy_block_access, user_defined_properties_to_include=CommandLineArgs.udp, + user_defined_properties_to_include_regex=CommandLineArgs.udp_regex, hidden_inst_name_regex=CommandLineArgs.hide_regex, legacy_enum_type=CommandLineArgs.legacy_enum_type, skip_systemrdl_name_and_desc_properties= diff --git a/src/peakrdl_python/__about__.py b/src/peakrdl_python/__about__.py index 2b69b958..7fff5899 100644 --- a/src/peakrdl_python/__about__.py +++ b/src/peakrdl_python/__about__.py @@ -17,4 +17,4 @@ Variables that describes the peakrdl-python Package """ -__version__ = "3.1.0" +__version__ = "3.1.0rc3" diff --git a/src/peakrdl_python/exporter.py b/src/peakrdl_python/exporter.py index 641aa48d..56c2ff89 100644 --- a/src/peakrdl_python/exporter.py +++ b/src/peakrdl_python/exporter.py @@ -415,8 +415,11 @@ def init_line_entry(module_name: str, context = { 'top_node': top_block, - 'systemrdlRegNode': RegNode, 'systemrdlFieldNode': FieldNode, + 'systemrdlRegNode': RegNode, + 'systemrdlRegfileNode': RegfileNode, + 'systemrdlAddrmapNode': AddrmapNode, + 'systemrdlMemNode': MemNode, 'systemrdlSignalNode': SignalNode, 'systemrdlUserStruct': UserStruct, 'systemrdlUserEnum': UserEnum, @@ -513,8 +516,11 @@ def init_line_entry(module_name:str, context = { 'top_node': top_block, - 'systemrdlMemNode': MemNode, 'systemrdlFieldNode': FieldNode, + 'systemrdlRegNode': RegNode, + 'systemrdlRegfileNode': RegfileNode, + 'systemrdlAddrmapNode': AddrmapNode, + 'systemrdlMemNode': MemNode, 'systemrdlSignalNode': SignalNode, 'systemrdlUserStruct': UserStruct, 'systemrdlUserEnum': UserEnum, @@ -587,6 +593,10 @@ def __export_reg_model_fields(self, *, context = { 'top_node': top_block, 'systemrdlFieldNode': FieldNode, + 'systemrdlRegNode': RegNode, + 'systemrdlRegfileNode': RegfileNode, + 'systemrdlAddrmapNode': AddrmapNode, + 'systemrdlMemNode': MemNode, 'systemrdlUserStruct': UserStruct, 'systemrdlUserEnum': UserEnum, 'isinstance': isinstance, diff --git a/src/peakrdl_python/lib/base.py b/src/peakrdl_python/lib/base.py index 1219f88b..4c570bad 100644 --- a/src/peakrdl_python/lib/base.py +++ b/src/peakrdl_python/lib/base.py @@ -26,12 +26,15 @@ from itertools import product from enum import IntEnum, Enum, auto import math +import re from .callbacks import CallbackSet, CallbackSetLegacy UDPStruct = dict[str, 'UDPType'] UDPType = Union[str, int, bool, IntEnum, UDPStruct] +array_instance_re = re.compile(r'(?P[A-Za-z_0-9]*)\[(?P\d+)\]') + class Base(ABC): """ base class of for all types @@ -97,6 +100,35 @@ def udp(self) -> UDPStruct: """ return {} + def _traverse_from_fully_qualified_name(self, fully_qualified_name: list[str]) -> 'Base': + """ + This method allows another node in the structure to located based on a list of string + which represented the systemRDL path. + + This function is intended for use with UDPs which reference other UDPs + """ + + # 1) location the root node by walking backwards up the tree until the parent is + # found + def locate_root(node: 'Base') -> 'Base': + if node.parent is None: + return node + return locate_root(node.parent) + root_node = locate_root(self) + # 2) check the 1st entry in the list matches the name of the root + if root_node.inst_name != fully_qualified_name[0]: + raise RuntimeError('root node name mismatch') + # 3) start walking down the tree matching the nodes + walking_node = root_node + for node_name in fully_qualified_name[1:]: + if not isinstance(walking_node, Node): + # the current node being traversed must be a Node type i.e. not a field + raise RuntimeError('node traversal has failed as type:{type(walking_node)} was' + ' unexpectedly encountered') + walking_node = walking_node.get_child_by_system_rdl_name(node_name) + + return walking_node + @property def rdl_name(self) -> Optional[str]: """ @@ -200,6 +232,18 @@ def get_child_by_system_rdl_name(self, name: Any) -> Any: """ if not isinstance(name, str): raise TypeError(f'name must be a string got {type(name)}') + + # check if an array style child pointer + array_name_match = array_instance_re.match(name) + if array_name_match: + root_name = array_name_match.group("root_name") + index = int(array_name_match.group("index")) + child_array = getattr(self, self.systemrdl_python_child_name_map[root_name]) + if not isinstance(child_array, NodeArray): + raise ValueError('attempting to use array indexing into a non-array ' + f'node: {root_name} of type:{type(child_array)}') + return child_array[index] + return getattr(self, self.systemrdl_python_child_name_map[name]) @property diff --git a/src/peakrdl_python/systemrdl_node_hashes.py b/src/peakrdl_python/systemrdl_node_hashes.py index 76b10bd1..2492f0e4 100644 --- a/src/peakrdl_python/systemrdl_node_hashes.py +++ b/src/peakrdl_python/systemrdl_node_hashes.py @@ -167,12 +167,18 @@ def __node_hash_components(node: Node, if desc is not None: value_to_hash.append(desc) + def udp_replace_for_hashing(item: Any) -> None: + if isinstance(item, list): + for child_udp_value in item: + udp_replace_for_hashing(child_udp_value) + elif isinstance(item, Node): + value_to_hash.append('.'.join(item.get_path_segments())) + else: + value_to_hash.append(item) + for udp in get_properties_to_include(node, udp_include_func): udp_value = node.get_property(udp) - if isinstance(udp_value, list): - value_to_hash.append(tuple(udp_value)) - else: - value_to_hash.append(udp_value) + udp_replace_for_hashing(udp_value) return value_to_hash diff --git a/src/peakrdl_python/templates/addrmap_tb.py.jinja b/src/peakrdl_python/templates/addrmap_tb.py.jinja index 85f71a8d..f5dd7d72 100644 --- a/src/peakrdl_python/templates/addrmap_tb.py.jinja +++ b/src/peakrdl_python/templates/addrmap_tb.py.jinja @@ -98,9 +98,9 @@ class {{fq_block_name}}_single_access({{top_node.inst_name}}_TestCase): # type: {% for property_name in property_list %} {% set property_value = node.get_property(property_name) %} {% if isinstance(property_value, list) %} - self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.udp['{{property_name}}'], [{% for sub_property_value in property_value %}{{ udp_property_entry(sub_property_value) }},{% endfor %}] ) + self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.udp['{{property_name}}'], [{% for sub_property_value in property_value %}{{ udp_property_entry(sub_property_value, true) }},{% endfor %}] ) {% else %} - self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.udp['{{property_name}}'], {{ udp_property_entry(property_value) }} ) + self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.udp['{{property_name}}'], {{ udp_property_entry(property_value, true) }} ) {% endif %} {% endfor %} {% endif %} diff --git a/src/peakrdl_python/templates/addrmap_udp_property.py.jinja b/src/peakrdl_python/templates/addrmap_udp_property.py.jinja index f9da8a43..4681d5cb 100644 --- a/src/peakrdl_python/templates/addrmap_udp_property.py.jinja +++ b/src/peakrdl_python/templates/addrmap_udp_property.py.jinja @@ -16,27 +16,33 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . #} -{%- macro udp_property_entry(value) %} +{%- macro udp_property_entry(value, full_qual_resolution) %} {%- if isinstance(value, systemrdlUserStruct) -%} { {%- for sub_name, sub_value in value.members.items() %} - {{udp_property_dict_entry(sub_name, sub_value)|indent(4)}} + {{udp_property_dict_entry(sub_name, sub_value, full_qual_resolution)|indent(4)}} {%- endfor %} } {%- elif isinstance(value, systemrdlUserEnum) -%} {{ type(value).type_name + '_property_enumcls.' + value.name.upper() }} {%- elif isinstance(value, str) -%} "{{ value }}" + {%- elif isinstance(value, (systemrdlFieldNode, systemrdlRegNode, systemrdlRegfileNode, systemrdlAddrmapNode, systemrdlMemNode)) -%} + {%- if full_qual_resolution -%} + self.dut.{{'.'.join(get_python_path_segments(value))}} + {%- else -%} + self._traverse_from_fully_qualified_name({{ value.get_path_segments() }}) + {%- endif -%} {%- else -%} {{ value }} {%- endif -%} {%- endmacro %} -{%- macro udp_property_dict_entry(name, value) %} +{%- macro udp_property_dict_entry(name, value, full_qual_resolution) %} {%- if isinstance(value, list) -%} - '{{name}}' : [ {% for sub_value in value %}{{udp_property_entry(sub_value)|indent(4)}}, {% endfor %}], + '{{name}}' : [ {% for sub_value in value %}{{udp_property_entry(sub_value, full_qual_resolution)}}, {% endfor %}], {%- else -%} - '{{name}}' : {{ udp_property_entry(value) }}, + '{{name}}' : {{ udp_property_entry(value, full_qual_resolution) }}, {%- endif %} {% endmacro %} @@ -47,9 +53,9 @@ along with this program. If not, see . @property def udp(self) -> UDPStruct: return { - {% for property_name in property_list %} - {{udp_property_dict_entry(property_name, node.instance.get_property(property_name))}} - {% endfor %} + {% for property_name in property_list -%} + {{udp_property_dict_entry(property_name, node.instance.get_property(property_name), false)|indent(4)}} + {%- endfor %} } {% endif %} diff --git a/tests/testcases/udp_with_referencing.rdl b/tests/testcases/udp_with_referencing.rdl new file mode 100644 index 00000000..4f685627 --- /dev/null +++ b/tests/testcases/udp_with_referencing.rdl @@ -0,0 +1,114 @@ +/* +Testcase the #292 bug +with a UDP that cross-references another +*/ +property field_pointer { + type = field; + component = field; +}; + +property register_pointer { + type = reg; + component = reg; +}; + +property register_file_pointer { + type = regfile; + component = regfile; +}; + +property field_parent_pointer { + type = reg; + component = field; +}; + +property register_child_pointer { + type = field[]; + component = reg; +}; + +struct addrmap_child { + regfile regfile_children[]; + reg reg_children[]; + mem mem_children[]; + addrmap addrmap_children[]; +}; + +property addrmap_child_pointer { + type = addrmap_child; + component = addrmap; +}; + +struct regfile_child { + regfile regfile_children[]; + reg reg_children[]; +}; + +property regfile_child_pointer { + type = regfile_child; + component = regfile; +}; + +property mem_child_pointer { + type = reg[]; + component = mem; +}; + +addrmap udp_with_referencing { + + reg reg_def { + field { fieldwidth=4; } field_a; + field { fieldwidth=4; } field_b; + field { fieldwidth=4; } field_c; + field { fieldwidth=4; } field_d; + register_child_pointer = '{ field_a, field_b, field_c, field_d } ; + }; + + regfile regfile_def { + regfile inner_regfile_def { + reg_def reg_value; + regfile_child_pointer = regfile_child'{ reg_children:'{ reg_value } }; + }; + reg_def reg_value; + inner_regfile_def inner_regfile_value; + regfile_child_pointer = regfile_child'{ reg_children:'{ reg_value }, regfile_children:'{ inner_regfile_value } }; + }; + + mem mem_def { + mementries = 32; + memwidth = 32; + + reg_def reg_value; + mem_child_pointer = '{ reg_value } ; + }; + + addrmap addrmap_def { + addrmap inner_addrmap_def { + reg_def reg_value; + regfile_def regfile_value; + addrmap_child_pointer = addrmap_child'{ reg_children:'{ reg_value }, regfile_children:'{ regfile_value } }; + }; + reg_def reg_value; + regfile_def addrmap_regfile_value; + external mem_def mem_value; + inner_addrmap_def addrmap_value; + addrmap_child_pointer = addrmap_child'{ reg_children:'{ reg_value }, regfile_children:'{ addrmap_regfile_value }, addrmap_children:'{ addrmap_value }, mem_children:'{ mem_value } }; + }; + + addrmap_def main; + addrmap_def view; + + view.reg_value.field_a->field_pointer = main.reg_value.field_a; + view.reg_value.field_a->field_parent_pointer = main.reg_value; + + view.reg_value->register_pointer = main.reg_value; + + reg basic_reg_def { + // a register def without any UDPs + field { fieldwidth=4; } field_a; + }; + + basic_reg_def basic_reg_array[3]; + main.addrmap_value.reg_value -> register_pointer = basic_reg_array[0]; + main.addrmap_value.regfile_value.reg_value -> register_pointer = basic_reg_array[1]; +}; \ No newline at end of file