diff --git a/src/peakrdl_python/__about__.py b/src/peakrdl_python/__about__.py index 94789b2b..43013e05 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.0.0rc6" +__version__ = "3.0.0rc7" diff --git a/src/peakrdl_python/lib_test/_common_base_test_class.py b/src/peakrdl_python/lib_test/_common_base_test_class.py index 85163505..0ceb5ed1 100644 --- a/src/peakrdl_python/lib_test/_common_base_test_class.py +++ b/src/peakrdl_python/lib_test/_common_base_test_class.py @@ -41,20 +41,23 @@ from ..lib.base_register import BaseReg from ..lib import Node from ..lib import Base +from ..lib import SystemRDLEnum from .utilities import get_field_bitmask_int, get_field_inv_bitmask from ..sim_lib.simulator import BaseSimulator + class NodeIterators: """ The Node Iterator class is intended to an efficient way to define the iterators of particular type that are present on a node """ __slots__ = ['__node_descriptions'] - def __init__(self, *args:Union[str, tuple[str, list[int]]]): + + def __init__(self, *args: Union[str, tuple[str, list[int]]]): self.__node_descriptions = args @staticmethod - def __rolled_item(item:Union[str, tuple[str, list[int]]]) -> str: + def __rolled_item(item: Union[str, tuple[str, list[int]]]) -> str: if isinstance(item, tuple): return item[0] return item @@ -64,7 +67,7 @@ def rolled(self) -> set[str]: """ name of all the rolled nodes in a set """ - return { self.__rolled_item(item) for item in self.__node_descriptions } + return {self.__rolled_item(item) for item in self.__node_descriptions} @property def unrolled(self) -> set[str]: @@ -74,7 +77,7 @@ def unrolled(self) -> set[str]: return_list = [] for item in self.__node_descriptions: if isinstance(item, tuple): - dim_set = list(product(*[range(dim) for dim in item[1]])) + dim_set = list(product(*[range(dim) for dim in item[1]])) for dim in dim_set: # to match the systemrdl compiler dimension put into the inst name of # the array, the name must be item[x][y] @@ -84,6 +87,7 @@ def unrolled(self) -> set[str]: return_list.append(item) return set(return_list) + class CommonTestBase(unittest.TestCase, ABC): """ Base Test class for the autogenerated register test to be used for the async and @@ -107,17 +111,17 @@ def legacy_block_access(self) -> bool: # pylint:disable-next=too-many-arguments def _single_field_property_test(self, *, fut: Union[FieldReadWrite, - FieldReadOnly, - FieldWriteOnly, - FieldEnumReadWrite, - FieldEnumReadOnly, - FieldEnumWriteOnly, - FieldAsyncReadOnly, - FieldAsyncWriteOnly, - FieldAsyncReadWrite, - FieldEnumAsyncReadOnly, - FieldEnumAsyncWriteOnly, - FieldEnumAsyncReadWrite], + FieldReadOnly, + FieldWriteOnly, + FieldEnumReadWrite, + FieldEnumReadOnly, + FieldEnumWriteOnly, + FieldAsyncReadOnly, + FieldAsyncWriteOnly, + FieldAsyncReadWrite, + FieldEnumAsyncReadOnly, + FieldEnumAsyncWriteOnly, + FieldEnumAsyncReadWrite], lsb: int, msb: int, low: int, @@ -137,7 +141,7 @@ def _single_field_property_test(self, *, self.assertEqual(fut.inverse_bitmask, get_field_inv_bitmask(fut)) width = (fut.high - fut.low) + 1 self.assertEqual(fut.width, width) - self.assertEqual(fut.max_value, (2**width) - 1) + self.assertEqual(fut.max_value, (2 ** width) - 1) self.assertEqual(fut.is_volatile, is_volatile) if default is None: @@ -284,9 +288,9 @@ def _single_regfile_property_test(self, *, self.__bad_attribute_test(dut=dut) def __single_node_rdl_name_and_desc_test(self, - dut: Base, - rdl_name: Optional[str], - rdl_desc: Optional[str]) -> None: + dut: Base, + rdl_name: Optional[str], + rdl_desc: Optional[str]) -> None: """ Check the SystemRDL Name and Desc properties for a node """ @@ -302,8 +306,8 @@ def __single_node_rdl_name_and_desc_test(self, def __test_node_inst_name(self, dut: Base, - parent_full_inst_name:Optional[str], - inst_name:str) -> None: + parent_full_inst_name: Optional[str], + inst_name: str) -> None: """ Test the `inst_name` and `full_inst_name` attributes of a node """ @@ -337,11 +341,11 @@ def __test_name_map(self, dut: Node, child_names: set[str]) -> None: def _test_field_iterators(self, *, rut: Union[RegReadOnly, - RegReadWrite, - RegWriteOnly, - RegAsyncReadOnly, - RegAsyncReadWrite, - RegAsyncWriteOnly], + RegReadWrite, + RegWriteOnly, + RegAsyncReadOnly, + RegAsyncReadWrite, + RegAsyncWriteOnly], has_sw_readable: bool, has_sw_writable: bool, readable_fields: set[str], @@ -354,7 +358,7 @@ def _test_field_iterators(self, *, )): raise TypeError(f'Register was expected to readable, got {type(rut)}') - child_readable_field_names = { field.inst_name for field in rut.readable_fields} + child_readable_field_names = {field.inst_name for field in rut.readable_fields} self.assertEqual(readable_fields, child_readable_field_names) else: @@ -382,16 +386,16 @@ def _test_field_iterators(self, *, self.assertEqual(readable_fields | writeable_fields, child_field_names) # Check the child name map - self.__test_name_map(dut=rut, child_names= readable_fields | writeable_fields) + self.__test_name_map(dut=rut, child_names=readable_fields | writeable_fields) def _test_register_iterators(self, dut: Union[AddressMap, AsyncAddressMap, RegFile, AsyncRegFile, - MemoryReadOnly, MemoryReadOnlyLegacy, - MemoryWriteOnly, MemoryWriteOnlyLegacy, - MemoryReadWrite, MemoryReadWriteLegacy, - MemoryAsyncReadOnly, MemoryAsyncReadOnlyLegacy, - MemoryAsyncWriteOnly, MemoryAsyncWriteOnlyLegacy, - MemoryAsyncReadWrite, MemoryAsyncReadWriteLegacy], + MemoryReadOnly, MemoryReadOnlyLegacy, + MemoryWriteOnly, MemoryWriteOnlyLegacy, + MemoryReadWrite, MemoryReadWriteLegacy, + MemoryAsyncReadOnly, MemoryAsyncReadOnlyLegacy, + MemoryAsyncWriteOnly, MemoryAsyncWriteOnlyLegacy, + MemoryAsyncReadWrite, MemoryAsyncReadWriteLegacy], readable_registers: NodeIterators, writeable_registers: NodeIterators) -> None: @@ -400,8 +404,8 @@ def _test_register_iterators(self, MemoryReadWrite, MemoryReadWriteLegacy, MemoryAsyncReadOnly, MemoryAsyncReadOnlyLegacy, MemoryAsyncReadWrite, MemoryAsyncReadWriteLegacy)): - child_readable_reg_names = { reg.inst_name for reg in - dut.get_readable_registers(unroll=True)} + child_readable_reg_names = {reg.inst_name for reg in + dut.get_readable_registers(unroll=True)} self.assertEqual(readable_registers.unrolled, child_readable_reg_names) child_readable_reg_names = {reg.inst_name for reg in dut.get_readable_registers(unroll=False)} @@ -443,7 +447,6 @@ def _test_register_iterators(self, child_names=readable_registers.rolled | writeable_registers.rolled) - def _test_memory_iterators(self, dut: Union[AddressMap, AsyncAddressMap], memories: NodeIterators) -> None: @@ -494,3 +497,56 @@ def _test_regfile_iterators(self, self.__test_name_map(dut=dut, child_names=readable_registers.rolled | writeable_registers.rolled | sections.rolled) + + def _full_to_reduced_enum_conversion( + self, + full_enum_def: dict[str, tuple[int, Optional[str], Optional[str]]]) -> dict[str, int]: + return {key:value[0] for key,value in full_enum_def.items() } + + def _test_enum_def_rdl_name_desc_( + self, + fut: Union[FieldEnumReadOnly, FieldEnumReadOnly, FieldEnumWriteOnly, + FieldEnumAsyncReadOnly, FieldEnumAsyncReadOnly, FieldEnumAsyncWriteOnly], + full_enum_def: dict[str, tuple[int, Optional[str], Optional[str]]]) -> None: + """ + Check that the enumeration in the field matches the enumeration specifed in the + systemRDL + + Args: + fut: field node + full_enum_def: definition of the enumeration a dictionary, with the of the + entry as a key and the value a tuple that has: + 1. int value encoding the enumeration + 2. system RDL name (or None) + 3. system RDL name (or None) + + Returns: None + + """ + + # pylint does not realise this is a class being returned rather than an object, so + # is unhappy with the name + # pylint:disable-next=invalid-name + EnumCls = fut.enum_cls + for name, value in full_enum_def.items(): + enum_inst = EnumCls[name] + self.assertEqual(enum_inst.value, value[0]) + + if issubclass(EnumCls, SystemRDLEnum): + if value[1] is None: + self.assertIsNone(enum_inst.rdl_name) + else: + self.assertEqual(enum_inst.rdl_name, value[1]) + + if value[2] is None: + self.assertIsNone(enum_inst.rdl_desc) + else: + self.assertEqual(enum_inst.rdl_desc, value[2]) + + else: + # if using a legacy enumeration, then the systemRDL name and desc must be None + # as the legacy enum did not support these + self.assertIsNone(value[1]) + self.assertIsNone(value[2]) + self.assertFalse(hasattr(enum_inst, 'rdl_name')) + self.assertFalse(hasattr(enum_inst, 'rdl_desc')) diff --git a/src/peakrdl_python/lib_test/async_reg_base_test_class.py b/src/peakrdl_python/lib_test/async_reg_base_test_class.py index e65d6431..2a1e6a55 100644 --- a/src/peakrdl_python/lib_test/async_reg_base_test_class.py +++ b/src/peakrdl_python/lib_test/async_reg_base_test_class.py @@ -24,7 +24,7 @@ import unittest from abc import ABC, abstractmethod -from typing import Union +from typing import Union, Optional from unittest.mock import patch, Mock, call from itertools import product, chain, combinations from collections.abc import Iterable @@ -299,7 +299,7 @@ async def __single_enum_field_write_test(self, async def _single_enum_field_read_and_write_test( self, fut: Union[FieldEnumAsyncReadOnly, FieldEnumAsyncReadOnly, FieldEnumAsyncWriteOnly], - enum_definition: dict[str, int], + full_enum_def: dict[str, tuple[int, Optional[str], Optional[str]]], is_sw_readable: bool, is_sw_writable: bool) -> None: """ @@ -314,6 +314,10 @@ async def _single_enum_field_read_and_write_test( is_sw_readable=is_sw_readable, is_sw_writable=is_sw_writable) + # split the enum definition from the full enum definition + self._test_enum_def_rdl_name_desc_(fut=fut, full_enum_def=full_enum_def) + enum_definition = self._full_to_reduced_enum_conversion(full_enum_def) + if is_sw_readable: if not isinstance(fut, (FieldEnumAsyncReadOnly, FieldEnumAsyncReadWrite)): raise TypeError('Test can not proceed as the fut is not a readable field') diff --git a/src/peakrdl_python/lib_test/base_reg_test_class.py b/src/peakrdl_python/lib_test/base_reg_test_class.py index f89e4f8e..97d9171c 100644 --- a/src/peakrdl_python/lib_test/base_reg_test_class.py +++ b/src/peakrdl_python/lib_test/base_reg_test_class.py @@ -22,7 +22,7 @@ Python tool. It provide the base class for the autogenerated tests """ from abc import ABC, abstractmethod -from typing import Union +from typing import Union, Optional from unittest.mock import patch, Mock, call from itertools import product, chain, combinations from collections.abc import Iterable @@ -292,7 +292,7 @@ def __single_enum_field_write_test(self, def _single_enum_field_read_and_write_test( self, fut: Union[FieldEnumReadOnly, FieldEnumReadOnly, FieldEnumWriteOnly], - enum_definition: dict[str, int], + full_enum_def: dict[str, tuple[int, Optional[str], Optional[str]]], is_sw_readable: bool, is_sw_writable: bool) -> None: """ @@ -307,6 +307,10 @@ def _single_enum_field_read_and_write_test( is_sw_readable=is_sw_readable, is_sw_writable=is_sw_writable) + # split the enum definition from the full enum definition + self._test_enum_def_rdl_name_desc_(fut=fut, full_enum_def=full_enum_def) + enum_definition = self._full_to_reduced_enum_conversion(full_enum_def) + if is_sw_readable: if not isinstance(fut, (FieldEnumReadOnly, FieldEnumReadWrite)): raise TypeError('Test can not proceed as the fut is not a readable field') diff --git a/src/peakrdl_python/templates/addrmap_tb.py.jinja b/src/peakrdl_python/templates/addrmap_tb.py.jinja index 8f6c837d..426cd1d7 100644 --- a/src/peakrdl_python/templates/addrmap_tb.py.jinja +++ b/src/peakrdl_python/templates/addrmap_tb.py.jinja @@ -82,31 +82,7 @@ from ._{{top_node.inst_name}}_test_base import random_enum_reg_value class {{fq_block_name}}_single_access({{top_node.inst_name}}_TestCase): # type: ignore[valid-type,misc] - def test_field_encoding_properties(self) -> None: - """ - Check that enumeration has the name and desc meta data from the systemRDL - """ - {% for node in owned_elements.fields -%} - {% if 'encode' in node.list_properties() %} - with self.subTest(msg='field: {{'.'.join(node.get_path_segments())}}'): - # test properties of field: {{'.'.join(node.get_path_segments())}} - {% for encoding_entry in node.get_property('encode') %} - self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.enum_cls.{{encoding_entry.name | upper}}.value, {{encoding_entry.value}}) - {% if not legacy_enum_type %} - {% if encoding_entry.rdl_name is none %} - self.assertIsNone(self.dut.{{'.'.join(get_python_path_segments(node))}}.enum_cls.{{encoding_entry.name | upper}}.rdl_name) - {% else %} - self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.enum_cls.{{encoding_entry.name | upper}}.rdl_name, {{encoding_entry.rdl_name | tojson}}) - {% endif %} - {% if encoding_entry.rdl_desc is none %} - self.assertIsNone(self.dut.{{'.'.join(get_python_path_segments(node))}}.enum_cls.{{encoding_entry.name | upper}}.rdl_desc) - {% else %} - self.assertEqual(self.dut.{{'.'.join(get_python_path_segments(node))}}.enum_cls.{{encoding_entry.name | upper}}.rdl_desc, {{encoding_entry.rdl_desc | tojson}}) - {% endif %} - {% endif %} - {% endfor %} - {% endif %} - {% endfor %} + def test_user_defined_properties(self) -> None: """ @@ -188,7 +164,7 @@ class {{fq_block_name}}_single_access({{top_node.inst_name}}_TestCase): # type: {% if asyncoutput %}await {%endif %}self._single_int_field_read_and_write_test(fut=self.dut.{{'.'.join(get_python_path_segments(node))}}, is_sw_readable={{node.is_sw_readable}}, is_sw_writable={{node.is_sw_writable}}) {%- else %} {% if asyncoutput %}await {%endif %}self._single_enum_field_read_and_write_test(fut=self.dut.{{'.'.join(get_python_path_segments(node))}}, # type: ignore[arg-type] - is_sw_readable={{node.is_sw_readable}}, is_sw_writable={{node.is_sw_writable}}, enum_definition={ {% for enum_entry in node.get_property('encode') %}'{{enum_entry.name.upper()}}':{{enum_entry.value}},{% endfor %} }) + is_sw_readable={{node.is_sw_readable}}, is_sw_writable={{node.is_sw_writable}}, full_enum_def={ {% for enum_entry in node.get_property('encode') %}'{{enum_entry.name.upper()}}':({{enum_entry.value}}, {% if enum_entry.rdl_name is none or legacy_enum_type or skip_systemrdl_name_and_desc_properties %}None{% else %}{{enum_entry.rdl_name | tojson}}{% endif %}, {% if enum_entry.rdl_desc is none or legacy_enum_type or skip_systemrdl_name_and_desc_properties %}None{% else %}{{enum_entry.rdl_desc | tojson}}{% endif %}),{% endfor %} }) {%- endif %} {%- endfor %}