Skip to content

Commit c29e520

Browse files
authored
Merge pull request #239 from krcb197/238-optimised-test_enum_field_read_and_write
Optimise the `test_enum_field_read_and_write`
2 parents 20e9498 + 00c072e commit c29e520

5 files changed

Lines changed: 318 additions & 150 deletions

File tree

generate_and_test.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
#from coverage import Coverage
3838

39-
#from peakrdl_ipxact import IPXACTImporter
39+
from peakrdl_ipxact import IPXACTImporter
4040

4141
sys.path.append('src')
4242
from peakrdl_python import PythonExporter
@@ -46,6 +46,8 @@
4646
CommandLineParser = argparse.ArgumentParser(description='Test the framework')
4747
CommandLineParser.add_argument('--RDL_source_file', dest='root_RDL_file',
4848
type=pathlib.Path, required=True)
49+
CommandLineParser.add_argument('--RDL_additional_file', dest='additional_file_RDL_file',
50+
type=pathlib.Path)
4951
CommandLineParser.add_argument('--root_node', dest='root_node',
5052
type=str, required=True)
5153
CommandLineParser.add_argument('--output', dest='output_path',
@@ -195,6 +197,8 @@ def build_logging_cong(logfilepath:str):
195197
else:
196198
raise(RuntimeError('not a list'))
197199

200+
if CommandLineArgs.additional_file_RDL_file is not None:
201+
rdlc.compile_file(CommandLineArgs.additional_file_RDL_file)
198202
rdlc.compile_file(CommandLineArgs.root_RDL_file)
199203
spec = rdlc.elaborate(top_def_name=CommandLineArgs.root_node).top
200204

src/peakrdl_python/lib_test/async_reg_base_test_class.py

Lines changed: 131 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,18 @@
2525
import unittest
2626
from abc import ABC
2727
from typing import Union
28-
import random
2928
from unittest.mock import patch
3029
from itertools import product
3130

3231
from ..lib import FieldAsyncReadOnly, FieldAsyncWriteOnly, FieldAsyncReadWrite
32+
from ..lib import FieldEnumAsyncReadOnly, FieldEnumAsyncWriteOnly, FieldEnumAsyncReadWrite
3333
from ..sim_lib.dummy_callbacks import async_dummy_read
3434
from ..sim_lib.dummy_callbacks import async_dummy_write
3535

3636

3737
from .utilities import reverse_bits, expected_reg_write_data
38+
from .utilities import reg_value_for_field_read_with_random_base
39+
from .utilities import random_field_value, random_field_parent_reg_value
3840

3941
class AsyncLibTestBase(unittest.IsolatedAsyncioTestCase, ABC):
4042
"""
@@ -119,14 +121,14 @@ async def __single_field_read_test(
119121

120122
# read back - random value
121123
read_callback_mock.reset_mock()
122-
random_value = random.randrange(0, fut.parent_register.max_value + 1)
124+
random_value = random_field_parent_reg_value(fut)
123125
read_callback_mock.return_value = random_value
124-
random_field_value = (random_value & fut.bitmask) >> fut.low
126+
field_value = (random_value & fut.bitmask) >> fut.low
125127
if fut.msb == fut.high:
126-
self.assertEqual(await fut.read(), random_field_value)
128+
self.assertEqual(await fut.read(), field_value)
127129
else:
128130
self.assertEqual(await fut.read(),
129-
reverse_bits(value=random_field_value, number_bits=fut.width))
131+
reverse_bits(value=field_value, number_bits=fut.width))
130132
read_callback_mock.assert_called_once_with(
131133
addr=fut.parent_register.address,
132134
width=fut.parent_register.width,
@@ -146,13 +148,9 @@ async def __single_field_write_test(
146148
# pylint:disable-next=protected-access
147149
readable_reg = fut.parent_register._is_readable
148150

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)
153151
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]):
152+
[0, fut.parent_register.max_value, random_field_parent_reg_value(fut)],
153+
[0, fut.max_value, random_field_value(fut)]):
156154
read_callback_mock.reset_mock()
157155
write_callback_mock.reset_mock()
158156
read_callback_mock.return_value = reg_base_value
@@ -183,3 +181,125 @@ async def __single_field_write_test(
183181

184182
with self.assertRaises(ValueError):
185183
await fut.write(-1)
184+
185+
async def __single_enum_field_read_test(self,
186+
fut: Union[FieldEnumAsyncReadOnly, FieldEnumAsyncReadOnly],
187+
enum_definition: dict[str, int],
188+
) -> None:
189+
190+
# pylint does not realise this is a class being returned rather than an object, so
191+
# is unhappy with the name
192+
#pylint:disable-next=invalid-name
193+
EnumCls = fut.enum_cls
194+
195+
with patch.object(self, 'write_callback') as write_callback_mock, \
196+
patch.object(self, 'read_callback', return_value=0) as read_callback_mock:
197+
198+
# read back - each legal enum value
199+
for enum_name, enum_value in enum_definition.items():
200+
read_callback_mock.reset_mock()
201+
reg_value = reg_value_for_field_read_with_random_base(fut=fut,
202+
field_value= enum_value)
203+
read_callback_mock.return_value = reg_value
204+
self.assertEqual(await fut.read(), EnumCls[enum_name])
205+
read_callback_mock.assert_called_once_with(
206+
addr=fut.parent_register.address,
207+
width=fut.parent_register.width,
208+
accesswidth=fut.parent_register.accesswidth)
209+
210+
# check register values that don't map to a legal enum value create an exception
211+
# this check is only relevant if there are potential field values that do not map to
212+
# the enum
213+
if len(enum_definition) < (2**fut.width):
214+
# there are two versions of this:
215+
# 1) for small fields (up to 8 bit wide) every value is tested
216+
# 2) for large fields (typically occurring with a sparse enum 100 values are
217+
# checked
218+
legal_enum_values_set = set(enum_definition.values())
219+
if fut.width <= 8:
220+
bad_field_value_iter = set(range(fut.max_value+1))
221+
else:
222+
bad_field_value_iter = {random_field_value(fut) for _ in range(100)}
223+
224+
for bad_field_value in bad_field_value_iter - legal_enum_values_set:
225+
read_callback_mock.reset_mock()
226+
reg_value = reg_value_for_field_read_with_random_base(
227+
fut=fut,
228+
field_value=bad_field_value)
229+
read_callback_mock.return_value = reg_value
230+
with self.assertRaises(ValueError):
231+
_ = await fut.read()
232+
read_callback_mock.assert_called_once_with(
233+
addr=fut.parent_register.address,
234+
width=fut.parent_register.width,
235+
accesswidth=fut.parent_register.accesswidth)
236+
237+
# at the end of the read tests the write should not have been called
238+
write_callback_mock.assert_not_called()
239+
240+
async def __single_enum_field_write_test(self,
241+
fut: Union[FieldEnumAsyncWriteOnly, FieldEnumAsyncReadWrite],
242+
enum_definition: dict[str, int]) -> None:
243+
# pylint does not realise this is a class being returned rather than an object, so
244+
# is unhappy with the name
245+
# pylint:disable-next=invalid-name
246+
EnumCls = fut.enum_cls
247+
248+
# pylint:disable-next=protected-access
249+
readable_reg = fut.parent_register._is_readable
250+
251+
with patch.object(self, 'write_callback') as write_callback_mock, \
252+
patch.object(self, 'read_callback', return_value=0) as read_callback_mock:
253+
254+
for enum_name, enum_value in enum_definition.items():
255+
random_reg_value = random_field_parent_reg_value(fut)
256+
read_callback_mock.return_value = random_reg_value
257+
await fut.write(EnumCls[enum_name])
258+
259+
# the read is skipped if the register is not readable or has the same width
260+
# as the field
261+
if (fut.width < fut.parent_register.width) and readable_reg:
262+
read_callback_mock.assert_called_once_with(
263+
addr=fut.parent_register.address,
264+
width=fut.parent_register.width,
265+
accesswidth=fut.parent_register.accesswidth)
266+
else:
267+
read_callback_mock.assert_not_called()
268+
269+
write_callback_mock.assert_called_once_with(
270+
addr=fut.parent_register.address,
271+
width=fut.parent_register.width,
272+
accesswidth=fut.parent_register.accesswidth,
273+
data=expected_reg_write_data(fut=fut,
274+
reg_base_value=random_reg_value,
275+
readable_reg=readable_reg,
276+
field_value=enum_value))
277+
278+
read_callback_mock.reset_mock()
279+
write_callback_mock.reset_mock()
280+
281+
async def _single_enum_field_read_and_write_test(
282+
self,
283+
fut: Union[FieldEnumAsyncReadOnly, FieldEnumAsyncReadOnly, FieldEnumAsyncWriteOnly],
284+
enum_definition: dict[str, int],
285+
is_sw_readable: bool,
286+
is_sw_writable: bool) -> None:
287+
"""
288+
Check the ability to read and write to integer (non-encoded) field
289+
"""
290+
291+
# the lsb, msb, high, low, bitmask, inv_bitmask and max_value are all checked as part of
292+
# `test_field_properties` so do not need to checked here. Similarly, the properties of
293+
# parent register are checked as part of `test_register_properties`
294+
295+
if is_sw_readable:
296+
if not isinstance(fut, (FieldEnumAsyncReadOnly, FieldEnumAsyncReadWrite)):
297+
raise TypeError('Test can not proceed as the fut is not a readable field')
298+
await self.__single_enum_field_read_test(fut=fut,
299+
enum_definition=enum_definition)
300+
301+
if is_sw_writable:
302+
if not isinstance(fut, (FieldEnumAsyncWriteOnly, FieldEnumAsyncReadWrite)):
303+
raise TypeError('Test can not proceed as the fut is not a writable field')
304+
await self.__single_enum_field_write_test(fut=fut,
305+
enum_definition=enum_definition)

0 commit comments

Comments
 (0)