Coverage for hdl_registers/field/test/test_integer.py: 100%
136 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 11:11 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 11:11 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl-registers project, an HDL register generator fast enough to run
5# in real time.
6# https://hdl-registers.com
7# https://github.com/hdl-registers/hdl-registers
8# --------------------------------------------------------------------------------------------------
10from copy import copy
12import pytest
14from hdl_registers.field.integer import Integer
16TEST_FIELD = Integer(
17 name="apa",
18 base_index=3,
19 description="hest",
20 min_value=120,
21 max_value=456,
22 default_value=127,
23)
26def test_fields():
27 assert TEST_FIELD.name == "apa"
28 assert TEST_FIELD.base_index == 3
29 assert TEST_FIELD.description == "hest"
30 assert TEST_FIELD.min_value == 120
31 assert TEST_FIELD.max_value == 456
32 assert TEST_FIELD.default_value == 127
35def test_repr_is_an_actual_representation():
36 # Check that repr is an actual representation, not just "X object at 0xABCDEF"
37 assert "apa" in repr(TEST_FIELD)
40def test_repr_is_same_after_copy():
41 field = copy(TEST_FIELD)
43 assert repr(field) == repr(TEST_FIELD)
46def test_repr_should_change_when_name_is_changed():
47 field = copy(TEST_FIELD)
48 field.name = "zebra"
50 assert repr(field) != repr(TEST_FIELD)
53def test_repr_should_change_when_default_value_is_changed():
54 field = copy(TEST_FIELD)
55 field.default_value = 133
57 assert repr(field) != repr(TEST_FIELD)
60def test_repr_when_static_members_have_different_value():
61 original_field = Integer(
62 name="apa",
63 base_index=0,
64 description="",
65 min_value=10,
66 max_value=10,
67 default_value=10,
68 )
70 # Different base_index
71 assert repr(
72 Integer(
73 name="apa",
74 base_index=25,
75 description="",
76 min_value=10,
77 max_value=10,
78 default_value=10,
79 )
80 ) != repr(original_field)
82 # Different description
83 assert repr(
84 Integer(
85 name="apa",
86 base_index=0,
87 description="blah",
88 min_value=10,
89 max_value=10,
90 default_value=10,
91 )
92 ) != repr(original_field)
94 # Different min_value
95 assert repr(
96 Integer(
97 name="apa",
98 base_index=0,
99 description="",
100 min_value=5,
101 max_value=10,
102 default_value=10,
103 )
104 ) != repr(original_field)
106 # Different max_value
107 assert repr(
108 Integer(
109 name="apa",
110 base_index=0,
111 description="",
112 min_value=10,
113 max_value=15,
114 default_value=10,
115 )
116 ) != repr(original_field)
119def test_is_signed():
120 def get_is_signed(min_value, max_value):
121 return Integer(
122 name="",
123 base_index=0,
124 description="",
125 min_value=min_value,
126 max_value=max_value,
127 default_value=min_value,
128 ).is_signed
130 assert get_is_signed(min_value=-10, max_value=10)
131 assert get_is_signed(min_value=-10, max_value=-5)
132 assert not get_is_signed(min_value=0, max_value=10)
133 assert not get_is_signed(min_value=5, max_value=10)
136def test_non_ascending_range_should_raise_exception():
137 with pytest.raises(ValueError) as exception_info:
138 Integer(
139 name="apa", base_index=0, description="", min_value=10, max_value=0, default_value=0
140 )
142 assert (
143 str(exception_info.value)
144 == 'Integer field "apa" should have ascending range. Got: [10, 0].'
145 )
148def test_non_integer_range_should_raise_exception():
149 with pytest.raises(TypeError) as exception_info:
150 Integer(
151 name="apa",
152 base_index=0,
153 description="",
154 min_value="5",
155 max_value=10,
156 default_value=11,
157 )
158 assert (
159 str(exception_info.value)
160 == 'Integer field "apa" should have integer value for "min_value". Got: "5".'
161 )
163 with pytest.raises(TypeError) as exception_info:
164 Integer(
165 name="apa",
166 base_index=0,
167 description="",
168 min_value=5,
169 max_value="X",
170 default_value=11,
171 )
172 assert (
173 str(exception_info.value)
174 == 'Integer field "apa" should have integer value for "max_value". Got: "X".'
175 )
178def test_get_value_unsigned():
179 integer = Integer(
180 name="", base_index=2, min_value=0, max_value=127, description="", default_value=0
181 )
182 assert integer.width == 7
184 register_value = int("0101010_11", base=2)
185 assert integer.get_value(register_value) == 42
187 register_value = int("1010101_00", base=2)
188 assert integer.get_value(register_value) == 85
191def test_get_value_signed():
192 integer = Integer(
193 name="", base_index=3, min_value=-128, max_value=127, description="", default_value=0
194 )
195 assert integer.width == 8
197 register_value = int("10101010_111", base=2)
198 assert integer.get_value(register_value) == -86
200 register_value = int("01010101_000", base=2)
201 assert integer.get_value(register_value) == 85
204def test_get_value_should_raise_exception_if_value_out_of_range():
205 integer = Integer(
206 name="apa", base_index=0, min_value=0, max_value=4, description="", default_value=0
207 )
208 assert integer.width == 3
210 with pytest.raises(ValueError) as exception_info:
211 integer.get_value(7)
212 assert (
213 str(exception_info.value)
214 == 'Register field value "7" not inside "apa" field\'s legal range: (0, 4).'
215 )
218def test_set_value_unsigned():
219 integer = Integer(
220 name="", base_index=5, min_value=0, max_value=7, description="", default_value=0
221 )
222 assert integer.width == 3
224 assert integer.set_value(5) == int("101_00000", base=2)
225 assert integer.set_value(2) == int("010_00000", base=2)
228def test_set_value_signed():
229 integer = Integer(
230 name="", base_index=5, min_value=-8, max_value=7, description="", default_value=0
231 )
232 assert integer.width == 4
234 assert integer.set_value(5) == int("0101_00000", base=2)
235 assert integer.set_value(-6) == int("1010_00000", base=2)
238def test_set_value_should_raise_exception_if_value_out_of_range():
239 integer = Integer(
240 name="apa", base_index=5, min_value=-1, max_value=7, description="", default_value=0
241 )
243 with pytest.raises(ValueError) as exception_info:
244 integer.set_value(-8)
245 assert str(exception_info.value) == 'Value "-8" not inside "apa" field\'s legal range: (-1, 7).'
248def test_default_value_uint():
249 def _get_default_value_uint(min_value, max_value, default_value):
250 return Integer(
251 name="",
252 base_index=0,
253 description="",
254 min_value=min_value,
255 max_value=max_value,
256 default_value=default_value,
257 ).default_value_uint
259 assert _get_default_value_uint(min_value=5, max_value=10, default_value=7) == 7
260 assert _get_default_value_uint(min_value=-10, max_value=10, default_value=3) == 3
262 # Negative values, converted to positive and sign extended to the width of the field.
263 assert _get_default_value_uint(min_value=-10, max_value=10, default_value=-9) == 0b10111
264 assert _get_default_value_uint(min_value=-10, max_value=10, default_value=-6) == 0b11010
267def test_default_value_of_bad_type_should_raise_exception():
268 with pytest.raises(TypeError) as exception_info:
269 Integer(
270 name="apa",
271 base_index=0,
272 description="",
273 min_value=5,
274 max_value=10,
275 default_value="7",
276 )
277 assert (
278 str(exception_info.value)
279 == 'Integer field "apa" should have integer value for "default_value". Got: "7".'
280 )
282 field = Integer(
283 name="apa",
284 base_index=0,
285 description="",
286 min_value=5,
287 max_value=10,
288 default_value=5,
289 )
291 with pytest.raises(TypeError) as exception_info:
292 field.default_value = "8"
293 assert (
294 str(exception_info.value)
295 == 'Integer field "apa" should have integer value for "default_value". Got: "8".'
296 )
299def test_default_value_out_of_range_should_raise_exception():
300 with pytest.raises(ValueError) as exception_info:
301 Integer(
302 name="apa",
303 base_index=0,
304 description="",
305 min_value=5,
306 max_value=10,
307 default_value=11,
308 )
309 assert (
310 str(exception_info.value)
311 == 'Integer field "apa" should have "default_value" within range [5, 10]. Got: "11".'
312 )
314 field = Integer(
315 name="apa",
316 base_index=0,
317 description="",
318 min_value=5,
319 max_value=10,
320 default_value=5,
321 )
323 with pytest.raises(ValueError) as exception_info:
324 field.default_value = 120
325 assert (
326 str(exception_info.value)
327 == 'Integer field "apa" should have "default_value" within range [5, 10]. Got: "120".'
328 )
331def _get_field_width(min_value, max_value):
332 return Integer(
333 name="",
334 base_index=0,
335 description="",
336 min_value=min_value,
337 max_value=max_value,
338 default_value=min_value,
339 ).width
342def test_unsigned_width():
343 assert _get_field_width(min_value=0, max_value=127) == 7
344 assert _get_field_width(min_value=0, max_value=128) == 8
345 assert _get_field_width(min_value=0, max_value=255) == 8
346 assert _get_field_width(min_value=0, max_value=256) == 9
348 # The lower bound of the range does not affect the width
349 # (but it will add checkers in our generated code).
350 assert _get_field_width(min_value=255, max_value=255) == 8
353def test_signed_width():
354 # Positive range has greater demand than negative
355 assert _get_field_width(min_value=-4, max_value=16) == 5 + 1
356 assert _get_field_width(min_value=-4, max_value=4) == 3 + 1
358 # Negative range has greater demand than positive
359 assert _get_field_width(min_value=-7, max_value=2) == 4
360 assert _get_field_width(min_value=-8, max_value=2) == 4
361 assert _get_field_width(min_value=-9, max_value=2) == 5
363 assert _get_field_width(min_value=-15, max_value=7) == 5
364 assert _get_field_width(min_value=-16, max_value=7) == 5
365 assert _get_field_width(min_value=-17, max_value=7) == 6
367 # The upper bound of the range here does not affect the width
368 # (but it will add checkers in our generated code).
369 assert _get_field_width(min_value=-8, max_value=-3) == 4
370 assert _get_field_width(min_value=-16, max_value=-4) == 5
373def test_width_out_of_range_should_raise_exception():
374 def _test_width_out_of_range(min_value, max_value):
375 with pytest.raises(ValueError) as exception_info:
376 _get_field_width(min_value=min_value, max_value=max_value)
377 assert (
378 str(exception_info.value)
379 == f"Supplied integer range [{min_value}, {max_value}] does not fit in a register."
380 )
382 # Unsigned. Just within range, should not raise exception.
383 _get_field_width(min_value=128, max_value=2**32 - 1)
384 # Just outside of range.
385 _test_width_out_of_range(min_value=128, max_value=2**32)
387 # Signed, limited by negative range. Just within range, should not raise exception.
388 _get_field_width(min_value=-(2**31), max_value=128)
389 # Just outside of range.
390 _test_width_out_of_range(min_value=-(2**31) - 1, max_value=128)
392 # Signed, limited by positive range. Just within range, should not raise exception.
393 _get_field_width(min_value=-128, max_value=2**31 - 1)
394 # Just outside of range.
395 _test_width_out_of_range(min_value=-128, max_value=2**31)