Skip to content

Commit 20e9498

Browse files
authored
Merge pull request #237 from krcb197/234-optimised-test_int_field_read_and_write
234 optimised `test_int_field_read_and_write`
2 parents b945003 + 6679b86 commit 20e9498

11 files changed

Lines changed: 525 additions & 217 deletions

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.1.0"
20+
__version__ = "3.0.0rc1"

src/peakrdl_python/_deploy_package.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,10 @@ class GeneratedPackage(PythonPackage):
154154
Args:
155155
include_tests (bool): include the tests package
156156
"""
157+
# pylint:disable=too-many-instance-attributes
157158
template_lib_package = PythonPackage(Path(__file__).parent / 'lib')
158159
template_sim_lib_package = PythonPackage(Path(__file__).parent / 'sim_lib')
160+
template_lib_test_package = PythonPackage(Path(__file__).parent / 'lib_test')
159161

160162
def __init__(self, path: str, package_name: str, include_tests: bool, include_libraries: bool):
161163
super().__init__(Path(path) / package_name)
@@ -169,6 +171,9 @@ def __init__(self, path: str, package_name: str, include_tests: bool, include_li
169171
self.reg_model = _GeneratedRegModelPackage(self.child_path('reg_model'))
170172

171173
if include_tests:
174+
if include_libraries:
175+
self.lib_test = self.child_ref_package('lib_test',
176+
self.template_lib_test_package)
172177
self.tests = self.child_package('tests')
173178

174179
if include_libraries:
@@ -200,4 +205,6 @@ def create_empty_package(self, cleanup: bool) -> None:
200205
if self._include_libraries:
201206
self.lib.create_empty_package(cleanup=cleanup)
202207
self.sim_lib.create_empty_package(cleanup=cleanup)
208+
if self._include_tests:
209+
self.lib_test.create_empty_package(cleanup=cleanup)
203210
self.sim.create_empty_package(cleanup=cleanup)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
peakrdl-python is a tool to generate Python Register Access Layer (RAL) from SystemRDL
3+
Copyright (C) 2021 - 2025
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
This package is intended to distributed as part of automatically generated code by the PeakRDL
19+
Python tool. It provides a set of base test classes to reduce the size of the auto generated code
20+
"""
21+
from .base_reg_test_class import LibTestBase
22+
from .async_reg_base_test_class import AsyncLibTestBase
23+
24+
from .utilities import reverse_bits
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"""
2+
peakrdl-python is a tool to generate Python Register Access Layer (RAL) from SystemRDL
3+
Copyright (C) 2021 - 2025
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
This package is intended to distributed as part of automatically generated code by the PeakRDL
19+
Python tool. It provide the base class for the autogenerated tests
20+
"""
21+
# this module is very similar to the non-async version, a lot of code has been put into common
22+
# methods but it did not make sense to do everything as it would destroy readability
23+
# pylint:disable=duplicate-code
24+
25+
import unittest
26+
from abc import ABC
27+
from typing import Union
28+
import random
29+
from unittest.mock import patch
30+
from itertools import product
31+
32+
from ..lib import FieldAsyncReadOnly, FieldAsyncWriteOnly, FieldAsyncReadWrite
33+
from ..sim_lib.dummy_callbacks import async_dummy_read
34+
from ..sim_lib.dummy_callbacks import async_dummy_write
35+
36+
37+
from .utilities import reverse_bits, expected_reg_write_data
38+
39+
class AsyncLibTestBase(unittest.IsolatedAsyncioTestCase, ABC):
40+
"""
41+
Base Test class for the autogenerated register test when in async mode
42+
"""
43+
44+
# The following may look odd by a second layer of indirection is required to effectively patch
45+
# the read and write within tests
46+
47+
# pylint:disable=missing-function-docstring
48+
49+
async def outer_read_callback(self, addr: int, width: int, accesswidth: int) -> int:
50+
return await self.read_callback(addr=addr,
51+
width=width,
52+
accesswidth=accesswidth)
53+
54+
async def read_callback(self, addr: int, width: int, accesswidth: int) -> int:
55+
return await async_dummy_read(addr=addr,
56+
width=width,
57+
accesswidth=accesswidth)
58+
59+
async def outer_write_callback(self, addr: int,
60+
width: int, accesswidth: int,
61+
data: int) -> None:
62+
return await self.write_callback(addr=addr,
63+
width=width,
64+
accesswidth=accesswidth,
65+
data=data)
66+
67+
async def write_callback(self, addr: int, width: int, accesswidth: int, data: int) -> None:
68+
return await async_dummy_write(addr=addr,
69+
width=width,
70+
accesswidth=accesswidth,
71+
data=data)
72+
73+
# pylint:enable=missing-function-docstring
74+
75+
76+
async def _single_int_field_read_and_write_test(
77+
self,
78+
fut: Union[FieldAsyncReadOnly, FieldAsyncWriteOnly, FieldAsyncReadWrite],
79+
is_sw_readable: bool,
80+
is_sw_writable: bool) -> None:
81+
82+
# the lsb, msb, high, low, bitmask, inv_bitmask and max_value are all checked as part of
83+
# `test_field_properties` so do no need to checked here. Similarly, the properties of
84+
# parent register are checked as part of `test_register_properties`
85+
86+
if is_sw_readable:
87+
if not isinstance(fut, (FieldAsyncReadOnly, FieldAsyncReadWrite)):
88+
raise TypeError('Test can not proceed as the fut is not a readable field')
89+
await self.__single_field_read_test(fut=fut)
90+
91+
if is_sw_writable:
92+
if not isinstance(fut, (FieldAsyncWriteOnly, FieldAsyncReadWrite)):
93+
raise TypeError('Test can not proceed as the fut is not a writable field')
94+
await self.__single_field_write_test(fut=fut)
95+
96+
async def __single_field_read_test(
97+
self,
98+
fut: Union[FieldAsyncReadOnly, FieldAsyncReadWrite]) -> None:
99+
100+
with patch.object(self,'write_callback') as write_callback_mock, \
101+
patch.object(self,'read_callback', return_value=0) as read_callback_mock:
102+
103+
# read back - zero, this is achieved by setting the register to inverse bitmask
104+
read_callback_mock.return_value = fut.inverse_bitmask
105+
self.assertEqual(await fut.read(),0)
106+
read_callback_mock.assert_called_once_with(
107+
addr=fut.parent_register.address,
108+
width=fut.parent_register.width,
109+
accesswidth=fut.parent_register.accesswidth)
110+
111+
# read back - max_value, this is achieved by setting the register to bitmask
112+
read_callback_mock.reset_mock()
113+
read_callback_mock.return_value = fut.bitmask
114+
self.assertEqual(await fut.read(), fut.max_value)
115+
read_callback_mock.assert_called_once_with(
116+
addr=fut.parent_register.address,
117+
width=fut.parent_register.width,
118+
accesswidth=fut.parent_register.accesswidth)
119+
120+
# read back - random value
121+
read_callback_mock.reset_mock()
122+
random_value = random.randrange(0, fut.parent_register.max_value + 1)
123+
read_callback_mock.return_value = random_value
124+
random_field_value = (random_value & fut.bitmask) >> fut.low
125+
if fut.msb == fut.high:
126+
self.assertEqual(await fut.read(), random_field_value)
127+
else:
128+
self.assertEqual(await fut.read(),
129+
reverse_bits(value=random_field_value, number_bits=fut.width))
130+
read_callback_mock.assert_called_once_with(
131+
addr=fut.parent_register.address,
132+
width=fut.parent_register.width,
133+
accesswidth=fut.parent_register.accesswidth)
134+
135+
# at the end of the read tests the write should not have been called
136+
read_callback_mock.reset_mock()
137+
write_callback_mock.assert_not_called()
138+
139+
async def __single_field_write_test(
140+
self,
141+
fut: Union[FieldAsyncWriteOnly, FieldAsyncReadWrite]) -> None:
142+
143+
with patch.object(self, 'write_callback') as write_callback_mock, \
144+
patch.object(self, 'read_callback', return_value=0) as read_callback_mock:
145+
146+
# pylint:disable-next=protected-access
147+
readable_reg = fut.parent_register._is_readable
148+
149+
150+
151+
random_reg_value = random.randrange(0, fut.parent_register.max_value + 1)
152+
random_field_value = random.randrange(0, fut.max_value + 1)
153+
for reg_base_value, field_value in product(
154+
[0, fut.parent_register.max_value, random_reg_value],
155+
[0, fut.max_value, random_field_value]):
156+
read_callback_mock.reset_mock()
157+
write_callback_mock.reset_mock()
158+
read_callback_mock.return_value = reg_base_value
159+
160+
await fut.write(field_value)
161+
162+
if (fut.width < fut.parent_register.width) and readable_reg:
163+
read_callback_mock.assert_called_once_with(
164+
addr=fut.parent_register.address,
165+
width=fut.parent_register.width,
166+
accesswidth=fut.parent_register.accesswidth)
167+
else:
168+
read_callback_mock.assert_not_called()
169+
170+
write_callback_mock.assert_called_once_with(
171+
addr=fut.parent_register.address,
172+
width=fut.parent_register.width,
173+
accesswidth=fut.parent_register.accesswidth,
174+
data=expected_reg_write_data(fut=fut,
175+
reg_base_value=reg_base_value,
176+
readable_reg=readable_reg,
177+
field_value=field_value))
178+
179+
180+
# check invalid write values bounce
181+
with self.assertRaises(ValueError):
182+
await fut.write(fut.max_value + 1)
183+
184+
with self.assertRaises(ValueError):
185+
await fut.write(-1)

0 commit comments

Comments
 (0)