Coverage for hdl_registers/test/unit/test_parser.py: 100%
178 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-29 22:03 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-29 22:03 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl_registers project, a HDL register generator fast enough to be run
5# in real time.
6# https://hdl-registers.com
7# https://gitlab.com/hdl_registers/hdl_registers
8# --------------------------------------------------------------------------------------------------
10# Standard libraries
11import unittest
13# Third party libraries
14import pytest
15from tsfpga.system_utils import create_file
17# First party libraries
18from hdl_registers.parser import from_toml, load_toml_file
19from hdl_registers.register import Register
22def get_test_default_registers():
23 registers = [
24 Register("config", 0, "r_w", "Configuration register."),
25 ]
26 return registers
29def test_bit_vector_without_width_should_raise_exception(tmp_path):
30 toml_file = create_file(
31 tmp_path / "regs.toml",
32 """
33[register.test_reg]
35mode = "w"
37[register.test_reg.bit_vector.test_bit_vector]
39default_value = "0"
40""",
41 )
43 with pytest.raises(ValueError) as exception_info:
44 from_toml(module_name="", toml_file=toml_file)
45 assert str(exception_info.value) == (
46 f'Bit vector "test_bit_vector" in register "test_reg" in file {toml_file} does not have '
47 'a "width" property'
48 )
51# pylint: disable=too-many-public-methods
52@pytest.mark.usefixtures("fixture_tmp_path")
53class TestRegisterParser(unittest.TestCase):
55 tmp_path = None
57 module_name = "sensor"
58 toml_data = """\
60################################################################################
61[register.data]
63mode = "w"
66################################################################################
67[register.irq]
69mode = "r_w"
70description = "Interrupt register"
72[register.irq.bit.bad]
74description = "Bad things happen"
76[register.irq.bit.not_good]
78description = ""
79default_value = "1"
81[register.irq.bit_vector.interrupts]
83width = 4
84description = "Many interrupts"
85default_value = "0110"
89################################################################################
90[register_array.configuration]
92array_length = 3
93description = "A register array"
95# ------------------------------------------------------------------------------
96[register_array.configuration.register.input_settings]
98description = "Input configuration"
99mode = "r_w"
101[register_array.configuration.register.input_settings.bit.enable]
103description = "Enable things"
104default_value = "1"
106[register_array.configuration.register.input_settings.bit.disable]
108description = ""
109default_value = "0"
112# ------------------------------------------------------------------------------
113[register_array.configuration.register.output_settings]
115mode = "w"
117[register_array.configuration.register.output_settings.bit_vector.data]
119width=16
120description = "Some data"
121default_value="0000000000000011"
124################################################################################
125"""
127 def setUp(self):
128 self.toml_file = create_file(self.tmp_path / "sensor_regs.toml", self.toml_data)
130 def create_toml_file_with_extras(self, toml_extras):
131 data = self.toml_data + toml_extras
132 create_file(self.toml_file, data)
134 def test_order_of_registers_and_bits(self):
135 registers = from_toml(self.module_name, self.toml_file).register_objects
137 assert registers[0].name == "data"
138 assert registers[0].mode == "w"
139 assert registers[0].index == 0
140 assert registers[0].description == ""
141 assert registers[0].default_value == 0
142 assert registers[0].fields == []
144 assert registers[1].name == "irq"
145 assert registers[1].mode == "r_w"
146 assert registers[1].index == 1
147 assert registers[1].description == "Interrupt register"
148 assert registers[1].default_value == 6 * 2**2 + 1 * 2**1
149 assert registers[1].fields[0].name == "bad"
150 assert registers[1].fields[0].description == "Bad things happen"
151 assert registers[1].fields[0].default_value == "0"
152 assert registers[1].fields[1].name == "not_good"
153 assert registers[1].fields[1].description == ""
154 assert registers[1].fields[1].default_value == "1"
155 assert registers[1].fields[2].name == "interrupts"
156 assert registers[1].fields[2].description == "Many interrupts"
157 assert registers[1].fields[2].width == 4
158 assert registers[1].fields[2].default_value == "0110"
160 assert registers[2].name == "configuration"
161 assert registers[2].length == 3
162 assert registers[2].description == "A register array"
163 assert registers[2].index == 2 + 2 * 3 - 1
164 assert len(registers[2].registers) == 2
165 assert registers[2].registers[0].name == "input_settings"
166 assert registers[2].registers[0].mode == "r_w"
167 assert registers[2].registers[0].index == 0
168 assert registers[2].registers[0].description == "Input configuration"
169 assert registers[2].registers[0].default_value == 1
170 assert registers[2].registers[0].fields[0].name == "enable"
171 assert registers[2].registers[0].fields[0].description == "Enable things"
172 assert registers[2].registers[0].fields[0].default_value == "1"
173 assert registers[2].registers[0].fields[1].name == "disable"
174 assert registers[2].registers[0].fields[1].description == ""
175 assert registers[2].registers[0].fields[1].default_value == "0"
176 assert registers[2].registers[1].name == "output_settings"
177 assert registers[2].registers[1].mode == "w"
178 assert registers[2].registers[1].index == 1
179 assert registers[2].registers[1].description == ""
180 assert registers[2].registers[1].default_value == 3
181 assert registers[2].registers[1].fields[0].name == "data"
182 assert registers[2].registers[1].fields[0].description == "Some data"
183 assert registers[2].registers[1].fields[0].width == 16
184 assert registers[2].registers[1].fields[0].default_value == "0000000000000011"
186 def test_default_registers(self):
187 default_registers = get_test_default_registers()
188 num_default_registers = len(default_registers)
189 toml_registers = from_toml(self.module_name, self.toml_file, default_registers)
191 # The registers from this test are appended at the end
192 assert toml_registers.get_register("data").index == num_default_registers
193 assert toml_registers.get_register("irq").index == num_default_registers + 1
195 def test_load_nonexistent_toml_file_should_raise_exception(self):
196 file = self.toml_file.with_name("apa.toml")
197 with pytest.raises(FileNotFoundError) as exception_info:
198 load_toml_file(file)
199 assert str(exception_info.value) == f"Requested TOML file does not exist: {file}"
201 def test_load_dirty_toml_file_should_raise_exception(self):
202 self.create_toml_file_with_extras("apa")
204 with pytest.raises(ValueError) as exception_info:
205 load_toml_file(self.toml_file)
206 assert str(exception_info.value).startswith(
207 f"Error while parsing TOML file {self.toml_file}:\nExpected '=' after a key"
208 )
210 def test_plain_register_with_array_length_attribute_should_raise_exception(self):
211 self.create_toml_file_with_extras(
212 """
213[register.apa]
215mode = "r_w"
216array_length = 4
217"""
218 )
220 with pytest.raises(ValueError) as exception_info:
221 from_toml(self.module_name, self.toml_file)
222 assert (
223 str(exception_info.value)
224 == f'Error while parsing register "apa" in {self.toml_file}: Unknown key "array_length"'
225 )
227 def test_register_array_but_no_array_length_attribute_should_raise_exception(self):
228 self.create_toml_file_with_extras(
229 """
230[register_array.apa]
232[register_array.apa.register.hest]
234mode = "r_w"
235"""
236 )
238 with pytest.raises(ValueError) as exception_info:
239 from_toml(self.module_name, self.toml_file)
240 assert (
241 str(exception_info.value)
242 == f'Register array "apa" in {self.toml_file} does not have "array_length" attribute'
243 )
245 def test_register_in_array_with_no_mode_attribute_should_raise_exception(self):
246 self.create_toml_file_with_extras(
247 """
248[register_array.apa]
250array_length = 2
252[register_array.apa.register.hest]
254description = "nothing"
255"""
256 )
258 with pytest.raises(ValueError) as exception_info:
259 from_toml(self.module_name, self.toml_file)
260 assert (
261 str(exception_info.value)
262 == f'Register "hest" within array "apa" in {self.toml_file} does not have "mode" field'
263 )
265 def test_register_with_no_mode_field_should_raise_exception(self):
266 self.create_toml_file_with_extras(
267 """
268[register.apa]
270description = "w"
271"""
272 )
274 with pytest.raises(ValueError) as exception_info:
275 from_toml(self.module_name, self.toml_file)
276 assert (
277 str(exception_info.value)
278 == f'Register "apa" in {self.toml_file} does not have "mode" field'
279 )
281 def test_two_registers_with_same_name_should_raise_exception(self):
282 self.create_toml_file_with_extras(
283 """
284[register.irq]
286mode = "w"
287"""
288 )
290 with pytest.raises(ValueError) as exception_info:
291 from_toml(self.module_name, self.toml_file)
292 expected = (
293 f"Error while parsing TOML file {self.toml_file}:\n"
294 "Cannot declare ('register', 'irq') twice"
295 )
296 assert str(exception_info.value).startswith(expected)
298 def test_register_with_same_name_as_register_array_should_raise_exception(self):
299 self.create_toml_file_with_extras(
300 """
301[register.configuration]
303mode = "w"
304"""
305 )
307 with pytest.raises(ValueError) as exception_info:
308 from_toml(self.module_name, self.toml_file)
309 assert str(exception_info.value) == f'Duplicate name "configuration" in {self.toml_file}'
311 def test_two_bits_with_same_name_should_raise_exception(self):
312 self.create_toml_file_with_extras(
313 """
314[register.test_reg]
316mode = "w"
318[register.test_reg.bit.test_bit]
320description = "Declaration 1"
322[register.test_reg.bit.test_bit]
324description = "Declaration 2"
325"""
326 )
328 with pytest.raises(ValueError) as exception_info:
329 from_toml(self.module_name, self.toml_file)
331 expected = (
332 f"Error while parsing TOML file {self.toml_file}:\n"
333 "Cannot declare ('register', 'test_reg', 'bit', 'test_bit') twice"
334 )
335 assert str(exception_info.value).startswith(expected)
337 def test_overriding_default_register(self):
338 self.create_toml_file_with_extras(
339 """
340[register.config]
342description = "apa"
343"""
344 )
345 toml_registers = from_toml(self.module_name, self.toml_file, get_test_default_registers())
347 assert toml_registers.get_register("config").description == "apa"
349 def test_changing_mode_of_default_register_should_raise_exception(self):
350 self.create_toml_file_with_extras(
351 """
352[register.config]
354mode = "w"
355"""
356 )
358 with pytest.raises(ValueError) as exception_info:
359 from_toml(self.module_name, self.toml_file, get_test_default_registers())
360 assert (
361 str(exception_info.value)
362 == f'Overloading register "config" in {self.toml_file}, one can not change "mode" '
363 "from default"
364 )
366 def test_unknown_register_field_should_raise_exception(self):
367 self.create_toml_file_with_extras(
368 """
369[register.test_reg]
371mode = "w"
372dummy = 3
373"""
374 )
376 with pytest.raises(ValueError) as exception_info:
377 from_toml(self.module_name, self.toml_file)
378 assert (
379 str(exception_info.value)
380 == f'Error while parsing register "test_reg" in {self.toml_file}: Unknown key "dummy"'
381 )
383 def test_unknown_register_array_field_should_raise_exception(self):
384 self.create_toml_file_with_extras(
385 """
386[register_array.test_array]
388array_length = 2
389dummy = 3
391[register_array.test_array.hest]
393mode = "r"
394"""
395 )
397 with pytest.raises(ValueError) as exception_info:
398 from_toml(self.module_name, self.toml_file)
399 assert (
400 str(exception_info.value)
401 == f'Error while parsing register array "test_array" in {self.toml_file}: '
402 'Unknown key "dummy"'
403 )
405 def test_unknown_register_field_in_register_array_should_raise_exception(self):
406 self.create_toml_file_with_extras(
407 """
408[register_array.test_array]
410array_length = 2
412[register_array.test_array.register.hest]
414mode = "r"
415dummy = 3
416"""
417 )
419 with pytest.raises(ValueError) as exception_info:
420 from_toml(self.module_name, self.toml_file)
421 assert (
422 str(exception_info.value)
423 == f'Error while parsing register "hest" in array "test_array" in {self.toml_file}: '
424 'Unknown key "dummy"'
425 )
427 def test_unknown_bit_field_should_raise_exception(self):
428 self.create_toml_file_with_extras(
429 """
430[register.dummy_reg]
432mode = "w"
434[register.dummy_reg.bit.dummy_bit]
436description = "Stuff"
437height = 3
438"""
439 )
441 with pytest.raises(ValueError) as exception_info:
442 from_toml(self.module_name, self.toml_file)
443 assert (
444 str(exception_info.value)
445 == f'Error while parsing bit "dummy_bit" in register "dummy_reg" in {self.toml_file}: '
446 'Unknown key "height"'
447 )
449 def test_unknown_bit_vector_field_should_raise_exception(self):
450 self.create_toml_file_with_extras(
451 """
452[register.dummy_reg]
454mode = "w"
456[register.dummy_reg.bit_vector.dummy_bit_vector]
458description = "Stuff"
459width = 3
460height = 4
462"""
463 )
465 with pytest.raises(ValueError) as exception_info:
466 from_toml(self.module_name, self.toml_file)
467 assert (
468 str(exception_info.value)
469 == f'Error while parsing bit vector "dummy_bit_vector" in register "dummy_reg" in '
470 f'{self.toml_file}: Unknown key "height"'
471 )
473 def test_constants_in_toml(self):
474 self.create_toml_file_with_extras(
475 """
476[constant.data_width]
478value = 0xf
479description = "the width"
481[constant.apa]
483value = 3.14
484"""
485 )
487 register_list = from_toml(self.module_name, self.toml_file)
488 assert len(register_list.constants) == 2
490 assert register_list.constants[0].name == "data_width"
491 assert register_list.constants[0].value == 15
492 assert register_list.constants[0].description == "the width"
494 assert register_list.constants[1].name == "apa"
495 assert register_list.constants[1].value == 3.14
496 assert register_list.constants[1].description == ""
498 def test_constant_without_value_should_raise_exception(self):
499 self.create_toml_file_with_extras(
500 """
501[constant.data_width]
503description = "the width"
504"""
505 )
506 with pytest.raises(ValueError) as exception_info:
507 from_toml(self.module_name, self.toml_file)
508 assert (
509 str(exception_info.value)
510 == f'Constant "data_width" in {self.toml_file} does not have "value" field'
511 )
513 def test_unknown_constant_field_should_raise_exception(self):
514 self.create_toml_file_with_extras(
515 """
516[constant.data_width]
518value = 0xf
519default_value = 0xf
520"""
521 )
523 with pytest.raises(ValueError) as exception_info:
524 from_toml(self.module_name, self.toml_file)
525 assert (
526 str(exception_info.value)
527 == f'Error while parsing constant "data_width" in {self.toml_file}: '
528 'Unknown key "default_value"'
529 )