Skip to content

Commit aee6c25

Browse files
authored
Merge pull request #143 from krcb197/105-user-defined-properties-in-python-wrappers
105 user defined properties in python wrappers
2 parents f469e5a + 5f17dea commit aee6c25

24 files changed

Lines changed: 817 additions & 33 deletions

.github/workflows/action.yaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ jobs:
3737
python -m pip install .
3838
python -m pip install -U pylint
3939
40-
- name: Run Lint
41-
#run: pylint --rcfile tests/pylint.rc src/peakrdl_python tests/unit_tests
40+
- name: Run Main code Lint
4241
run: pylint --rcfile tests/pylint.rc src/peakrdl_python
4342

43+
- name: Run unit tests Lint
44+
run: pylint --rcfile tests/pylint.rc src/peakrdl_python tests/unit_tests
45+
4446
mypy:
4547
runs-on: ubuntu-latest
4648
steps:
@@ -126,6 +128,12 @@ jobs:
126128
127129
python -m generate_and_test --RDL_source_file tests/testcases/accelera-generic_example.rdl --root_node some_register_map
128130
131+
python -m generate_and_test --RDL_source_file tests/testcases/user_defined_properties.rdl --root_node user_defined_properties --udp bool_property_to_include
132+
python -m generate_and_test --RDL_source_file tests/testcases/user_defined_properties.rdl --root_node user_defined_properties --udp bool_property_to_include enum_property_to_include
133+
python -m generate_and_test --RDL_source_file tests/testcases/user_defined_properties.rdl --root_node user_defined_properties --udp bool_property_to_include enum_property_to_include int_property_to_include
134+
python -m generate_and_test --RDL_source_file tests/testcases/user_defined_properties.rdl --root_node user_defined_properties --udp bool_property_to_include enum_property_to_include int_property_to_include str_property_to_include
135+
python -m generate_and_test --RDL_source_file tests/testcases/user_defined_properties.rdl --root_node user_defined_properties --udp bool_property_to_include enum_property_to_include int_property_to_include str_property_to_include struct_property_to_include double_layer_struct_property_to_include
136+
129137
peakrdl_integration:
130138
needs:
131139
- mypy
@@ -158,6 +166,7 @@ jobs:
158166
peakrdl python tests/testcases/hidden_property.rdl -o peakrdl_out/raw/show_hidden/ --show_hidden
159167
peakrdl python tests/testcases/simple.xml tests/testcases/multifile.rdl -o peakrdl_out/raw
160168
peakrdl python tests/testcases/extended_memories.rdl -o peakrdl_out/raw/
169+
peakrdl python tests/testcases/user_defined_properties.rdl -o peakrdl_out/raw/ --udp bool_property_to_include
161170
python -m unittest discover -s peakrdl_out/raw
162171
163172
peakrdl python tests/testcases/basic.rdl -o peakrdl_out/raw_async/ --async

docs/generated_package.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,29 @@ worry if they are in an array or not.
383383
.. literalinclude :: ../example/tranversing_address_map/reseting_registers.py
384384
:language: python
385385
386+
Exposing User Defined Properties
387+
--------------------------------
388+
389+
SystemRDL allows properties to be added to any component (Field, Memory, Register, Register File,
390+
Address Map), so called *User Defined Properties (UDP)*.
391+
392+
Consider the following systemRDL example with a user defined property: ``component_usage``
393+
394+
.. literalinclude :: ../example/user_defined_properties/user_defined_properties.rdl
395+
:language: systemrdl
396+
397+
User Defined Properties are not automatically included they must be specified, as shown:
398+
399+
.. code-block:: bash
400+
401+
peakrdl python chip_with_registers.rdl -o chip_with_registers
402+
403+
The user defined properties are stored in a ``udp`` property of all component in the generated
404+
register access and can be accessed as follows:
405+
406+
.. literalinclude :: ../example/user_defined_properties/demo_user_defined_properties.py
407+
:language: python
408+
386409
Python Safe Names
387410
=================
388411

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from user_defined_property.reg_model.user_defined_property import user_defined_property_cls
2+
from user_defined_property.sim_lib.dummy_callbacks import dummy_read, dummy_write
3+
from user_defined_property.lib.callbacks import NormalCallbackSet
4+
5+
if __name__ == '__main__':
6+
7+
# create an instance of the class
8+
regmodel = user_defined_property_cls(callbacks=NormalCallbackSet(read_callback=dummy_read,
9+
write_callback=dummy_write))
10+
11+
# loop through the the fields in the register access model and print out the value of the
12+
# component_usage property
13+
for field in regmodel.control_register.readable_fields:
14+
field_usage = field.udp['component_usage']
15+
print(f"Control register field:{field.inst_name} has recommend usage {field_usage.name}")
16+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
enum component_type {
2+
test_function = 0 { name = "test the design"; };
3+
device_configuration = 1 { name = "configuring_the_device"; };
4+
normal_use = 2 { desc="normal device usage"; };
5+
};
6+
7+
property component_usage { type = component_type; component = addrmap | regfile | reg | field | mem; };
8+
9+
addrmap user_defined_property {
10+
11+
reg {
12+
field { fieldwidth=4; component_usage=component_type::test_function; } data_loop_back;
13+
field { fieldwidth=1; component_usage=component_type::device_configuration; } power_control;
14+
field { fieldwidth=1; component_usage=component_type::normal_use; } led_control;
15+
} control_register;
16+
17+
};

