diff --git a/src/peakrdl_python/__about__.py b/src/peakrdl_python/__about__.py index b1d15b6b..c76b096f 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__ = "0.9.2" +__version__ = "0.9.3" diff --git a/src/peakrdl_python/lib/__init__.py b/src/peakrdl_python/lib/__init__.py index 84acea5c..74b3ef94 100644 --- a/src/peakrdl_python/lib/__init__.py +++ b/src/peakrdl_python/lib/__init__.py @@ -37,52 +37,52 @@ from .base import AsyncAddressMapArray from .base import AsyncRegFileArray -from .register import Reg -from .register import RegArray -from .register import RegisterWriteVerifyError +from .register_and_field import Reg +from .register_and_field import RegArray +from .register_and_field import RegisterWriteVerifyError -from .register import RegReadOnly -from .register import RegWriteOnly -from .register import RegReadWrite -from .register import WritableRegister -from .register import ReadableRegister +from .register_and_field import RegReadOnly +from .register_and_field import RegWriteOnly +from .register_and_field import RegReadWrite +from .register_and_field import WritableRegister +from .register_and_field import ReadableRegister -from .register import RegReadOnlyArray -from .register import RegWriteOnlyArray -from .register import RegReadWriteArray -from .register import ReadableRegisterArray -from .register import WriteableRegisterArray +from .register_and_field import RegReadOnlyArray +from .register_and_field import RegWriteOnlyArray +from .register_and_field import RegReadWriteArray +from .register_and_field import ReadableRegisterArray +from .register_and_field import WriteableRegisterArray -from .async_register import AsyncReg -from .async_register import AsyncRegArray -from .async_register import RegAsyncReadOnly -from .async_register import RegAsyncWriteOnly -from .async_register import RegAsyncReadWrite -from .async_register import ReadableAsyncRegister -from .async_register import WritableAsyncRegister -from .async_register import RegAsyncReadOnlyArray -from .async_register import RegAsyncWriteOnlyArray -from .async_register import RegAsyncReadWriteArray -from .async_register import ReadableAsyncRegisterArray -from .async_register import WriteableAsyncRegisterArray +from .async_register_and_field import AsyncReg +from .async_register_and_field import AsyncRegArray +from .async_register_and_field import RegAsyncReadOnly +from .async_register_and_field import RegAsyncWriteOnly +from .async_register_and_field import RegAsyncReadWrite +from .async_register_and_field import ReadableAsyncRegister +from .async_register_and_field import WritableAsyncRegister +from .async_register_and_field import RegAsyncReadOnlyArray +from .async_register_and_field import RegAsyncWriteOnlyArray +from .async_register_and_field import RegAsyncReadWriteArray +from .async_register_and_field import ReadableAsyncRegisterArray +from .async_register_and_field import WriteableAsyncRegisterArray -from .fields import FieldSizeProps -from .fields import FieldMiscProps -from .fields import FieldReadOnly -from .fields import FieldWriteOnly -from .fields import FieldReadWrite -from .fields import Field -from .fields import FieldEnumReadOnly -from .fields import FieldEnumWriteOnly -from .fields import FieldEnumReadWrite -from .fields import FieldEnum +from .base_field import FieldSizeProps +from .base_field import FieldMiscProps +from .register_and_field import FieldReadOnly +from .register_and_field import FieldWriteOnly +from .register_and_field import FieldReadWrite +from .base_field import Field +from .register_and_field import FieldEnumReadOnly +from .register_and_field import FieldEnumWriteOnly +from .register_and_field import FieldEnumReadWrite +from .base_field import FieldEnum -from .fields import FieldAsyncReadOnly -from .fields import FieldAsyncWriteOnly -from .fields import FieldAsyncReadWrite -from .fields import FieldEnumAsyncReadOnly -from .fields import FieldEnumAsyncWriteOnly -from .fields import FieldEnumAsyncReadWrite +from .async_register_and_field import FieldAsyncReadOnly +from .async_register_and_field import FieldAsyncWriteOnly +from .async_register_and_field import FieldAsyncReadWrite +from .async_register_and_field import FieldEnumAsyncReadOnly +from .async_register_and_field import FieldEnumAsyncWriteOnly +from .async_register_and_field import FieldEnumAsyncReadWrite from .memory import MemoryReadOnly, MemoryReadOnlyLegacy from .memory import MemoryWriteOnly, MemoryWriteOnlyLegacy diff --git a/src/peakrdl_python/lib/async_memory.py b/src/peakrdl_python/lib/async_memory.py index da876375..60954b3c 100644 --- a/src/peakrdl_python/lib/async_memory.py +++ b/src/peakrdl_python/lib/async_memory.py @@ -40,10 +40,9 @@ if TYPE_CHECKING: - from .async_register import AsyncReg, AsyncRegArray - from .async_register import ReadableAsyncRegister, WritableAsyncRegister - from .async_register import ReadableAsyncRegisterArray, WriteableAsyncRegisterArray - + from .async_register_and_field import AsyncReg, AsyncRegArray + from .async_register_and_field import ReadableAsyncRegister, WritableAsyncRegister + from .async_register_and_field import ReadableAsyncRegisterArray, WriteableAsyncRegisterArray # pylint: disable=duplicate-code diff --git a/src/peakrdl_python/lib/async_register.py b/src/peakrdl_python/lib/async_register_and_field.py similarity index 82% rename from src/peakrdl_python/lib/async_register.py rename to src/peakrdl_python/lib/async_register_and_field.py index bafdb5cd..be6fe582 100644 --- a/src/peakrdl_python/lib/async_register.py +++ b/src/peakrdl_python/lib/async_register_and_field.py @@ -17,10 +17,10 @@ This module is intended to distributed as part of automatically generated code by the peakrdl-python tool. It provides a set of classes used by the autogenerated code to represent -registers +asynchronous registers and fields """ from enum import Enum -from typing import List, Union, Iterator, TYPE_CHECKING, Tuple, Optional, Dict, TypeVar +from typing import List, Union, Iterator, Tuple, Optional, Dict, TypeVar, cast from typing import AsyncGenerator from abc import ABC, abstractmethod from contextlib import asynccontextmanager @@ -28,15 +28,17 @@ import sys from warnings import warn -from .utility_functions import get_array_typecode +from .utility_functions import get_array_typecode, swap_msb_lsb_ordering from .base import AsyncAddressMap, AsyncRegFile -from .register import BaseReg, BaseRegArray, RegisterWriteVerifyError from .async_memory import MemoryAsyncReadOnly, MemoryAsyncWriteOnly, MemoryAsyncReadWrite, \ AsyncMemory, ReadableAsyncMemory, WritableAsyncMemory from .async_memory import MemoryAsyncReadOnlyLegacy, MemoryAsyncWriteOnlyLegacy, \ MemoryAsyncReadWriteLegacy from .async_memory import ReadableAsyncMemoryLegacy, WritableAsyncMemoryLegacy from .callbacks import AsyncCallbackSet, AsyncCallbackSetLegacy +from .base_register import BaseReg, BaseRegArray, RegisterWriteVerifyError +from .base_field import FieldEnum, FieldSizeProps, FieldMiscProps, \ + _FieldReadOnlyFramework, _FieldWriteOnlyFramework # pylint: disable=duplicate-code if sys.version_info >= (3, 11): @@ -45,9 +47,13 @@ from typing_extensions import Self # pylint: enable=duplicate-code -if TYPE_CHECKING: - - from .fields import FieldAsyncReadOnly, FieldAsyncWriteOnly, FieldAsyncReadWrite +# pylint: disable=duplicate-code +if sys.version_info >= (3, 10): + # type guarding was introduced in python 3.10 + from typing import TypeGuard +else: + from typing_extensions import TypeGuard +# pylint: enable=duplicate-code # pylint: disable=redefined-slots-in-subclass,too-many-lines @@ -99,7 +105,7 @@ def _callbacks(self) -> Union[AsyncCallbackSet, AsyncCallbackSetLegacy]: @property @abstractmethod def fields(self) -> \ - Iterator[Union['FieldAsyncReadOnly','FieldAsyncWriteOnly', 'FieldAsyncReadWrite']]: + Iterator[Union['FieldAsyncReadOnly', 'FieldAsyncWriteOnly', 'FieldAsyncReadWrite']]: """ generator that produces has all the fields within the register """ @@ -191,11 +197,17 @@ async def read(self) -> int: raise RuntimeError('This function does not have a useable callback') @property - @abstractmethod def readable_fields(self) -> Iterator[Union['FieldAsyncReadOnly', 'FieldAsyncReadWrite']]: """ generator that produces has all the readable fields within the register """ + def is_readable(field: Union['FieldAsyncReadOnly', + 'FieldAsyncWriteOnly', + 'FieldAsyncReadWrite']) -> \ + TypeGuard[Union['FieldAsyncReadOnly', 'FieldAsyncReadWrite']]: + return isinstance(field, (FieldAsyncReadOnly, FieldAsyncReadWrite)) + + return filter(is_readable, self.fields) async def read_fields(self) -> Dict['str', Union[bool, Enum, int]]: """ @@ -295,11 +307,17 @@ async def write(self, data: int) -> None: raise RuntimeError('This function does not have a useable callback') @property - @abstractmethod def writable_fields(self) -> Iterator[Union['FieldAsyncWriteOnly', 'FieldAsyncReadWrite']]: """ generator that produces has all the writable fields within the register """ + def is_writable(field: Union['FieldAsyncReadOnly', + 'FieldAsyncWriteOnly', + 'FieldAsyncReadWrite']) -> \ + TypeGuard[Union['FieldAsyncWriteOnly', 'FieldAsyncReadWrite']]: + return isinstance(field, (FieldAsyncWriteOnly, FieldAsyncReadWrite)) + + return filter(is_writable, self.fields) @abstractmethod async def write_fields(self, **kwargs) -> None: # type: ignore[no-untyped-def] @@ -348,7 +366,7 @@ def __init__(self, *, @asynccontextmanager async def single_read_modify_write(self, verify: bool = False, skip_write: bool = False) -> \ - AsyncGenerator[Self, None]: + AsyncGenerator[Self, None]: """ Context manager to allow multiple field reads/write to be done with a single set of field operations @@ -1035,3 +1053,211 @@ def _is_writeable(self) -> bool: ReadableAsyncRegisterArray = Union[RegAsyncReadOnlyArray, RegAsyncReadWriteArray] WriteableAsyncRegisterArray = Union[RegAsyncWriteOnlyArray, RegAsyncReadWriteArray] + + +class FieldAsyncReadOnly(_FieldReadOnlyFramework, ABC): + """ + class for an asynchronous read only register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__: List[str] = [] + + def __init__(self, *, + parent_register: ReadableAsyncRegister, + size_props: FieldSizeProps, + misc_props: FieldMiscProps, + logger_handle: str, + inst_name: str): + + if not isinstance(parent_register, (RegAsyncReadWrite, RegAsyncReadOnly)): + raise TypeError(f'size_props must be of {type(RegAsyncReadWrite)} or ' + f'{type(RegAsyncReadOnly)} but got {type(parent_register)}') + + # pylint: disable=duplicate-code + super().__init__(logger_handle=logger_handle, + size_props=size_props, + misc_props=misc_props, + parent_register=parent_register, + inst_name=inst_name) + # pylint: enable=duplicate-code + + async def read(self) -> int: # pylint: disable=invalid-overridden-method + """ + Asynchronously reads the register that this field is located in and retries the field + value applying the required masking and shifting + + Returns: + field value + + """ + return self.decode_read_value(await self.parent_register.read()) + + @property + def parent_register(self) -> ReadableAsyncRegister: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(ReadableAsyncRegister, self.parent) + + +class FieldAsyncWriteOnly(_FieldWriteOnlyFramework, ABC): + """ + class for an asynchronous write only register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__: List[str] = [] + + def __init__(self, *, + parent_register: WritableAsyncRegister, + size_props: FieldSizeProps, + misc_props: FieldMiscProps, + logger_handle: str, + inst_name: str): + + if not isinstance(parent_register, (RegAsyncReadWrite, RegAsyncWriteOnly)): + raise TypeError(f'size_props must be of {type(RegAsyncReadWrite)} or ' + f'{type(RegAsyncWriteOnly)} but got {type(parent_register)}') + + # pylint: disable=duplicate-code + super().__init__(logger_handle=logger_handle, + size_props=size_props, + misc_props=misc_props, + parent_register=parent_register, + inst_name=inst_name) + # pylint: enable=duplicate-code + + async def write(self, value: int) -> None: # pylint: disable=invalid-overridden-method + """ + The behaviour of this method depends on whether the field is located in + a readable register or not: + + If the register is readable, the method will perform an async read-modify-write + on the register updating the field with the value provided + + If the register is not writable all other field values will be asynchronously written + with zero. + + Args: + value: field value to update to + + """ + + # This code closely resembles the non-async version but is duplicated for clarity + # pylint: disable=duplicate-code + self._write_value_checks(value=value) + + if self.msb0: + value = swap_msb_lsb_ordering(value=value, width=self.width) + # pylint: enable=duplicate-code + + if (self.high == (self.register_data_width - 1)) and (self.low == 0): + # special case where the field occupies the whole register, + # there a straight write can be performed + new_reg_value = value + else: + # do a read, modify write + if isinstance(self.parent_register, RegAsyncReadWrite): + reg_value = await self.parent_register.read() + masked_reg_value = reg_value & self.inverse_bitmask + new_reg_value = masked_reg_value | (value << self.low) + elif isinstance(self.parent_register, RegAsyncWriteOnly): + new_reg_value = value << self.low + else: + raise TypeError('Unhandled parent type') + + await self.parent_register.write(new_reg_value) + + @property + def parent_register(self) -> WritableAsyncRegister: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(WritableAsyncRegister, self.parent) + + +class FieldAsyncReadWrite(FieldAsyncReadOnly, FieldAsyncWriteOnly, ABC): + """ + class for an asyncronous read/write register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__: List[str] = [] + + def __init__(self, *, + parent_register: RegAsyncReadWrite, + size_props: FieldSizeProps, + misc_props: FieldMiscProps, + logger_handle: str, + inst_name: str): + + if not isinstance(parent_register, RegAsyncReadWrite): + raise TypeError(f'size_props must be of {type(RegAsyncReadWrite)} ' + f'but got {type(parent_register)}') + + # pylint: disable=duplicate-code + super().__init__(logger_handle=logger_handle, + size_props=size_props, + misc_props=misc_props, + parent_register=parent_register, + inst_name=inst_name) + # pylint: enable=duplicate-code + + @property + def parent_register(self) -> RegAsyncReadWrite: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(RegAsyncReadWrite, self.parent) + + +class FieldEnumAsyncReadWrite(FieldAsyncReadWrite, FieldEnum, ABC): + """ + class for an async read/write register field with an enumerated value + """ + __slots__: List[str] = [] + + @property + def parent_register(self) -> RegAsyncReadWrite: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(RegAsyncReadWrite, self.parent) + + +class FieldEnumAsyncReadOnly(FieldAsyncReadOnly, FieldEnum, ABC): + """ + class for an async read only register field with an enumerated value + """ + __slots__: List[str] = [] + + +class FieldEnumAsyncWriteOnly(FieldAsyncWriteOnly, FieldEnum, ABC): + """ + class for an async write only register field with an enumerated value + """ + __slots__: List[str] = [] diff --git a/src/peakrdl_python/lib/base.py b/src/peakrdl_python/lib/base.py index 32c44d0d..71e60091 100644 --- a/src/peakrdl_python/lib/base.py +++ b/src/peakrdl_python/lib/base.py @@ -43,12 +43,12 @@ if TYPE_CHECKING: from .memory import Memory, MemoryArray from .async_memory import AsyncMemory, AsyncMemoryArray - from .register import Reg, RegArray - from .register import WritableRegister, ReadableRegister - from .async_register import AsyncReg, AsyncRegArray - from .async_register import ReadableAsyncRegister, WritableAsyncRegister - from .register import ReadableRegisterArray, WriteableRegisterArray - from .async_register import ReadableAsyncRegisterArray, WriteableAsyncRegisterArray + from .register_and_field import Reg, RegArray + from .register_and_field import WritableRegister, ReadableRegister + from .async_register_and_field import AsyncReg, AsyncRegArray + from .async_register_and_field import ReadableAsyncRegister, WritableAsyncRegister + from .register_and_field import ReadableRegisterArray, WriteableRegisterArray + from .async_register_and_field import ReadableAsyncRegisterArray, WriteableAsyncRegisterArray UDPStruct = Dict[str, 'UDPType'] UDPType = Union[str, int, bool, IntEnum, UDPStruct] diff --git a/src/peakrdl_python/lib/base_field.py b/src/peakrdl_python/lib/base_field.py new file mode 100644 index 00000000..7c605374 --- /dev/null +++ b/src/peakrdl_python/lib/base_field.py @@ -0,0 +1,473 @@ +""" +peakrdl-python is a tool to generate Python Register Access Layer (RAL) from SystemRDL +Copyright (C) 2021 - 2023 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +This module is intended to distributed as part of automatically generated code by the +peakrdl-python tool. It provides the base types for fields that are shared by non-async and async +fields +""" +from enum import EnumMeta +from typing import List, cast, Optional +from abc import ABC, abstractmethod + +from .base import Base +from .utility_functions import swap_msb_lsb_ordering +from .base_register import BaseReg + + +class FieldSizeProps: + """ + class to hold the key attributes of a field + """ + __slots__ = ['__msb', '__lsb', '__width', '__high', '__low'] + + # pylint: disable-next=too-many-arguments + def __init__(self, *, width: int, msb: int, lsb: int, high: int, low: int): + self.__width = width + self.__msb = msb + self.__lsb = lsb + self.__high = high + self.__low = low + + if self.width < 1: + raise ValueError('width must be greater than 0') + + if self.high < self.low: + raise ValueError('field high bit position can not be less than the ' + 'low bit position') + + if self.lsb < 0: + raise ValueError('field low bit position cannot be less than zero') + + @property + def lsb(self) -> int: + """ + bit position of the least significant bit (lsb) of the field in the + parent register + + Note: + fields can be defined as msb in bit 0 or as lsb in bit 0 + """ + return self.__lsb + + @property + def msb(self) -> int: + """ + bit position of the most significant bit (msb) of the field in the + parent register + + Note: + fields can be defined as msb in bit 0 or as lsb in bit 0 + """ + return self.__msb + + @property + def width(self) -> int: + """ + The width of the field in bits + """ + return self.__width + + @property + def max_value(self) -> int: + """maximum unsigned integer value that can be stored in the field + + For example: + + * 8-bit field returns 0xFF (255) + * 16-bit field returns 0xFFFF (65535) + * 32-bit field returns 0xFFFF_FFFF (4294967295) + + """ + return (2 ** self.width) - 1 + + @property + def high(self) -> int: + """ + low index of the bit range of the field in the + parent register + + Note: + The first bit in the register is bit 0 + """ + return self.__high + + @property + def low(self) -> int: + """ + low index of the bit range of the field in the + parent register + + Note: + The first bit in the register is bit 0 + """ + return self.__low + + +class FieldMiscProps: + """ + Class to hold additional attributes of a field + """ + + __slots__ = ['__default', '__is_volatile'] + + def __init__(self, default:Optional[int], is_volatile:bool): + self.__default = default + self.__is_volatile = is_volatile + + @property + def default(self) -> Optional[int]: + """ + The default (reset) value of the field + + None + - if the field is not reset. + - if the register resets to a signal value tht can not be determined + """ + return self.__default + + @property + def is_volatile(self) -> bool: + """ + Volatility of the field. True if the field is hardware-writable. + """ + return self.__is_volatile + + +class Field(Base, ABC): + """ + base class of register field wrappers + + Note: + It is not expected that this class will be instantiated under normal + circumstances however, it is useful for type checking + """ + + __slots__ = ['__size_props', '__misc_props', + '__bitmask', '__msb0', '__lsb0'] + + def __init__(self, *, + parent_register: BaseReg, size_props: FieldSizeProps, misc_props: FieldMiscProps, + logger_handle: str, inst_name: str): + + super().__init__(logger_handle=logger_handle, + inst_name=inst_name, parent=parent_register) + + if not isinstance(size_props, FieldSizeProps): + raise TypeError(f'size_props must be of {type(FieldSizeProps)} ' + f'but got {type(size_props)}') + self.__size_props = size_props + + if not isinstance(misc_props, FieldMiscProps): + raise TypeError(f'misc_props must be of {type(FieldMiscProps)} ' + f'but got {type(misc_props)}') + self.__misc_props = misc_props + + if not isinstance(parent_register, BaseReg): + raise TypeError(f'parent_register must be of {type(BaseReg)} ' + f'but got {type(parent_register)}') + + if self.width > self.register_data_width: + raise ValueError('width can not be greater than parent width') + + if self.high > self.register_data_width: + raise ValueError(f'field high bit position {self.high:d} must be less than the ' + f'parent register width ({self.register_data_width:d})') + + if self.low > self.register_data_width: + raise ValueError('field lsb must be less than the parent ' + 'register width') + + if self.high - self.low + 1 != self.width: + raise ValueError('field width defined by lsb and msb does not match' + ' specified width') + + if (self.msb == self.high) and (self.lsb == self.low): + self.__lsb0 = True + self.__msb0 = False + elif (self.msb == self.low) and (self.lsb == self.high): + self.__lsb0 = False + self.__msb0 = True + else: + raise ValueError('msb/lsb are inconsistent with low/high') + + self.__bitmask = 0 + for bit_position in range(self.low, self.high+1): + self.__bitmask |= (1 << bit_position) + + @property + def lsb(self) -> int: + """ + bit position of the least significant bit (lsb) of the field in the + parent register + + Note: + fields can be defined as msb in bit 0 or as lsb in bit 0 + """ + return self.__size_props.lsb + + @property + def msb(self) -> int: + """ + bit position of the most significant bit (msb) of the field in the + parent register + + Note: + fields can be defined as msb in bit 0 or as lsb in bit 0 + """ + return self.__size_props.msb + + @property + def width(self) -> int: + """ + The width of the field in bits + """ + return self.__size_props.width + + @property + def max_value(self) -> int: + """maximum unsigned integer value that can be stored in the field + + For example: + + * 8-bit field returns 0xFF (255) + * 16-bit field returns 0xFFFF (65535) + * 32-bit field returns 0xFFFF_FFFF (4294967295) + + """ + return (2 ** self.width) - 1 + + @property + def high(self) -> int: + """ + low index of the bit range of the field in the + parent register + + Note: + The first bit in the register is bit 0 + """ + return self.__size_props.high + + @property + def low(self) -> int: + """ + low index of the bit range of the field in the + parent register + + Note: + The first bit in the register is bit 0 + """ + return self.__size_props.low + + @property + def bitmask(self) -> int: + """ + The bit mask needed to extract the field from its register + + For example a register field occupying bits 7 to 4 in a 16-bit register + will have a bit mask of 0x00F0 + """ + return self.__bitmask + + @property + def register_data_width(self) -> int: + """ + The width of the register within which the field resides in bits + """ + return self.__parent_register.width + + @property + def inverse_bitmask(self) -> int: + """ + The bitwise inverse of the bitmask needed to extract the field from its + register + + For example a register field occupying bits 7 to 4 in a 16-bit register + will have a inverse bit mask of 0xFF0F + """ + return self.__parent_register.max_value ^ self.bitmask + + @property + def msb0(self) -> bool: + """ + The field can either be lsb0 or msb0 + + Returns: true if msb0 + + """ + return self.__msb0 + + @property + def lsb0(self) -> bool: + """ + The field can either be lsb0 or msb0 + + Returns: true if lsb0 + + """ + return self.__lsb0 + + @property + def default(self) -> Optional[int]: + """ + The default value of the field + + This returns None: + - if the field is not reset. + - if the register resets to a signal value tht can not be determined + """ + return self.__misc_props.default + + @property + def is_volatile(self) -> bool: + """ + The HW volatility of the field + """ + return self.__misc_props.is_volatile + + @property + def __parent_register(self) -> BaseReg: + """ + parent register the field is placed in + """ + # this cast is OK because an explict typing check was done in the __init__ + return cast(BaseReg, self.parent) + + +class _FieldReadOnlyFramework(Field, ABC): + """ + base class for a async or normal read only register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__ : List[str] = [] + + def decode_read_value(self, value: int) -> int: + """ + extracts the field value from a register value, by applying the bit + mask and shift needed + + Args: + value: value to decode, normally read from a register + + Returns: + field value + """ + if not isinstance(value, int): + raise TypeError(f'value must be an int but got {type(value)}') + + if value < 0: + raise ValueError('value to be decoded must be greater ' + 'than or equal to 0') + + if value > self.__parent_register.max_value: + raise ValueError(f'value to bede coded must be less than or equal ' + f'to {self.__parent_register.max_value:d}') + + if self.msb0 is False: + return_value = (value & self.bitmask) >> self.low + else: + return_value = swap_msb_lsb_ordering(value=(value & self.bitmask) >> self.low, + width=self.width) + + return return_value + + @property + def __parent_register(self) -> BaseReg: + """ + parent register the field is placed in + """ + # this cast is OK because an explict typing check was done in the __init__ + return cast(BaseReg, self.parent) + + +class _FieldWriteOnlyFramework(Field, ABC): + """ + class for a write only register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__ : List[str] = [] + + def _write_value_checks(self, value: int) -> None: + """ + Carries out the value for the encode_write_value and write methods + + Args: + value: proposed value to write + """ + if not isinstance(value, int): + raise TypeError(f'value must be an int but got {type(value)}') + + if value < 0: + raise ValueError('value to be written to register must be greater ' + 'than or equal to 0') + + if value > self.max_value: + raise ValueError(f'value to be written to register must be less ' + f'than or equal to {self.max_value:d}') + + def encode_write_value(self, value: int) -> int: + """ + Check that a value is legal for the field and then encode it in preparation to be written + to the register + + Args: + value: field value + + Returns: + value which can be applied to the register to update the field + + """ + self._write_value_checks(value=value) + + if self.msb0 is False: + return_value = value << self.low + else: + return_value = swap_msb_lsb_ordering(value=value, width=self.width) << self.low + + return return_value + + +class FieldEnum(Field, ABC): + """ + class for a register field with an enumerated value + """ + __slots__: List[str] = [] + + @property + @abstractmethod + def enum_cls(self) -> EnumMeta: + """ + The enumeration class for this field + """ + + @property + def _enum_values(self) -> List[int]: + """ + provide the legal values for the enumeration + """ + return [e.value for e in self.enum_cls] # type: ignore[var-annotated] diff --git a/src/peakrdl_python/lib/base_register.py b/src/peakrdl_python/lib/base_register.py new file mode 100644 index 00000000..463e7054 --- /dev/null +++ b/src/peakrdl_python/lib/base_register.py @@ -0,0 +1,229 @@ +""" +peakrdl-python is a tool to generate Python Register Access Layer (RAL) from SystemRDL +Copyright (C) 2021 - 2023 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +This module is intended to distributed as part of automatically generated code by the +peakrdl-python tool. It provides a set of classes used by the autogenerated code to represent +registers +""" +from typing import List, Union, Tuple, Optional, Dict, TypeVar, Type +from abc import ABC, abstractmethod + + +from .base import Node, AddressMap, RegFile, NodeArray +from .utility_functions import legal_register_width +from .base import AsyncAddressMap, AsyncRegFile +from .memory import BaseMemory + + +class RegisterWriteVerifyError(Exception): + """ + Exception that occurs when the read after a write does not match the expected value + """ + + +class BaseReg(Node, ABC): + """ + base class of register wrappers + + Note: + It is not expected that this class will be instantiated under normal + circumstances however, it is useful for type checking + """ + + __slots__: List[str] = ['__width', '__accesswidth'] + + # pylint: disable=too-many-arguments,duplicate-code + def __init__(self, *, + address: int, + width: int, + accesswidth: int, + logger_handle: str, + inst_name: str, + parent: Union[AddressMap, AsyncAddressMap, RegFile, AsyncRegFile, BaseMemory, + 'BaseRegArray']): + + super().__init__(address=address, + logger_handle=logger_handle, + inst_name=inst_name, + parent=parent) + if not isinstance(width, int): + raise TypeError(f'width should be int but got {(type(width))}') + if not legal_register_width(width_in_bits=width): + raise ValueError(f'Unsupported register width {width:d}') + self.__width = width + if not isinstance(accesswidth, int): + raise TypeError(f'accesswidth should be int but got {(type(accesswidth))}') + if not legal_register_width(width_in_bits=accesswidth): + raise ValueError(f'Unsupported access width {accesswidth:d}') + self.__accesswidth = accesswidth + # pylint: enable=too-many-arguments,duplicate-code + + @property + def max_value(self) -> int: + """ + maximum unsigned integer value that can be stored in the register + + For example: + + * 8-bit register returns 0xFF (255) + * 16-bit register returns 0xFFFF (65535) + * 32-bit register returns 0xFFFF_FFFF (4294967295) + + """ + return (2 ** self.width) - 1 + + def _validate_data(self, data: int) -> None: + """ + Check that the data parameter is of valid type and range + """ + if not isinstance(data, int): + raise TypeError(f'data should be an int got {type(data)}') + + if data > self.max_value: + raise ValueError('data out of range') + + if data < 0: + raise ValueError('data out of range') + + @property + def width(self) -> int: + """ + The width of the register in bits, this uses the `regwidth` systemRDL property + """ + return self.__width + + @property + def accesswidth(self) -> int: + """ + The access width of the register in bits, this uses the `accesswidth` systemRDL property + """ + return self.__accesswidth + + @property + def size(self) -> int: + """ + Total Number of bytes of address the node occupies + """ + return self.__width >> 3 + + @property + @abstractmethod + def _is_readable(self) -> bool: + ... + + @property + @abstractmethod + def _is_writeable(self) -> bool: + ... + +# pylint: disable-next=invalid-name +BaseRegArrayElementType= TypeVar('BaseRegArrayElementType', bound=BaseReg) + + +class BaseRegArray(NodeArray[BaseRegArrayElementType], ABC): + """ + base class of register array wrappers (async and non-async) + + Note: + It is not expected that this class will be instantiated under normal + circumstances however, it is useful for type checking + """ + # pylint: disable=too-many-arguments,duplicate-code + + __slots__: List[str] = ['__width', '__accesswidth'] + + def __init__(self, *, + logger_handle: str, inst_name: str, + parent: Union[AddressMap, AsyncAddressMap, RegFile, AsyncRegFile, BaseMemory], + width: int, + accesswidth: int, + address: int, + stride: int, + dimensions: Tuple[int, ...], + elements: Optional[Dict[Tuple[int, ...], BaseRegArrayElementType]] = None): + + if not isinstance(width, int): + raise TypeError(f'width should be int but got {(type(width))}') + if not legal_register_width(width_in_bits=width): + raise ValueError(f'Unsupported register width {width:d}') + self.__width = width + if not isinstance(accesswidth, int): + raise TypeError(f'accesswidth should be int but got {(type(accesswidth))}') + if not legal_register_width(width_in_bits=accesswidth): + raise ValueError(f'Unsupported access width {accesswidth:d}') + self.__accesswidth = accesswidth + + if not issubclass(self._element_datatype, BaseReg): + raise TypeError(f'{self._element_datatype}') + + super().__init__(logger_handle=logger_handle, inst_name=inst_name, + parent=parent, address=address, + stride=stride, dimensions=dimensions, elements=elements) + + @property + def width(self) -> int: + """ + The width of the register in bits, this uses the `regwidth` systemRDL property + """ + return self.__width + + @property + def accesswidth(self) -> int: + """ + The access width of the register in bits, this uses the `accesswidth` systemRDL property + """ + return self.__accesswidth + + def _build_element(self, indices: Tuple[int, ...]) -> BaseRegArrayElementType: + + return self._element_datatype( + logger_handle=self._build_element_logger_handle(indices=indices), + address=self._address_calculator(indices), + inst_name=self._build_element_inst_name(indices=indices), + width=self.width, + accesswidth=self.accesswidth, + parent=self) + + def _sub_instance(self, elements: Dict[Tuple[int, ...], BaseRegArrayElementType]) ->\ + NodeArray[BaseRegArrayElementType]: + if not isinstance(self.parent, (AddressMap, AsyncAddressMap, RegFile, + AsyncRegFile, BaseMemory)): + raise RuntimeError('Parent of a Node Array must be Node') + return self.__class__(logger_handle=self._logger.name, + inst_name=self.inst_name, + parent=self.parent, + address=self.address, + width=self.width, + accesswidth=self.accesswidth, + stride=self.stride, + dimensions=self.dimensions, + elements=elements) + + @property + @abstractmethod + def _element_datatype(self) -> Type[BaseRegArrayElementType]: + ... + + @property + @abstractmethod + def _is_readable(self) -> bool: + ... + + @property + @abstractmethod + def _is_writeable(self) -> bool: + ... diff --git a/src/peakrdl_python/lib/fields.py b/src/peakrdl_python/lib/fields.py deleted file mode 100644 index 5c7b80fa..00000000 --- a/src/peakrdl_python/lib/fields.py +++ /dev/null @@ -1,892 +0,0 @@ -""" -peakrdl-python is a tool to generate Python Register Access Layer (RAL) from SystemRDL -Copyright (C) 2021 - 2023 - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -This module is intended to distributed as part of automatically generated code by the -peakrdl-python tool. It provides a set of classes used by the autogenerated code to represent -register fields -""" -from enum import EnumMeta -from typing import List, cast, Optional -from abc import ABC, abstractmethod - -from .base import Base -from .utility_functions import swap_msb_lsb_ordering -from .register import BaseReg -from .register import RegReadOnly -from .async_register import RegAsyncReadOnly -from .register import RegReadWrite -from .async_register import RegAsyncReadWrite -from .register import RegWriteOnly -from .async_register import RegAsyncWriteOnly -from .register import ReadableRegister -from .async_register import ReadableAsyncRegister -from .register import WritableRegister -from .async_register import WritableAsyncRegister - - -class FieldSizeProps: - """ - class to hold the key attributes of a field - """ - __slots__ = ['__msb', '__lsb', '__width', '__high', '__low'] - - # pylint: disable-next=too-many-arguments - def __init__(self, *, width: int, msb: int, lsb: int, high: int, low: int): - self.__width = width - self.__msb = msb - self.__lsb = lsb - self.__high = high - self.__low = low - - if self.width < 1: - raise ValueError('width must be greater than 0') - - if self.high < self.low: - raise ValueError('field high bit position can not be less than the ' - 'low bit position') - - if self.lsb < 0: - raise ValueError('field low bit position cannot be less than zero') - - @property - def lsb(self) -> int: - """ - bit position of the least significant bit (lsb) of the field in the - parent register - - Note: - fields can be defined as msb in bit 0 or as lsb in bit 0 - """ - return self.__lsb - - @property - def msb(self) -> int: - """ - bit position of the most significant bit (msb) of the field in the - parent register - - Note: - fields can be defined as msb in bit 0 or as lsb in bit 0 - """ - return self.__msb - - @property - def width(self) -> int: - """ - The width of the field in bits - """ - return self.__width - - @property - def max_value(self) -> int: - """maximum unsigned integer value that can be stored in the field - - For example: - - * 8-bit field returns 0xFF (255) - * 16-bit field returns 0xFFFF (65535) - * 32-bit field returns 0xFFFF_FFFF (4294967295) - - """ - return (2 ** self.width) - 1 - - @property - def high(self) -> int: - """ - low index of the bit range of the field in the - parent register - - Note: - The first bit in the register is bit 0 - """ - return self.__high - - @property - def low(self) -> int: - """ - low index of the bit range of the field in the - parent register - - Note: - The first bit in the register is bit 0 - """ - return self.__low - - -class FieldMiscProps: - """ - Class to hold additional attributes of a field - """ - - __slots__ = ['__default', '__is_volatile'] - - def __init__(self, default:Optional[int], is_volatile:bool): - self.__default = default - self.__is_volatile = is_volatile - - @property - def default(self) -> Optional[int]: - """ - The default (reset) value of the field - - None - - if the field is not reset. - - if the register resets to a signal value tht can not be determined - """ - return self.__default - - @property - def is_volatile(self) -> bool: - """ - Volatility of the field. True if the field is hardware-writable. - """ - return self.__is_volatile - - -class Field(Base, ABC): - """ - base class of register field wrappers - - Note: - It is not expected that this class will be instantiated under normal - circumstances however, it is useful for type checking - """ - - __slots__ = ['__size_props', '__misc_props', - '__bitmask', '__msb0', '__lsb0'] - - def __init__(self, *, - parent_register: BaseReg, size_props: FieldSizeProps, misc_props: FieldMiscProps, - logger_handle: str, inst_name: str): - - super().__init__(logger_handle=logger_handle, - inst_name=inst_name, parent=parent_register) - - if not isinstance(size_props, FieldSizeProps): - raise TypeError(f'size_props must be of {type(FieldSizeProps)} ' - f'but got {type(size_props)}') - self.__size_props = size_props - - if not isinstance(misc_props, FieldMiscProps): - raise TypeError(f'misc_props must be of {type(FieldMiscProps)} ' - f'but got {type(misc_props)}') - self.__misc_props = misc_props - - if not isinstance(parent_register, BaseReg): - raise TypeError(f'parent_register must be of {type(BaseReg)} ' - f'but got {type(parent_register)}') - - if self.width > self.register_data_width: - raise ValueError('width can not be greater than parent width') - - if self.high > self.register_data_width: - raise ValueError(f'field high bit position {self.high:d} must be less than the ' - f'parent register width ({self.register_data_width:d})') - - if self.low > self.register_data_width: - raise ValueError('field lsb must be less than the parent ' - 'register width') - - if self.high - self.low + 1 != self.width: - raise ValueError('field width defined by lsb and msb does not match' - ' specified width') - - if (self.msb == self.high) and (self.lsb == self.low): - self.__lsb0 = True - self.__msb0 = False - elif (self.msb == self.low) and (self.lsb == self.high): - self.__lsb0 = False - self.__msb0 = True - else: - raise ValueError('msb/lsb are inconsistent with low/high') - - self.__bitmask = 0 - for bit_position in range(self.low, self.high+1): - self.__bitmask |= (1 << bit_position) - - @property - def lsb(self) -> int: - """ - bit position of the least significant bit (lsb) of the field in the - parent register - - Note: - fields can be defined as msb in bit 0 or as lsb in bit 0 - """ - return self.__size_props.lsb - - @property - def msb(self) -> int: - """ - bit position of the most significant bit (msb) of the field in the - parent register - - Note: - fields can be defined as msb in bit 0 or as lsb in bit 0 - """ - return self.__size_props.msb - - @property - def width(self) -> int: - """ - The width of the field in bits - """ - return self.__size_props.width - - @property - def max_value(self) -> int: - """maximum unsigned integer value that can be stored in the field - - For example: - - * 8-bit field returns 0xFF (255) - * 16-bit field returns 0xFFFF (65535) - * 32-bit field returns 0xFFFF_FFFF (4294967295) - - """ - return (2 ** self.width) - 1 - - @property - def high(self) -> int: - """ - low index of the bit range of the field in the - parent register - - Note: - The first bit in the register is bit 0 - """ - return self.__size_props.high - - @property - def low(self) -> int: - """ - low index of the bit range of the field in the - parent register - - Note: - The first bit in the register is bit 0 - """ - return self.__size_props.low - - @property - def bitmask(self) -> int: - """ - The bit mask needed to extract the field from its register - - For example a register field occupying bits 7 to 4 in a 16-bit register - will have a bit mask of 0x00F0 - """ - return self.__bitmask - - @property - def register_data_width(self) -> int: - """ - The width of the register within which the field resides in bits - """ - return self.__parent_register.width - - @property - def inverse_bitmask(self) -> int: - """ - The bitwise inverse of the bitmask needed to extract the field from its - register - - For example a register field occupying bits 7 to 4 in a 16-bit register - will have a inverse bit mask of 0xFF0F - """ - return self.__parent_register.max_value ^ self.bitmask - - @property - def msb0(self) -> bool: - """ - The field can either be lsb0 or msb0 - - Returns: true if msb0 - - """ - return self.__msb0 - - @property - def lsb0(self) -> bool: - """ - The field can either be lsb0 or msb0 - - Returns: true if lsb0 - - """ - return self.__lsb0 - - @property - def default(self) -> Optional[int]: - """ - The default value of the field - - This returns None: - - if the field is not reset. - - if the register resets to a signal value tht can not be determined - """ - return self.__misc_props.default - - @property - def is_volatile(self) -> bool: - """ - The HW volatility of the field - """ - return self.__misc_props.is_volatile - - @property - def __parent_register(self) -> BaseReg: - """ - parent register the field is placed in - """ - # this cast is OK because an explict typing check was done in the __init__ - return cast(BaseReg, self.parent) - - -class _FieldReadOnlyFramework(Field, ABC): - """ - base class for a async or normal read only register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def decode_read_value(self, value: int) -> int: - """ - extracts the field value from a register value, by applying the bit - mask and shift needed - - Args: - value: value to decode, normally read from a register - - Returns: - field value - """ - if not isinstance(value, int): - raise TypeError(f'value must be an int but got {type(value)}') - - if value < 0: - raise ValueError('value to be decoded must be greater ' - 'than or equal to 0') - - if value > self.__parent_register.max_value: - raise ValueError(f'value to bede coded must be less than or equal ' - f'to {self.__parent_register.max_value:d}') - - if self.msb0 is False: - return_value = (value & self.bitmask) >> self.low - else: - return_value = swap_msb_lsb_ordering(value=(value & self.bitmask) >> self.low, - width=self.width) - - return return_value - - @property - def __parent_register(self) -> BaseReg: - """ - parent register the field is placed in - """ - # this cast is OK because an explict typing check was done in the __init__ - return cast(BaseReg, self.parent) - - -class FieldReadOnly(_FieldReadOnlyFramework, ABC): - """ - class for a read only register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def __init__(self, *, - parent_register: ReadableRegister, - size_props: FieldSizeProps, - misc_props: FieldMiscProps, - logger_handle: str, - inst_name: str): - - if not isinstance(parent_register, (RegReadWrite, RegReadOnly)): - raise TypeError(f'size_props must be of {type(RegReadWrite)} or {type(RegReadOnly)} ' - f'but got {type(parent_register)}') - - super().__init__(logger_handle=logger_handle, - size_props=size_props, - misc_props=misc_props, - parent_register=parent_register, - inst_name=inst_name) - - def read(self) -> int: - """ - Reads the register that this field is located in and retries the field - value applying the required masking and shifting - - Returns: - field value - - """ - return self.decode_read_value(self.parent_register.read()) - - @property - def parent_register(self) -> ReadableRegister: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(ReadableRegister, self.parent) - - -class _FieldWriteOnlyFramework(Field, ABC): - """ - class for a write only register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def encode_write_value(self, value: int) -> int: - """ - Check that a value is legal for the field and then encode it in preparation to be written - to the register - - Args: - value: field value - - Returns: - value which can be applied to the register to update the field - - """ - - if not isinstance(value, int): - raise TypeError(f'value must be an int but got {type(value)}') - - if value < 0: - raise ValueError('value to be written to register must be greater ' - 'than or equal to 0') - - if value > self.max_value: - raise ValueError(f'value to be written to register must be less ' - f'than or equal to {self.max_value:d}') - - if self.msb0 is False: - return_value = value << self.low - else: - return_value = swap_msb_lsb_ordering(value=value, width=self.width) << self.low - - return return_value - - -class FieldWriteOnly(_FieldWriteOnlyFramework, ABC): - """ - class for a write only register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def __init__(self, *, - parent_register: WritableRegister, - size_props: FieldSizeProps, - misc_props: FieldMiscProps, - logger_handle: str, - inst_name: str): - - if not isinstance(parent_register, (RegReadWrite, RegWriteOnly)): - raise TypeError(f'size_props must be of {type(RegReadWrite)} or {type(RegWriteOnly)} ' - f'but got {type(parent_register)}') - - super().__init__(logger_handle=logger_handle, - size_props=size_props, - misc_props=misc_props, - parent_register=parent_register, - inst_name=inst_name) - - - - def write(self, value: int) -> None: - """ - The behaviour of this method depends on whether the field is located in - a readable register or not: - - If the register is readable, the method will perform a read-modify-write - on the register updating the field with the value provided - - If the register is not writable all other field values will be written - with zero. - - Args: - value: field value to update to - - """ - - if not isinstance(value, int): - raise TypeError(f'value must be an int but got {type(value)}') - - if value < 0: - raise ValueError('value to be written to register must be greater ' - 'than or equal to 0') - - if value > self.max_value: - raise ValueError(f'value to be written to register must be less ' - f'than or equal to {self.max_value:d}') - - if self.msb0: - value = swap_msb_lsb_ordering(value=value, width=self.width) - - if (self.high == (self.register_data_width - 1)) and (self.low == 0): - # special case where the field occupies the whole register, - # there a straight write can be performed - new_reg_value = value - else: - # do a read, modify write - if isinstance(self.parent_register, RegReadWrite): - reg_value = self.parent_register.read() - masked_reg_value = reg_value & self.inverse_bitmask - new_reg_value = masked_reg_value | (value << self.low) - elif isinstance(self.parent_register, RegWriteOnly): - new_reg_value = value << self.low - else: - raise TypeError('Unhandled parent type') - - self.parent_register.write(new_reg_value) - - @property - def parent_register(self) -> WritableRegister: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(WritableRegister, self.parent) - - -class FieldReadWrite(FieldReadOnly, FieldWriteOnly, ABC): - """ - class for a read/write register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def __init__(self, *, - parent_register: RegReadWrite, - size_props: FieldSizeProps, - misc_props: FieldMiscProps, - logger_handle: str, - inst_name: str): - - if not isinstance(parent_register, RegReadWrite): - raise TypeError(f'size_props must be of {type(RegReadWrite)} ' - f'but got {type(parent_register)}') - - super().__init__(logger_handle=logger_handle, - size_props=size_props, - misc_props=misc_props, - parent_register=parent_register, - inst_name=inst_name) - - @property - def parent_register(self) -> RegReadWrite: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(RegReadWrite, self.parent) - -class FieldAsyncReadOnly(_FieldReadOnlyFramework, ABC): - """ - class for an asynchronous read only register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def __init__(self, *, - parent_register: ReadableAsyncRegister, - size_props: FieldSizeProps, - misc_props: FieldMiscProps, - logger_handle: str, - inst_name: str): - - if not isinstance(parent_register, (RegAsyncReadWrite, RegAsyncReadOnly)): - raise TypeError(f'size_props must be of {type(RegAsyncReadWrite)} or ' - f'{type(RegAsyncReadOnly)} but got {type(parent_register)}') - - super().__init__(logger_handle=logger_handle, - size_props=size_props, - misc_props=misc_props, - parent_register=parent_register, - inst_name=inst_name) - - async def read(self) -> int: # pylint: disable=invalid-overridden-method - """ - Asynchronously reads the register that this field is located in and retries the field - value applying the required masking and shifting - - Returns: - field value - - """ - return self.decode_read_value(await self.parent_register.read()) - - @property - def parent_register(self) -> ReadableAsyncRegister: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(ReadableAsyncRegister, self.parent) - - -class FieldAsyncWriteOnly(_FieldWriteOnlyFramework, ABC): - """ - class for an asynchronous write only register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def __init__(self, *, - parent_register: WritableAsyncRegister, - size_props: FieldSizeProps, - misc_props: FieldMiscProps, - logger_handle: str, - inst_name: str): - - if not isinstance(parent_register, (RegAsyncReadWrite, RegAsyncWriteOnly)): - raise TypeError(f'size_props must be of {type(RegAsyncReadWrite)} or ' - f'{type(RegAsyncWriteOnly)} but got {type(parent_register)}') - - super().__init__(logger_handle=logger_handle, - size_props=size_props, - misc_props=misc_props, - parent_register=parent_register, - inst_name=inst_name) - - async def write(self, value: int) -> None: # pylint: disable=invalid-overridden-method - """ - The behaviour of this method depends on whether the field is located in - a readable register or not: - - If the register is readable, the method will perform an async read-modify-write - on the register updating the field with the value provided - - If the register is not writable all other field values will be asynchronously written - with zero. - - Args: - value: field value to update to - - """ - - if not isinstance(value, int): - raise TypeError(f'value must be an int but got {type(value)}') - - if value < 0: - raise ValueError('value to be written to register must be greater ' - 'than or equal to 0') - - if value > self.max_value: - raise ValueError(f'value to be written to register must be less ' - f'than or equal to {self.max_value:d}') - - if self.msb0: - value = swap_msb_lsb_ordering(value=value, width=self.width) - - if (self.high == (self.register_data_width - 1)) and (self.low == 0): - # special case where the field occupies the whole register, - # there a straight write can be performed - new_reg_value = value - else: - # do a read, modify write - if isinstance(self.parent_register, RegAsyncReadWrite): - reg_value = await self.parent_register.read() - masked_reg_value = reg_value & self.inverse_bitmask - new_reg_value = masked_reg_value | (value << self.low) - elif isinstance(self.parent_register, RegAsyncWriteOnly): - new_reg_value = value << self.low - else: - raise TypeError('Unhandled parent type') - - await self.parent_register.write(new_reg_value) - - @property - def parent_register(self) -> WritableAsyncRegister: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(WritableAsyncRegister, self.parent) - - -class FieldAsyncReadWrite(FieldAsyncReadOnly, FieldAsyncWriteOnly, ABC): - """ - class for an asyncronous read/write register field - - Args: - parent_register: register within which the field resides - size_props: object defining the msb, lsb, high bit, low bit and width - logger_handle: name to be used logging messages associate with this - object - - """ - __slots__ : List[str] = [] - - def __init__(self, *, - parent_register: RegAsyncReadWrite, - size_props: FieldSizeProps, - misc_props: FieldMiscProps, - logger_handle: str, - inst_name: str): - - if not isinstance(parent_register, RegAsyncReadWrite): - raise TypeError(f'size_props must be of {type(RegAsyncReadWrite)} ' - f'but got {type(parent_register)}') - - super().__init__(logger_handle=logger_handle, - size_props=size_props, - misc_props=misc_props, - parent_register=parent_register, - inst_name=inst_name) - - @property - def parent_register(self) -> RegAsyncReadWrite: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(RegAsyncReadWrite, self.parent) - - -class FieldEnum(Field, ABC): - """ - class for a register field with an enumerated value - """ - __slots__: List[str] = [] - - @property - @abstractmethod - def enum_cls(self) -> EnumMeta: - """ - The enumeration class for this field - """ - - @property - def _enum_values(self) -> List[int]: - """ - provide the legal values for the enumeration - """ - return [e.value for e in self.enum_cls] # type: ignore[var-annotated] - - -class FieldEnumReadWrite(FieldReadWrite, FieldEnum, ABC): - """ - class for a read/write register field with an enumerated value - """ - __slots__: List[str] = [] - - @property - def parent_register(self) -> RegReadWrite: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(RegReadWrite, self.parent) - - -class FieldEnumReadOnly(FieldReadOnly, FieldEnum, ABC): - """ - class for a read only register field with an enumerated value - """ - __slots__: List[str] = [] - - -class FieldEnumWriteOnly(FieldWriteOnly, FieldEnum, ABC): - """ - class for a write only register field with an enumerated value - """ - __slots__: List[str] = [] - - -class FieldEnumAsyncReadWrite(FieldAsyncReadWrite, FieldEnum, ABC): - """ - class for an async read/write register field with an enumerated value - """ - __slots__: List[str] = [] - - @property - def parent_register(self) -> RegAsyncReadWrite: - """ - parent register the field is placed in - """ - - # this cast is OK because an explict typing check was done in the __init__ - return cast(RegAsyncReadWrite, self.parent) - - -class FieldEnumAsyncReadOnly(FieldAsyncReadOnly, FieldEnum, ABC): - """ - class for an async read only register field with an enumerated value - """ - __slots__: List[str] = [] - - -class FieldEnumAsyncWriteOnly(FieldAsyncWriteOnly, FieldEnum, ABC): - """ - class for an async write only register field with an enumerated value - """ - __slots__: List[str] = [] diff --git a/src/peakrdl_python/lib/memory.py b/src/peakrdl_python/lib/memory.py index 5c2608a3..00a3beb6 100644 --- a/src/peakrdl_python/lib/memory.py +++ b/src/peakrdl_python/lib/memory.py @@ -40,9 +40,9 @@ if TYPE_CHECKING: - from .register import Reg, RegArray - from .register import ReadableRegister, WritableRegister - from .register import ReadableRegisterArray, WriteableRegisterArray + from .register_and_field import Reg, RegArray + from .register_and_field import ReadableRegister, WritableRegister + from .register_and_field import ReadableRegisterArray, WriteableRegisterArray from .async_memory import AsyncMemoryArray # pylint: disable=duplicate-code diff --git a/src/peakrdl_python/lib/register.py b/src/peakrdl_python/lib/register_and_field.py similarity index 83% rename from src/peakrdl_python/lib/register.py rename to src/peakrdl_python/lib/register_and_field.py index bee0844c..1a092b9d 100644 --- a/src/peakrdl_python/lib/register.py +++ b/src/peakrdl_python/lib/register_and_field.py @@ -17,10 +17,10 @@ This module is intended to distributed as part of automatically generated code by the peakrdl-python tool. It provides a set of classes used by the autogenerated code to represent -registers +registers and fields """ from enum import Enum -from typing import List, Union, Iterator, TYPE_CHECKING, Tuple, cast, Optional, Dict, TypeVar, Type +from typing import List, Union, Iterator, Tuple, cast, Optional, Dict, TypeVar from typing import Generator from abc import ABC, abstractmethod from contextlib import contextmanager @@ -28,14 +28,17 @@ import sys from warnings import warn -from .base import Node, AddressMap, RegFile, NodeArray -from .utility_functions import get_array_typecode, legal_register_width -from .base import AsyncAddressMap, AsyncRegFile -from .memory import MemoryReadOnly, MemoryWriteOnly, MemoryReadWrite, \ - BaseMemory, Memory, ReadableMemory, WritableMemory +from .base import AddressMap, RegFile +from .utility_functions import get_array_typecode +from .utility_functions import swap_msb_lsb_ordering +from .memory import MemoryReadOnly, MemoryWriteOnly, MemoryReadWrite, Memory, \ + ReadableMemory, WritableMemory from .memory import MemoryReadOnlyLegacy, MemoryWriteOnlyLegacy, MemoryReadWriteLegacy from .memory import ReadableMemoryLegacy, WritableMemoryLegacy from .callbacks import NormalCallbackSet, NormalCallbackSetLegacy +from .base_register import BaseReg, BaseRegArray, RegisterWriteVerifyError +from .base_field import FieldEnum, FieldSizeProps, FieldMiscProps, \ + _FieldReadOnlyFramework, _FieldWriteOnlyFramework # pylint: disable=duplicate-code if sys.version_info >= (3, 11): @@ -44,114 +47,17 @@ from typing_extensions import Self # pylint: enable=duplicate-code -if TYPE_CHECKING: - from .fields import FieldReadOnly, FieldWriteOnly, FieldReadWrite +# pylint: disable=duplicate-code +if sys.version_info >= (3, 10): + # type guarding was introduced in python 3.10 + from typing import TypeGuard +else: + from typing_extensions import TypeGuard +# pylint: enable=duplicate-code # pylint: disable=redefined-slots-in-subclass,too-many-lines -class RegisterWriteVerifyError(Exception): - """ - Exception that occurs when the read after a write does not match the expected value - """ - - -class BaseReg(Node, ABC): - """ - base class of register wrappers - - Note: - It is not expected that this class will be instantiated under normal - circumstances however, it is useful for type checking - """ - - __slots__: List[str] = ['__width', '__accesswidth'] - - # pylint: disable=too-many-arguments,duplicate-code - def __init__(self, *, - address: int, - width: int, - accesswidth: int, - logger_handle: str, - inst_name: str, - parent: Union[AddressMap, AsyncAddressMap, RegFile, AsyncRegFile, BaseMemory, - 'BaseRegArray']): - - super().__init__(address=address, - logger_handle=logger_handle, - inst_name=inst_name, - parent=parent) - if not isinstance(width, int): - raise TypeError(f'width should be int but got {(type(width))}') - if not legal_register_width(width_in_bits=width): - raise ValueError(f'Unsupported register width {width:d}') - self.__width = width - if not isinstance(accesswidth, int): - raise TypeError(f'accesswidth should be int but got {(type(accesswidth))}') - if not legal_register_width(width_in_bits=accesswidth): - raise ValueError(f'Unsupported access width {accesswidth:d}') - self.__accesswidth = accesswidth - # pylint: enable=too-many-arguments,duplicate-code - - @property - def max_value(self) -> int: - """ - maximum unsigned integer value that can be stored in the register - - For example: - - * 8-bit register returns 0xFF (255) - * 16-bit register returns 0xFFFF (65535) - * 32-bit register returns 0xFFFF_FFFF (4294967295) - - """ - return (2 ** self.width) - 1 - - def _validate_data(self, data: int) -> None: - """ - Check that the data parameter is of valid type and range - """ - if not isinstance(data, int): - raise TypeError(f'data should be an int got {type(data)}') - - if data > self.max_value: - raise ValueError('data out of range') - - if data < 0: - raise ValueError('data out of range') - - @property - def width(self) -> int: - """ - The width of the register in bits, this uses the `regwidth` systemRDL property - """ - return self.__width - - @property - def accesswidth(self) -> int: - """ - The access width of the register in bits, this uses the `accesswidth` systemRDL property - """ - return self.__accesswidth - - @property - def size(self) -> int: - """ - Total Number of bytes of address the node occupies - """ - return self.__width >> 3 - - @property - @abstractmethod - def _is_readable(self) -> bool: - ... - - @property - @abstractmethod - def _is_writeable(self) -> bool: - ... - - class Reg(BaseReg, ABC): """ base class of non-async register wrappers @@ -204,106 +110,7 @@ def fields(self) -> Iterator[Union['FieldReadOnly', 'FieldWriteOnly', 'FieldRead # pylint: disable-next=invalid-name -BaseRegArrayElementType= TypeVar('BaseRegArrayElementType', bound=BaseReg) - - -class BaseRegArray(NodeArray[BaseRegArrayElementType], ABC): - """ - base class of register array wrappers (async and non-async) - - Note: - It is not expected that this class will be instantiated under normal - circumstances however, it is useful for type checking - """ - # pylint: disable=too-many-arguments,duplicate-code - - __slots__: List[str] = ['__width', '__accesswidth'] - - def __init__(self, *, - logger_handle: str, inst_name: str, - parent: Union[AddressMap, AsyncAddressMap, RegFile, AsyncRegFile, BaseMemory], - width: int, - accesswidth: int, - address: int, - stride: int, - dimensions: Tuple[int, ...], - elements: Optional[Dict[Tuple[int, ...], BaseRegArrayElementType]] = None): - - if not isinstance(width, int): - raise TypeError(f'width should be int but got {(type(width))}') - if not legal_register_width(width_in_bits=width): - raise ValueError(f'Unsupported register width {width:d}') - self.__width = width - if not isinstance(accesswidth, int): - raise TypeError(f'accesswidth should be int but got {(type(accesswidth))}') - if not legal_register_width(width_in_bits=accesswidth): - raise ValueError(f'Unsupported access width {accesswidth:d}') - self.__accesswidth = accesswidth - - if not issubclass(self._element_datatype, BaseReg): - raise TypeError(f'{self._element_datatype}') - - super().__init__(logger_handle=logger_handle, inst_name=inst_name, - parent=parent, address=address, - stride=stride, dimensions=dimensions, elements=elements) - - @property - def width(self) -> int: - """ - The width of the register in bits, this uses the `regwidth` systemRDL property - """ - return self.__width - - @property - def accesswidth(self) -> int: - """ - The access width of the register in bits, this uses the `accesswidth` systemRDL property - """ - return self.__accesswidth - - def _build_element(self, indices: Tuple[int, ...]) -> BaseRegArrayElementType: - - return self._element_datatype( - logger_handle=self._build_element_logger_handle(indices=indices), - address=self._address_calculator(indices), - inst_name=self._build_element_inst_name(indices=indices), - width=self.width, - accesswidth=self.accesswidth, - parent=self) - - def _sub_instance(self, elements: Dict[Tuple[int, ...], BaseRegArrayElementType]) ->\ - NodeArray[BaseRegArrayElementType]: - if not isinstance(self.parent, (AddressMap, AsyncAddressMap, RegFile, - AsyncRegFile, BaseMemory)): - raise RuntimeError('Parent of a Node Array must be Node') - return self.__class__(logger_handle=self._logger.name, - inst_name=self.inst_name, - parent=self.parent, - address=self.address, - width=self.width, - accesswidth=self.accesswidth, - stride=self.stride, - dimensions=self.dimensions, - elements=elements) - - @property - @abstractmethod - def _element_datatype(self) -> Type[BaseRegArrayElementType]: - ... - - @property - @abstractmethod - def _is_readable(self) -> bool: - ... - - @property - @abstractmethod - def _is_writeable(self) -> bool: - ... - - -# pylint: disable-next=invalid-name -RegArrayElementType= TypeVar('RegArrayElementType', bound=Reg) +RegArrayElementType= TypeVar('RegArrayElementType', bound=BaseReg) class RegArray(BaseRegArray, ABC): @@ -379,7 +186,7 @@ def __block_read_legacy(self) -> Array: if self.__register_address_array is None: raise RuntimeError('This address array has not be initialised') - for entry,address in enumerate(self.__register_address_array): + for entry, address in enumerate(self.__register_address_array): # python 3.7 doesn't have the callback defined as protocol so mypy doesn't # recognise the arguments in the call back functions @@ -670,7 +477,6 @@ class RegReadOnly(Reg, ABC): class for a read only register Args: - callbacks: set of callback to be used for accessing the hardware or simulator address: address of the register width: width of the register in bits accesswidth: minimum access width of the register in bits @@ -743,11 +549,15 @@ def read(self) -> int: raise RuntimeError('This function does not have a useable callback') @property - @abstractmethod def readable_fields(self) -> Iterator[Union['FieldReadOnly', 'FieldReadWrite']]: """ generator that produces has all the readable fields within the register """ + def is_readable(field: Union['FieldReadOnly', 'FieldWriteOnly', 'FieldReadWrite']) -> \ + TypeGuard[Union['FieldReadOnly', 'FieldReadWrite']]: + return isinstance(field, (FieldReadOnly, FieldReadWrite)) + + return filter(is_readable, self.fields) def read_fields(self) -> Dict['str', Union[bool, Enum, int]]: """ @@ -839,11 +649,15 @@ def write(self, data: int) -> None: raise RuntimeError('This function does not have a useable callback') @property - @abstractmethod def writable_fields(self) -> Iterator[Union['FieldWriteOnly', 'FieldReadWrite']]: """ generator that produces has all the readable fields within the register """ + def is_writable(field: Union['FieldReadOnly', 'FieldWriteOnly', 'FieldReadWrite']) -> \ + TypeGuard[Union['FieldWriteOnly', 'FieldReadWrite']]: + return isinstance(field, (FieldWriteOnly, FieldReadWrite)) + + return filter(is_writable, self.fields) @abstractmethod def write_fields(self, **kwargs) -> None: # type: ignore[no-untyped-def] @@ -1123,7 +937,7 @@ def single_write(self) -> \ field operations """ with self._cached_access(verify=False, skip_write=False, - skip_initial_read=True) as reg_array: + skip_initial_read=True) as reg_array: yield reg_array @property @@ -1195,3 +1009,205 @@ def _is_writeable(self) -> bool: ReadableRegisterArray = Union[RegReadOnlyArray, RegReadWriteArray] WriteableRegisterArray = Union[RegWriteOnlyArray, RegReadWriteArray] + + +class FieldReadOnly(_FieldReadOnlyFramework, ABC): + """ + class for a read only register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__: List[str] = [] + + def __init__(self, *, + parent_register: ReadableRegister, + size_props: FieldSizeProps, + misc_props: FieldMiscProps, + logger_handle: str, + inst_name: str): + + if not isinstance(parent_register, (RegReadWrite, RegReadOnly)): + raise TypeError(f'size_props must be of {type(RegReadWrite)} or {type(RegReadOnly)} ' + f'but got {type(parent_register)}') + + # pylint: disable=duplicate-code + super().__init__(logger_handle=logger_handle, + size_props=size_props, + misc_props=misc_props, + parent_register=parent_register, + inst_name=inst_name) + # pylint: enable=duplicate-code + + def read(self) -> int: + """ + Reads the register that this field is located in and retries the field + value applying the required masking and shifting + + Returns: + field value + + """ + return self.decode_read_value(self.parent_register.read()) + + @property + def parent_register(self) -> ReadableRegister: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(ReadableRegister, self.parent) + + +class FieldWriteOnly(_FieldWriteOnlyFramework, ABC): + """ + class for a write only register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__: List[str] = [] + + def __init__(self, *, + parent_register: WritableRegister, + size_props: FieldSizeProps, + misc_props: FieldMiscProps, + logger_handle: str, + inst_name: str): + + if not isinstance(parent_register, (RegReadWrite, RegWriteOnly)): + raise TypeError(f'size_props must be of {type(RegReadWrite)} or {type(RegWriteOnly)} ' + f'but got {type(parent_register)}') + + # pylint: disable=duplicate-code + super().__init__(logger_handle=logger_handle, + size_props=size_props, + misc_props=misc_props, + parent_register=parent_register, + inst_name=inst_name) + # pylint: enable=duplicate-code + + def write(self, value: int) -> None: + """ + The behaviour of this method depends on whether the field is located in + a readable register or not: + + If the register is readable, the method will perform a read-modify-write + on the register updating the field with the value provided + + If the register is not writable all other field values will be written + with zero. + + Args: + value: field value to update to + + """ + self._write_value_checks(value=value) + + if self.msb0: + value = swap_msb_lsb_ordering(value=value, width=self.width) + + if (self.high == (self.register_data_width - 1)) and (self.low == 0): + # special case where the field occupies the whole register, + # there a straight write can be performed + new_reg_value = value + else: + # do a read, modify write + if isinstance(self.parent_register, RegReadWrite): + reg_value = self.parent_register.read() + masked_reg_value = reg_value & self.inverse_bitmask + new_reg_value = masked_reg_value | (value << self.low) + elif isinstance(self.parent_register, RegWriteOnly): + new_reg_value = value << self.low + else: + raise TypeError('Unhandled parent type') + + self.parent_register.write(new_reg_value) + + @property + def parent_register(self) -> WritableRegister: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(WritableRegister, self.parent) + + +class FieldReadWrite(FieldReadOnly, FieldWriteOnly, ABC): + """ + class for a read/write register field + + Args: + parent_register: register within which the field resides + size_props: object defining the msb, lsb, high bit, low bit and width + logger_handle: name to be used logging messages associate with this + object + + """ + __slots__: List[str] = [] + + def __init__(self, *, + parent_register: RegReadWrite, + size_props: FieldSizeProps, + misc_props: FieldMiscProps, + logger_handle: str, + inst_name: str): + + if not isinstance(parent_register, RegReadWrite): + raise TypeError(f'size_props must be of {type(RegReadWrite)} ' + f'but got {type(parent_register)}') + + super().__init__(logger_handle=logger_handle, + size_props=size_props, + misc_props=misc_props, + parent_register=parent_register, + inst_name=inst_name) + + @property + def parent_register(self) -> RegReadWrite: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(RegReadWrite, self.parent) + + +class FieldEnumReadWrite(FieldReadWrite, FieldEnum, ABC): + """ + class for a read/write register field with an enumerated value + """ + __slots__: List[str] = [] + + @property + def parent_register(self) -> RegReadWrite: + """ + parent register the field is placed in + """ + + # this cast is OK because an explict typing check was done in the __init__ + return cast(RegReadWrite, self.parent) + + +class FieldEnumReadOnly(FieldReadOnly, FieldEnum, ABC): + """ + class for a read only register field with an enumerated value + """ + __slots__: List[str] = [] + + +class FieldEnumWriteOnly(FieldWriteOnly, FieldEnum, ABC): + """ + class for a write only register field with an enumerated value + """ + __slots__: List[str] = [] diff --git a/src/peakrdl_python/templates/addrmap_register.py.jinja b/src/peakrdl_python/templates/addrmap_register.py.jinja index 4e1bdce6..9b6b61ec 100644 --- a/src/peakrdl_python/templates/addrmap_register.py.jinja +++ b/src/peakrdl_python/templates/addrmap_register.py.jinja @@ -85,32 +85,8 @@ class {{get_fully_qualified_type_name(node)}}_cls(Reg{% if asyncoutput %}Async{% # Empty generator in case there are no children of this type if False: yield - {% if node.has_sw_readable %} - @property - def readable_fields(self) -> Iterator[Union[Field{% if asyncoutput %}Async{% endif %}ReadOnly, Field{% if asyncoutput %}Async{% endif %}ReadWrite]]: - """ - generator that produces has all the readable fields within the register - """ - {% for child_node in get_reg_readable_fields(node, hide_node_func) -%} - yield self.{{safe_node_name(child_node)}} - {% endfor %} - # Empty generator in case there are no children of this type - if False: yield - {% endif %} - {% if node.has_sw_writable %} - @property - def writable_fields(self) -> Iterator[Union[Field{% if asyncoutput %}Async{% endif %}WriteOnly, Field{% if asyncoutput %}Async{% endif %}ReadWrite]]: - """ - generator that produces has all the writable fields within the register - """ - {% for child_node in get_reg_writable_fields(node, hide_node_func) -%} - yield self.{{safe_node_name(child_node)}} - {% endfor %} - # Empty generator in case there are no children of this type - if False: yield - - {% if not node.has_sw_readable %} + {% if node.has_sw_writable and not node.has_sw_readable %} {# if the register has no readable components, all the fields must be writen as one #} {% if asyncoutput %}async {% endif %}def write_fields(self, {%- for child_node in node.fields() -%} {{safe_node_name(child_node)}} : {%- if 'encode' in child_node.list_properties() %}{{get_fully_qualified_enum_type(child_node.get_property('encode'), top_node.parent, child_node, hide_node_func)}}_enumcls{% else %}int{% endif %}{%- if not loop.last -%},{%- endif -%}{%- endfor -%}) -> None: # type: ignore[override] """ @@ -124,7 +100,6 @@ class {{get_fully_qualified_type_name(node)}}_cls(Reg{% if asyncoutput %}Async{% {% if asyncoutput %}await {% endif %}self.write(reg_value) {% endif %} - {% endif %} # build the properties for the fields {% for child_node in get_reg_fields(node, hide_node_func) %}