Skip to content

Commit aeabb17

Browse files
authored
Merge pull request #242 from krcb197/240-optimised-test_field_properties
240 optimised `test_field_properties`
2 parents c29e520 + 9bffafd commit aeabb17

6 files changed

Lines changed: 100 additions & 44 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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 common to both the async and non-async versions
20+
"""
21+
import unittest
22+
from abc import ABC
23+
from typing import Union, Optional
24+
25+
from ..lib import FieldReadWrite, FieldReadOnly, FieldWriteOnly
26+
from ..lib import FieldEnumReadWrite, FieldEnumReadOnly, FieldEnumWriteOnly
27+
from ..lib import FieldAsyncReadOnly, FieldAsyncWriteOnly, FieldAsyncReadWrite
28+
from ..lib import FieldEnumAsyncReadOnly, FieldEnumAsyncWriteOnly, FieldEnumAsyncReadWrite
29+
30+
class CommonTestBase(unittest.TestCase, ABC):
31+
"""
32+
Base Test class for the autogenerated register test to be used for for the async and
33+
non-async cases
34+
"""
35+
36+
# pylint:disable-next=too-many-arguments
37+
def _single_field_property_test(self, *,
38+
fut: Union[FieldReadWrite,
39+
FieldReadOnly,
40+
FieldWriteOnly,
41+
FieldEnumReadWrite,
42+
FieldEnumReadOnly,
43+
FieldEnumWriteOnly,
44+
FieldAsyncReadOnly,
45+
FieldAsyncWriteOnly,
46+
FieldAsyncReadWrite,
47+
FieldEnumAsyncReadOnly,
48+
FieldEnumAsyncWriteOnly,
49+
FieldEnumAsyncReadWrite],
50+
lsb: int,
51+
msb: int,
52+
low: int,
53+
high: int,
54+
bitmask: int,
55+
inverse_bitmask: int,
56+
max_value: int,
57+
is_volatile: bool,
58+
default: Optional[int]) -> None:
59+
self.assertEqual(fut.lsb, lsb)
60+
self.assertEqual(fut.msb, msb)
61+
self.assertEqual(fut.low, low)
62+
self.assertEqual(fut.high, high)
63+
self.assertEqual(fut.bitmask, bitmask)
64+
self.assertEqual(fut.inverse_bitmask, inverse_bitmask)
65+
self.assertEqual(fut.max_value, max_value)
66+
self.assertEqual(fut.is_volatile, is_volatile)
67+
68+
if default is None:
69+
self.assertIsNone(fut.default)
70+
else:
71+
if isinstance(fut, (FieldEnumReadWrite,
72+
FieldEnumReadOnly,
73+
FieldEnumWriteOnly,
74+
FieldEnumAsyncReadOnly,
75+
FieldEnumAsyncWriteOnly,
76+
FieldEnumAsyncReadWrite)):
77+
# pylint does not realise this is a class being returned rather than an object, so
78+
# is unhappy with the name
79+
# pylint:disable-next=invalid-name
80+
EnumCls = fut.enum_cls
81+
if default in [item.value for item in fut.enum_cls]:
82+
self.assertEqual(fut.default, EnumCls(default))
83+
else:
84+
# this is a special case if the default value for the field does not map
85+
# to a legal value of the encoding
86+
self.assertIsNone(fut.default)
87+
else:
88+
self.assertEqual(fut.default, default)

src/peakrdl_python/lib_test/async_reg_base_test_class.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
from .utilities import reg_value_for_field_read_with_random_base
3939
from .utilities import random_field_value, random_field_parent_reg_value
4040

41-
class AsyncLibTestBase(unittest.IsolatedAsyncioTestCase, ABC):
41+
from ._common_base_test_class import CommonTestBase
42+
43+
class AsyncLibTestBase(unittest.IsolatedAsyncioTestCase, CommonTestBase, ABC):
4244
"""
4345
Base Test class for the autogenerated register test when in async mode
4446
"""

src/peakrdl_python/lib_test/base_reg_test_class.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
This package is intended to distributed as part of automatically generated code by the PeakRDL
1919
Python tool. It provide the base class for the autogenerated tests
2020
"""
21-
import unittest
2221
from abc import ABC
2322
from typing import Union
2423
from unittest.mock import patch
@@ -33,7 +32,9 @@
3332
from .utilities import reg_value_for_field_read_with_random_base
3433
from .utilities import random_field_value, random_field_parent_reg_value
3534

36-
class LibTestBase(unittest.TestCase, ABC):
35+
from ._common_base_test_class import CommonTestBase
36+
37+
class LibTestBase(CommonTestBase, ABC):
3738
"""
3839
Base Test class for the autogenerated register test when in async mode
3940
"""

src/peakrdl_python/systemrdl_node_utility_functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def get_field_default_value(node: FieldNode) -> Optional[int]:
420420
return value
421421

422422
if isinstance(value, (FieldNode, SignalNode)):
423-
# if the node resets to an external external signal or value of another field, there is no
423+
# if the node resets to an external signal or value of another field, there is no
424424
# knowledge the code can have of this state and it gets treated as None
425425
return None
426426