generate_and_test.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
dest='legacy_block_access',
8484
help='peakrdl python has two methods to hold blocks of data, the '
8585
'legacy mode based on Array or the new mode using lists')
86+
CommandLineParser.add_argument('--udp', dest='udp', nargs='*',
87+
type=str, help='any user defined properties to include in the '
88+
'reg_model')
8689

8790

8891
def build_logging_cong(logfilepath:str):
@@ -168,7 +171,8 @@ def build_logging_cong(logfilepath:str):
168171
asyncoutput=CommandLineArgs.asyncoutput,
169172
delete_existing_package_content=not CommandLineArgs.suppress_cleanup,
170173
skip_library_copy=not CommandLineArgs.copy_libraries,
171-
legacy_block_access=CommandLineArgs.legacy_block_access)
174+
legacy_block_access=CommandLineArgs.legacy_block_access,
175+
user_defined_properties_to_include=CommandLineArgs.udp)
172176
print(f'generation time {time.time() - start_time}s')
173177

174178
if not CommandLineArgs.export_only:

src/peakrdl_python/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
1818
Variables that describes the peakrdl-python Package
1919
"""
20-
__version__ = "0.9.0.rc3"
20+
__version__ = "0.9.0-rc4"

src/peakrdl_python/__peakrdl__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ def add_exporter_arguments(self, arg_group: 'argparse._ActionsContainer') -> Non
7777
help='show addrmap, regfile, memory, register and fields that '
7878
'have been given the python_hide user defined property and '
7979
'would be removed from the build python by default')
80+
arg_group.add_argument('--udp', dest='udp', nargs='*', type=str,
81+
help='any user defined properties to include in the reg_model')
8082

8183
def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
8284
"""
@@ -100,5 +102,6 @@ def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> N
100102
skip_test_case_generation=options.skip_test_case_generation,
101103
delete_existing_package_content=not options.suppress_cleanup,
102104
legacy_block_access=options.legacy_block_access,
103-
show_hidden=options.show_hidden
105+
show_hidden=options.show_hidden,
106+
user_defined_properties_to_include=options.udp
104107
)

src/peakrdl_python/exporter.py

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import os
2121
from pathlib import Path
2222
from shutil import copy
23-
from typing import List, NoReturn, Iterable, Tuple, Dict, Any
23+
from typing import List, NoReturn, Iterable, Tuple, Dict, Any, Optional
2424

2525
import jinja2 as jj
2626
from systemrdl import RDLWalker # type: ignore
@@ -29,15 +29,16 @@
2929
from systemrdl.node import FieldNode, MemNode, AddressableNode # type: ignore
3030
from systemrdl.node import SignalNode # type: ignore
3131
from systemrdl.rdltypes import OnReadType, OnWriteType, PropertyReference # type: ignore
32-
from systemrdl.rdltypes.user_enum import UserEnumMeta # type: ignore
32+
from systemrdl.rdltypes.user_enum import UserEnum, UserEnumMeta # type: ignore
33+
from systemrdl.rdltypes.user_struct import UserStruct # type: ignore
3334

3435
from .systemrdl_node_utility_functions import get_reg_readable_fields, get_reg_writable_fields, \
3536
get_table_block, get_dependent_component, \
3637
get_field_bitmask_hex_string, get_field_inv_bitmask_hex_string, \
3738
get_field_max_value_hex_string, get_reg_max_value_hex_string, get_fully_qualified_type_name, \
3839
uses_enum, uses_memory, \
3940
get_memory_max_entry_value_hex_string, get_memory_width_bytes, \
40-
get_field_default_value, get_enum_values
41+
get_field_default_value, get_enum_values, get_properties_to_include, get_reg_fields
4142

4243
from .lib import get_array_typecode
4344

@@ -291,7 +292,8 @@ def __export_reg_model(self, # pylint: disable=too-many-arguments
291292
skip_lib_copy: bool,
292293
asyncoutput: bool,
293294
legacy_block_access: bool,
294-
show_hidden: bool) -> None:
295+
show_hidden: bool,
296+
udp_to_include: Optional[List[str]]) -> None:
295297

