Skip to content

Commit 5dd0505

Browse files
authored
Merge pull request #260 from krcb197/255-use-the-compiler-overlap-features
Improve the support for overlapping register and fields
2 parents 3b367d1 + 115203f commit 5dd0505

12 files changed

Lines changed: 485 additions & 93 deletions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ dynamic = ["version"]
88
# Callable is broken in python 3.9.0 and 3.9.1 so these need to be excluded
99
requires-python = ">=3.9.2"
1010
dependencies = [
11-
"systemrdl-compiler>=1.29.3", # this is needed to make sure all the latest type checking
11+
"systemrdl-compiler>=1.31.0", # this is needed for the latest overlappign register features
1212
"jinja2",
1313
"typing-extensions;python_version<'3.11'",
1414
# the batched fucntion was adopted into the standard python library at 3.13

src/peakrdl_python/__about__.py

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

src/peakrdl_python/exporter.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
get_memory_max_entry_value_hex_string, get_memory_width_bytes, \
4747
get_field_default_value, get_enum_values, get_properties_to_include, \
4848
HideNodeCallback, hide_based_on_property, \
49-
full_slice_accessor, ShowUDPCallback
49+
full_slice_accessor, ShowUDPCallback, simulator_field_definition
5050
from .unique_component_iterator import UniqueComponents
5151
from .unique_component_iterator import PeakRDLPythonUniqueRegisterComponents
5252
from .unique_component_iterator import PeakRDLPythonUniqueMemoryComponents
@@ -675,16 +675,17 @@ def __export_simulator(self, *,
675675
raise TypeError(f'node should be a register, got {type(node)}')
676676
reg_addr = node.absolute_address
677677
if reg_addr in reg_dict:
678+
if not node.has_overlaps:
679+
raise RuntimeError('A non-overlaping node should not have a duplicate address')
678680
existing_entry = reg_dict[reg_addr]
679-
# if the entry is already list simply append to it
680-
if isinstance(existing_entry, list):
681-
existing_entry.append(node)
682-
elif isinstance(existing_entry, RegNode):
683-
reg_dict[reg_addr] = [existing_entry, node]
684-
else:
685-
raise TypeError(f'exiting entry of unexpected type: {type(existing_entry)}')
681+
if not isinstance(existing_entry, list):
682+
raise RuntimeError('A overlapping entry should have been made as list')
683+
existing_entry.append(node)
686684
else:
687-
reg_dict[reg_addr] = node
685+
if node.has_overlaps:
686+
reg_dict[reg_addr] = [node,]
687+
else:
688+
reg_dict[reg_addr] = node
688689

689690

690691
context = {
@@ -696,7 +697,8 @@ def __export_simulator(self, *,
696697
'asyncoutput': asyncoutput,
697698
'skip_lib_copy': skip_lib_copy,
698699
'legacy_block_access': legacy_block_access,
699-
'list': list
700+
'list': list,
701+
'simulator_field_definition': simulator_field_definition,
700702
}
701703

702704
module_name = top_block.inst_name

src/peakrdl_python/sim_lib/field.py

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"""
2121
from dataclasses import dataclass
2222
from typing import TYPE_CHECKING
23-
from typing import Optional
23+
from typing import Optional, Union
24+
from enum import Enum, auto
2425

2526
from ..lib.utility_functions import swap_msb_lsb_ordering
2627

@@ -33,19 +34,8 @@
3334

3435
# pylint: disable=too-many-instance-attributes,too-many-arguments
3536

36-
37-
@dataclass
38-
class FieldDefinition:
39-
"""
40-
Class for an entry in the list of memories in the simulator
41-
"""
42-
high: int
43-
low: int
44-
msb: int
45-
lsb: int
46-
inst_name: str
47-
48-
class Field(Base):
37+
#pylint:disable-next=too-few-public-methods
38+
class _FieldBase(Base):
4939
"""
5040
class for all fields
5141
@@ -58,7 +48,7 @@ class for all fields
5848
'__bitmask', '__inverse_bitmask',
5949
'__msb0', '__lsb0',
6050
'__parent_register','__parent_width',
61-
'__read_callback', '__write_callback']
51+
'_read_callback', '_write_callback']
6252

6353
def __init__(self, *, low: int, high: int, msb: int, lsb: int,
6454
parent_register: 'BaseRegister', parent_width: int, inst_name: str):
@@ -94,30 +84,8 @@ def __init__(self, *, low: int, high: int, msb: int, lsb: int,
9484
parent_max_value = (2 ** parent_width) - 1
9585
self.__inverse_bitmask = parent_max_value ^ self.__bitmask
9686

97-
self.__read_callback: Optional[FieldReadCallback] = None
98-
self.__write_callback: Optional[FieldWriteCallback] = None
99-
100-
@property
101-
def read_callback(self) -> Optional[FieldReadCallback]:
102-
"""
103-
Callback made during each read operation
104-
"""
105-
return self.__read_callback
106-
107-
@read_callback.setter
108-
def read_callback(self, callback: Optional[FieldReadCallback]) -> None:
109-
self.__read_callback = callback
110-
111-
@property
112-
def write_callback(self) -> Optional[FieldWriteCallback]:
113-
"""
114-
Callback made during each write operation
115-
"""
116-
return self.__write_callback
117-
118-
@write_callback.setter
119-
def write_callback(self, callback: Optional[FieldWriteCallback]) -> None:
120-
self.__write_callback = callback
87+
self._write_callback: Optional[FieldWriteCallback] = None
88+
self._read_callback: Optional[FieldReadCallback] = None
12189

12290
@property
12391
def __width(self) -> int:
@@ -149,6 +117,89 @@ def value(self, value: int) -> None:
149117
self.__parent_register.value = value
150118
else:
151119
# do a read, modify write
152-
reg_value = self.__parent_register.read()
120+
reg_value = self.__parent_register.value
153121
self.__parent_register.value = (reg_value & self.__inverse_bitmask) | \
154122
(value << self.__low)
123+
124+
class ReadOnlyField(_FieldBase):
125+
"""
126+
Simulation of a read-only field
127+
"""
128+
__slots__: list[str] = []
129+
130+
@property
131+
def read_callback(self) -> Optional[FieldReadCallback]:
132+
"""
133+
Callback made during each read operation
134+
"""
135+
return self._read_callback
136+
137+
@read_callback.setter
138+
def read_callback(self, callback: Optional[FieldReadCallback]) -> None:
139+
self._read_callback = callback
140+
141+
142+
class WriteOnlyField(_FieldBase):
143+
"""
144+
Simulation of a write-only field
145+
"""
146+
__slots__:list[str] = []
147+
148+
149+
150+
151+
@property
152+
def write_callback(self) -> Optional[FieldWriteCallback]:
153+
"""
154+
Callback made during each write operation
155+
"""
156+
return self._write_callback
157+
158+
@write_callback.setter
159+
def write_callback(self, callback: Optional[FieldWriteCallback]) -> None:
160+
self._write_callback = callback
161+
162+
class ReadWriteField(ReadOnlyField, WriteOnlyField):
163+
"""
164+
Simulation of a read/write field
165+
"""
166+
__slots__:list[str] = []
167+
168+
169+
Field = Union[ReadOnlyField, WriteOnlyField, ReadWriteField]
170+
171+
172+
class FieldType(Enum):
173+
"""
174+
types of register field to be simulated based on the software access
175+
"""
176+
READONLY = auto()
177+
WRITEONLY = auto()
178+
READWRITE = auto()
179+
180+
181+
_class_type_map: dict[FieldType, Union[type[ReadOnlyField],
182+
type[WriteOnlyField],
183+
type[ReadWriteField]]] = {
184+
FieldType.READONLY: ReadOnlyField,
185+
FieldType.WRITEONLY: WriteOnlyField,
186+
FieldType.READWRITE: ReadWriteField}
187+
188+
@dataclass
189+
class FieldDefinition:
190+
"""
191+
Class for an entry in the list of memories in the simulator
192+
"""
193+
high: int
194+
low: int
195+
msb: int
196+
lsb: int
197+
inst_name: str
198+
field_type: FieldType
199+
200+
@property
201+
def class_type(self) -> Union[type[ReadOnlyField], type[WriteOnlyField], type[ReadWriteField]]:
202+
"""
203+
Classtype to be used base on the fieldtype
204+
"""
205+
return _class_type_map[self.field_type]

src/peakrdl_python/sim_lib/register.py

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,23 @@
1919
peakrdl-python tool. It provides a set of base classes used by the autogenerated code
2020
"""
2121
from abc import ABC, abstractmethod
22-
from typing import Optional
22+
from typing import Optional, Union
23+
import sys
24+
from collections.abc import Iterator
2325

2426
from .memory import Memory
2527
from .base import Base
26-
from .field import FieldDefinition, Field
28+
from .field import FieldDefinition, ReadOnlyField, WriteOnlyField, ReadWriteField
2729
from ._callbacks import RegisterReadCallback,RegisterWriteCallback
2830

31+
# pylint: disable=duplicate-code
32+
if sys.version_info >= (3, 10):
33+
# type guarding was introduced in python 3.10
34+
from typing import TypeGuard
35+
else:
36+
from typing_extensions import TypeGuard
37+
# pylint: enable=duplicate-code
38+
2939
# pylint: disable=too-many-arguments
3040

3141
class BaseRegister(Base, ABC):
@@ -46,13 +56,13 @@ def __init__(self, *,
4656
self._width = width
4757
self._readable = readable
4858
self._writable = writable
49-
self.fields = [Field(low=field_def.low,
50-
high=field_def.high,
51-
msb=field_def.msb,
52-
lsb=field_def.lsb,
53-
inst_name=field_def.inst_name,
54-
parent_register=self,
55-
parent_width=width) for field_def in fields]
59+
self.fields = tuple(field_def.class_type(low=field_def.low,
60+
high=field_def.high,
61+
msb=field_def.msb,
62+
lsb=field_def.lsb,
63+
inst_name=field_def.inst_name,
64+
parent_register=self,
65+
parent_width=width) for field_def in fields)
5666
self.__read_callback: Optional[RegisterReadCallback] = None
5767
self.__write_callback: Optional[RegisterWriteCallback] = None
5868

@@ -85,7 +95,7 @@ def _action_read_callback(self) -> None:
8595
# pylint: disable-next=not-callable
8696
self.read_callback(value=self.value)
8797

88-
for field in self.fields:
98+
for field in self.readable_fields:
8999
if field.read_callback is not None:
90100
field.read_callback(value=field.value)
91101

@@ -96,7 +106,7 @@ def _action_write_callback(self) -> None:
96106
# pylint: disable-next=not-callable
97107
self.write_callback(value=self.value)
98108

99-
for field in self.fields:
109+
for field in self.writable_fields:
100110
if field.write_callback is not None:
101111
field.write_callback(value=field.value)
102112

@@ -135,6 +145,28 @@ def value(self) -> int:
135145
def value(self, value:int) -> None:
136146
...
137147

148+
@property
149+
def writable_fields(self) -> Iterator[Union[WriteOnlyField, ReadWriteField]]:
150+
"""
151+
Iterator of the Writable fields
152+
"""
153+
def is_writable(field: Union[WriteOnlyField, ReadWriteField, ReadOnlyField]) -> \
154+
TypeGuard[Union[WriteOnlyField, ReadWriteField]]:
155+
return isinstance(field, (WriteOnlyField, ReadWriteField))
156+
157+
return filter(is_writable, self.fields)
158+
159+
@property
160+
def readable_fields(self) -> Iterator[Union[ReadOnlyField, ReadWriteField]]:
161+
"""
162+
Iterator of the Readable fields
163+
"""
164+
def is_readable(field: Union[WriteOnlyField, ReadWriteField, ReadOnlyField]) -> \
165+
TypeGuard[Union[ReadOnlyField, ReadWriteField]]:
166+
return isinstance(field, (ReadOnlyField, ReadWriteField))
167+
168+
return filter(is_readable, self.fields)
169+
138170

139171
class Register(BaseRegister):
140172
"""

src/peakrdl_python/sim_lib/simulator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ def memory_for_address_with_exception(self, address: int) -> MemoryEntry:
137137
return memory_entry
138138

139139
def _read(self, addr: int,
140-
width: int, # pylint: disable=unused-argument
141-
accesswidth: int) -> int: # pylint: disable=unused-argument
140+
width: int, # pylint: disable=unused-argument
141+
accesswidth: int) -> int: # pylint: disable=unused-argument
142142
"""
143143
function to simulate a device read, this needs to match the protocol for the callbacks
144144
"""

src/peakrdl_python/systemrdl_node_utility_functions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from systemrdl.rdltypes.user_enum import UserEnumMeta
3636

3737
from .lib.utility_functions import calculate_bitmask
38+
from .sim_lib.field import FieldType
3839

3940
class HideNodeCallback(Protocol):
4041
"""
@@ -480,3 +481,22 @@ def full_slice_accessor(node: AddressableNode) -> str:
480481
if dimensions is None:
481482
raise RuntimeError('array node should have dimensions')
482483
return '[' + ','.join([':' for _ in range(len(dimensions))]) + ']'
484+
485+
def simulator_field_definition(node: FieldNode) -> str:
486+
"""
487+
Based on whether the field is readable and/or writeable, returns the value of the FieldType
488+
to use
489+
"""
490+
if not isinstance(node, FieldNode):
491+
raise TypeError(f'This should only be called on an FieldNode, got {type(node)}')
492+
493+
if node.is_sw_readable and node.is_sw_writable:
494+
return FieldType.READWRITE.name
495+
496+
if node.is_sw_readable and not node.is_sw_writable:
497+
return FieldType.READONLY.name
498+
499+
if not node.is_sw_readable and node.is_sw_writable:
500+
return FieldType.WRITEONLY.name
501+
502+
raise RuntimeError('Encountered a field node that was neither readable or writable')

src/peakrdl_python/templates/addrmap_simulation.py.jinja

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ from typing import Union
2727
from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.register import Register, MemoryRegister
2828
from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.memory import Memory
2929
from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.simulator import MemoryEntry
30-
from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.field import FieldDefinition
30+
from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.field import FieldDefinition, FieldType
3131
{% if asyncoutput -%}
3232
from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.simulator import AsyncSimulator{% if legacy_block_access %}Legacy{% endif %} as Simulator{% if legacy_block_access %}Legacy{% endif %}
3333
{% else %}
@@ -39,7 +39,7 @@ from {{ peakrdl_python_sim_lib(depth=lib_depth) }}.simulator import Simulator{%
3939
fields=[
4040

4141
{%- for field in node.fields() -%}
42-
FieldDefinition(high={{field.high}}, low={{field.low}}, msb={{field.msb}}, lsb={{field.lsb}}, inst_name='{{field.inst_name}}'),
42+
FieldDefinition(high={{field.high}}, low={{field.low}}, msb={{field.msb}}, lsb={{field.lsb}}, inst_name='{{field.inst_name}}', field_type=FieldType.{{simulator_field_definition(field)}}),
4343
{%- endfor %}
4444
])
4545
{%- endmacro %}

0 commit comments

Comments
 (0)