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) %}