296298
context = {
297299
'print': print,
@@ -304,11 +306,14 @@ def __export_reg_model(self, # pylint: disable=too-many-arguments
304306
'systemrdlMemNode': MemNode,
305307
'systemrdlAddressableNode': AddressableNode,
306308
'systemrdlSignalNode': SignalNode,
309+
'systemrdlUserEnum': UserEnum,
310+
'systemrdlUserStruct': UserStruct,
307311
'asyncoutput': asyncoutput,
308312
'OnWriteType': OnWriteType,
309313
'OnReadType': OnReadType,
310314
'PropertyReference': PropertyReference,
311315
'isinstance': isinstance,
316+
'str': str,
312317
'uses_enum' : uses_enum(top_block),
313318
'uses_memory' : uses_memory(top_block),
314319
'get_fully_qualified_type_name': self._lookup_type_name,
@@ -323,6 +328,7 @@ def __export_reg_model(self, # pylint: disable=too-many-arguments
323328
'get_table_block': get_table_block,
324329
'get_reg_writable_fields': get_reg_writable_fields,
325330
'get_reg_readable_fields': get_reg_readable_fields,
331+
'get_reg_fields' : get_reg_fields,
326332
'get_memory_max_entry_value_hex_string': get_memory_max_entry_value_hex_string,
327333
'get_memory_width_bytes': get_memory_width_bytes,
328334
'get_field_default_value': get_field_default_value,
@@ -332,7 +338,12 @@ def __export_reg_model(self, # pylint: disable=too-many-arguments
332338
'skip_lib_copy': skip_lib_copy,
333339
'version': __version__,
334340
'legacy_block_access': legacy_block_access,
335-
'show_hidden': show_hidden
341+
'show_hidden': show_hidden,
342+
'udp_to_include': udp_to_include,
343+
'get_properties_to_include': get_properties_to_include,
344+
'dependent_property_enum':
345+
self._get_dependent_property_enum(node=top_block,
346+
udp_to_include=udp_to_include)
336347
}
337348
if legacy_block_access is True:
338349
context['get_array_typecode'] = get_array_typecode
@@ -437,7 +448,8 @@ def __export_tests(self, # pylint: disable=too-many-arguments
437448
skip_lib_copy: bool,
438449
asyncoutput: bool,
439450
legacy_block_access: bool,
440-
show_hidden: bool) -> None:
451+
show_hidden: bool,
452+
udp_to_include: Optional[List[str]]) -> None:
441453
"""
442454
443455
Args:
@@ -492,7 +504,11 @@ def is_reg_array(item: RegNode) -> bool:
492504
'systemrdlMemNode': MemNode,
493505
'systemrdlRegfileNode': RegfileNode,
494506
'systemrdlAddrmapNode': AddrmapNode,
507+
'systemrdlUserEnum': UserEnum,
508+
'systemrdlUserStruct': UserStruct,
495509
'isinstance': isinstance,
510+
'type': type,
511+
'str': str,
496512
'get_python_path_segments': get_python_path_segments,
497513
'safe_node_name': safe_node_name,
498514
'uses_memory': (len(owned_elements.memories) > 0),
@@ -512,7 +528,12 @@ def is_reg_array(item: RegNode) -> bool:
512528
'version': __version__,
513529
'get_array_typecode': get_array_typecode,
514530
'legacy_block_access': legacy_block_access,
515-
'show_hidden': show_hidden
531+
'show_hidden': show_hidden,
532+
'udp_to_include': udp_to_include,
533+
'get_properties_to_include': get_properties_to_include,
534+
'dependent_property_enum':
535+
self._get_dependent_property_enum(node=top_block,
536+
udp_to_include=udp_to_include)
516537
}
517538

518539

@@ -532,7 +553,8 @@ def export(self, node: Node, path: str, # pylint: disable=too-many-arguments
532553
delete_existing_package_content: bool = True,
533554
skip_library_copy: bool = False,
534555
legacy_block_access: bool = True,
535-
show_hidden: bool = False) -> List[str]:
556+
show_hidden: bool = False,
557+
user_defined_properties_to_include: Optional[List[str]] = None) -> List[str]:
536558
"""
537559
Generated Python Code and Testbench
538560
@@ -560,6 +582,9 @@ def export(self, node: Node, path: str, # pylint: disable=too-many-arguments
560582
set to true will not be included in the generated python code.
561583
This behaviour can be overridden by setting this property to
562584
true.
585+
user_defined_properties_to_include : A list of strings of the names of user-defined
586+
properties to include. Set to None for nothing
587+
to appear.
563588
564589
Returns:
565590
List[str] : modules that have been exported:
@@ -584,11 +609,28 @@ def export(self, node: Node, path: str, # pylint: disable=too-many-arguments
584609
include_libraries=not skip_library_copy)
585610
package.create_empty_package(cleanup=delete_existing_package_content)
586611

612+
if user_defined_properties_to_include is not None:
613+
# the list of user defined properties may not include the names used by peakrdl python
614+
# for control behaviours
615+
if not isinstance(user_defined_properties_to_include, list):
616+
raise TypeError(f'The user_defined_properties_to_include must be a list, got '
617+
f'{type(user_defined_properties_to_include)}')
618+
for entry in user_defined_properties_to_include:
619+
if not isinstance(entry, str):
620+
raise TypeError('The entries in the user_defined_properties_to_include must '
621+
f'be a str, got {type(entry)}')
622+
reserved_names = ['python_hide', 'python_name']
623+
for reserved_name in reserved_names:
624+
if reserved_name in user_defined_properties_to_include:
625+
raise RuntimeError('It is not permitted to expose a property name used to'
626+
' build the peakrdl-python wrappers: '+reserved_name)
627+
587628
self._build_node_type_table(top_block, show_hidden)
588629

589630
self.__export_reg_model(top_block=top_block, package=package, asyncoutput=asyncoutput,
590631
skip_lib_copy=skip_library_copy,
591-
legacy_block_access=legacy_block_access, show_hidden=show_hidden)
632+
legacy_block_access=legacy_block_access, show_hidden=show_hidden,
633+
udp_to_include=user_defined_properties_to_include)
592634

593635
self.__export_simulator(top_block=top_block, package=package, asyncoutput=asyncoutput,
594636
skip_lib_copy=skip_library_copy,
@@ -608,7 +650,8 @@ def export(self, node: Node, path: str, # pylint: disable=too-many-arguments
608650
self.__export_tests(top_block=top_block, package=package, asyncoutput=asyncoutput,
609651
skip_lib_copy=skip_library_copy,
610652
legacy_block_access=legacy_block_access,
611-
show_hidden=show_hidden)
653+
show_hidden=show_hidden,
654+
udp_to_include=user_defined_properties_to_include)
612655

613656
return top_block.inst_name
614657

@@ -719,3 +762,43 @@ def _get_dependent_enum(self, node: AddressableNode, show_hidden: bool) -> \
719762
if fully_qualified_enum_name not in enum_needed:
720763
enum_needed.append(fully_qualified_enum_name)
721764
yield field_enum, child_node
765+
766+
def _get_dependent_property_enum(self, node: AddressableNode,
767+
udp_to_include: Optional[List[str]]) -> \
768+
List[UserEnumMeta]:
769+
"""
770+
iterable of enums which is used by a descendant of the input node,
771+
this list is de-duplicated
772+
"""
773+
if udp_to_include is None:
774+
return []
775+
776+
enum_needed = []
777+
778+
def update_enum_list(node_to_process: AddressableNode) -> None:
779+
780+
def walk_property_struct_node(value: Any) -> None:
781+
if isinstance(value, UserEnum) and type(value) not in enum_needed:
782+
enum_needed.append(type(value))
783+
784+
if isinstance(value, UserStruct):
785+
for sub_value in value.members.values():
786+
walk_property_struct_node(sub_value)
787+
788+
node_properties = get_properties_to_include(node=node_to_process,
789+
udp_to_include=udp_to_include)
790+
for node_property_name in node_properties:
791+
node_property = node_to_process.get_property(node_property_name)
792+
if isinstance(node_property, UserEnum) and type(node_property) not in enum_needed:
793+
enum_needed.append(type(node_property))
794+
795+
if isinstance(node_property, UserStruct):
796+
for sub_value in node_property.members.values():
797+
walk_property_struct_node(sub_value)
798+
799+
update_enum_list(node_to_process=node)
800+
801+
for child_node in node.descendants():
802+
update_enum_list(node_to_process=child_node)
803+
804+
return enum_needed

src/peakrdl_python/lib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,4 @@
108108
from .utility_functions import get_array_typecode
109109
from .utility_functions import UnsupportedWidthError
110110
from .base import Node
111+
from .base import UDPStruct

src/peakrdl_python/lib/async_register.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ def _callbacks(self) -> Union[AsyncCallbackSet, AsyncCallbackSetLegacy]:
9696

9797
raise TypeError(f'unhandled parent callback type: {type(self.parent._callbacks)}')
9898

99+
@property
100+
@abstractmethod
101+
def fields(self) -> \
102+
Iterator[Union['FieldAsyncReadOnly','FieldAsyncWriteOnly', 'FieldAsyncReadWrite']]:
103+
"""
104+
generator that produces has all the fields within the register
105+
"""
106+
99107

100108
class RegAsyncReadOnly(AsyncReg, ABC):
101109
"""

0 commit comments

Comments
 (0)