src/peakrdl_python/templates/addrmap_tb.py.jinja

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -179,42 +179,6 @@ class {{fq_block_name}}_single_access({{top_node.inst_name}}_TestCase): # type:
179179
{% if 'accesswidth' in node.list_properties() -%}self.assertEqual(mut.accesswidth, {{node.get_property('accesswidth')}}){%- else -%}self.assertEqual(mut.accesswidth, mut.accesswidth){%- endif %}
180180
{% endfor %}
181181

182-
def test_field_properties(self) -> None:
183-
"""
184-
walk the address map and check:
185-
- that the lsb and msb of every field is correct
186-
- that where default values are provided they are applied correctly
187-
"""
188-
fut:Field
189-
{% for node in owned_elements.fields -%}
190-
with self.subTest(msg='field: {{'.'.join(node.get_path_segments())}}'):
191-
# test properties of field: {{'.'.join(node.get_path_segments())}}
192-
fut = self.dut.{{'.'.join(get_python_path_segments(node))}} # type: ignore[union-attr]
193-
if not isinstance(fut, Field):
194-
raise TypeError('This test relies on node being of type Field')
195-
self.assertEqual(fut.lsb,{{node.lsb}})
196-
self.assertEqual(fut.msb,{{node.msb}})
197-
self.assertEqual(fut.low,{{node.low}})
198-
self.assertEqual(fut.high,{{node.high}})
199-
self.assertEqual(fut.bitmask,{{get_field_bitmask_hex_string(node)}})
200-
self.assertEqual(fut.inverse_bitmask,{{get_field_inv_bitmask_hex_string(node)}})
201-
self.assertEqual(fut.max_value,{{get_field_max_value_hex_string(node)}})
202-
{% if 'encode' in node.list_properties() %}
203-
{# only attempt to test enum fields if the reset value is legal for the field #}
204-
{% if get_field_default_value(node) in get_enum_values(node.get_property('encode')) %}
205-
if fut.default is None:
206-
# This is needed to ensure type checking with mypy is happy
207-
raise TypeError('The default should be set')
208-
self.assertEqual(fut.default.value,{{get_field_default_value(node)}})
209-
{% else %}
210-
self.assertIsNone(fut.default)
211-
{% endif %}
212-
{% else %}
213-
self.assertEqual(fut.default,{{get_field_default_value(node)}})
214-
{% endif %}
215-
self.assertEqual(fut.is_volatile,{{node.is_hw_writable}})
216-
{% endfor %}
217-
218182
def test_field_encoding_properties(self) -> None:
219183
"""
220184
Check that enumeration has the name and desc meta data from the systemRDL
@@ -382,12 +346,13 @@ class {{fq_block_name}}_single_access({{top_node.inst_name}}_TestCase): # type:
382346
read_callback_mock.assert_not_called()
383347
{% endfor %}
384348

385-
{% if asyncoutput %}async {% endif %}def test_field_read_and_write(self) -> None:
349+
{% if asyncoutput %}async {% endif %}def test_field(self) -> None:
386350
"""
387-
Check the ability to read and write to fields integer and enum
351+
Check the properties and function (read and write) on the fields both integer and enum
388352
"""
389353
{% for node in owned_elements.fields %}
390354
with self.subTest(msg='field: {{'.'.join(node.get_path_segments())}}'):
355+
self._single_field_property_test(fut=self.dut.{{'.'.join(get_python_path_segments(node))}}, lsb={{node.lsb}}, msb={{node.msb}}, low={{node.low}}, high={{node.high}}, bitmask={{get_field_bitmask_hex_string(node)}}, inverse_bitmask={{get_field_inv_bitmask_hex_string(node)}}, max_value={{get_field_max_value_hex_string(node)}}, is_volatile={{node.is_hw_writable}}, default={{get_field_default_value(node)}})
391356
{%- if 'encode' not in node.list_properties() %}
392357
{% if asyncoutput %}await {%endif %}self._single_int_field_read_and_write_test(fut=self.dut.{{'.'.join(get_python_path_segments(node))}}, is_sw_readable={{node.is_sw_readable}}, is_sw_writable={{node.is_sw_writable}})
393358
{%- else %}

tests/unit_tests/test_export.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import tempfile
2626
import sys
2727
import re
28-
from itertools import chain, permutations, product
28+
from itertools import chain, combinations, product
2929
from pathlib import Path
3030
from array import array as Array
3131
import inspect
@@ -272,7 +272,7 @@ def check_udp_present(dut, udp_to_include:list[str]) -> None:
272272

273273

274274
for udp_to_include in chain.from_iterable(
275-
[permutations(full_property_list, r) for r in range(len(full_property_list))]):
275+
[combinations(full_property_list, r) for r in range(4)]):
276276
# check the list method
277277
with self.subTest(udp_to_include=udp_to_include), \
278278
self.build_wrappers_and_import(udp_list=list(udp_to_include)) as dut:

0 commit comments

Comments
 (0)