Coverage for hdl_registers/test/unit/test_parser.py: 100%

170 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-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/tsfpga/hdl_registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10import unittest 

11import pytest 

12 

13from tsfpga.system_utils import create_file 

14 

15from hdl_registers.parser import from_toml, load_toml_file 

16from hdl_registers.register import Register 

17 

18 

19def get_test_default_registers(): 

20 registers = [ 

21 Register("config", 0, "r_w", "Configuration register."), 

22 ] 

23 return registers 

24 

25 

26def test_bit_vector_without_width_should_raise_exception(tmp_path): 

27 toml_file = create_file( 

28 tmp_path / "regs.toml", 

29 """ 

30[register.test_reg] 

31 

32mode = "w" 

33 

34[register.test_reg.bit_vector.test_bit_vector] 

35 

36default_value = "0" 

37""", 

38 ) 

39 

40 with pytest.raises(ValueError) as exception_info: 

41 from_toml(module_name="", toml_file=toml_file) 

42 assert str(exception_info.value) == ( 

43 f'Bit vector "test_bit_vector" in register "test_reg" in file {toml_file} does not have ' 

44 'a "width" property' 

45 ) 

46 

47 

48# pylint: disable=too-many-public-methods 

49@pytest.mark.usefixtures("fixture_tmp_path") 

50class TestRegisterParser(unittest.TestCase): 

51 

52 tmp_path = None 

53 

54 module_name = "sensor" 

55 toml_data = """\ 

56 

57################################################################################ 

58[register.data] 

59 

60mode = "w" 

61 

62 

63################################################################################ 

64[register.irq] 

65 

66mode = "r_w" 

67description = "Interrupt register" 

68 

69[register.irq.bit.bad] 

70 

71description = "Bad things happen" 

72 

73[register.irq.bit.not_good] 

74 

75description = "" 

76default_value = "1" 

77 

78[register.irq.bit_vector.interrupts] 

79 

80width = 4 

81description = "Many interrupts" 

82default_value = "0110" 

83 

84 

85 

86################################################################################ 

87[register_array.configuration] 

88 

89array_length = 3 

90description = "A register array" 

91 

92# ------------------------------------------------------------------------------ 

93[register_array.configuration.register.input_settings] 

94 

95description = "Input configuration" 

96mode = "r_w" 

97 

98[register_array.configuration.register.input_settings.bit.enable] 

99 

100description = "Enable things" 

101default_value = "1" 

102 

103[register_array.configuration.register.input_settings.bit.disable] 

104 

105description = "" 

106default_value = "0" 

107 

108 

109# ------------------------------------------------------------------------------ 

110[register_array.configuration.register.output_settings] 

111 

112mode = "w" 

113 

114[register_array.configuration.register.output_settings.bit_vector.data] 

115 

116width=16 

117description = "Some data" 

118default_value="0000000000000011" 

119 

120 

121################################################################################ 

122""" 

123 

124 def setUp(self): 

125 self.toml_file = create_file(self.tmp_path / "sensor_regs.toml", self.toml_data) 

126 

127 def create_toml_file_with_extras(self, toml_extras): 

128 data = self.toml_data + toml_extras 

129 create_file(self.toml_file, data) 

130 

131 def test_order_of_registers_and_bits(self): 

132 registers = from_toml(self.module_name, self.toml_file).register_objects 

133 

134 assert registers[0].name == "data" 

135 assert registers[0].mode == "w" 

136 assert registers[0].index == 0 

137 assert registers[0].description == "" 

138 assert registers[0].default_value == 0 

139 assert registers[0].fields == [] 

140 

141 assert registers[1].name == "irq" 

142 assert registers[1].mode == "r_w" 

143 assert registers[1].index == 1 

144 assert registers[1].description == "Interrupt register" 

145 assert registers[1].default_value == 6 * 2**2 + 1 * 2**1 

146 assert registers[1].fields[0].name == "bad" 

147 assert registers[1].fields[0].description == "Bad things happen" 

148 assert registers[1].fields[0].default_value == "0" 

149 assert registers[1].fields[1].name == "not_good" 

150 assert registers[1].fields[1].description == "" 

151 assert registers[1].fields[1].default_value == "1" 

152 assert registers[1].fields[2].name == "interrupts" 

153 assert registers[1].fields[2].description == "Many interrupts" 

154 assert registers[1].fields[2].width == 4 

155 assert registers[1].fields[2].default_value == "0110" 

156 

157 assert registers[2].name == "configuration" 

158 assert registers[2].length == 3 

159 assert registers[2].description == "A register array" 

160 assert registers[2].index == 2 + 2 * 3 - 1 

161 assert len(registers[2].registers) == 2 

162 assert registers[2].registers[0].name == "input_settings" 

163 assert registers[2].registers[0].mode == "r_w" 

164 assert registers[2].registers[0].index == 0 

165 assert registers[2].registers[0].description == "Input configuration" 

166 assert registers[2].registers[0].default_value == 1 

167 assert registers[2].registers[0].fields[0].name == "enable" 

168 assert registers[2].registers[0].fields[0].description == "Enable things" 

169 assert registers[2].registers[0].fields[0].default_value == "1" 

170 assert registers[2].registers[0].fields[1].name == "disable" 

171 assert registers[2].registers[0].fields[1].description == "" 

172 assert registers[2].registers[0].fields[1].default_value == "0" 

173 assert registers[2].registers[1].name == "output_settings" 

174 assert registers[2].registers[1].mode == "w" 

175 assert registers[2].registers[1].index == 1 

176 assert registers[2].registers[1].description == "" 

177 assert registers[2].registers[1].default_value == 3 

178 assert registers[2].registers[1].fields[0].name == "data" 

179 assert registers[2].registers[1].fields[0].description == "Some data" 

180 assert registers[2].registers[1].fields[0].width == 16 

181 assert registers[2].registers[1].fields[0].default_value == "0000000000000011" 

182 

183 def test_default_registers(self): 

184 default_registers = get_test_default_registers() 

185 num_default_registers = len(default_registers) 

186 toml_registers = from_toml(self.module_name, self.toml_file, default_registers) 

187 

188 # The registers from this test are appended at the end 

189 assert toml_registers.get_register("data").index == num_default_registers 

190 assert toml_registers.get_register("irq").index == num_default_registers + 1 

191 

192 def test_load_nonexistent_toml_file_should_raise_exception(self): 

193 file = self.toml_file.with_name("apa.toml") 

194 with pytest.raises(FileNotFoundError) as exception_info: 

195 load_toml_file(file) 

196 assert str(exception_info.value) == f"Requested TOML file does not exist: {file}" 

197 

198 def test_load_dirty_toml_file_should_raise_exception(self): 

199 self.create_toml_file_with_extras("apa") 

200 

201 with pytest.raises(ValueError) as exception_info: 

202 load_toml_file(self.toml_file) 

203 assert str(exception_info.value).startswith( 

204 f"Error while parsing TOML file {self.toml_file}:\nExpected '=' after a key" 

205 ) 

206 

207 def test_plain_register_with_array_length_attribute_should_raise_exception(self): 

208 self.create_toml_file_with_extras( 

209 """ 

210[register.apa] 

211 

212mode = "r_w" 

213array_length = 4 

214""" 

215 ) 

216 

217 with pytest.raises(ValueError) as exception_info: 

218 from_toml(self.module_name, self.toml_file) 

219 assert ( 

220 str(exception_info.value) 

221 == f'Error while parsing register "apa" in {self.toml_file}: Unknown key "array_length"' 

222 ) 

223 

224 def test_register_array_but_no_array_length_attribute_should_raise_exception(self): 

225 self.create_toml_file_with_extras( 

226 """ 

227[register_array.apa] 

228 

229[register_array.apa.register.hest] 

230 

231mode = "r_w" 

232""" 

233 ) 

234 

235 with pytest.raises(ValueError) as exception_info: 

236 from_toml(self.module_name, self.toml_file) 

237 assert ( 

238 str(exception_info.value) 

239 == f'Register array "apa" in {self.toml_file} does not have "array_length" attribute' 

240 ) 

241 

242 def test_register_in_array_with_no_mode_attribute_should_raise_exception(self): 

243 self.create_toml_file_with_extras( 

244 """ 

245[register_array.apa] 

246 

247array_length = 2 

248 

249[register_array.apa.register.hest] 

250 

251description = "nothing" 

252""" 

253 ) 

254 

255 with pytest.raises(ValueError) as exception_info: 

256 from_toml(self.module_name, self.toml_file) 

257 assert ( 

258 str(exception_info.value) 

259 == f'Register "hest" within array "apa" in {self.toml_file} does not have "mode" field' 

260 ) 

261 

262 def test_register_with_no_mode_field_should_raise_exception(self): 

263 self.create_toml_file_with_extras( 

264 """ 

265[register.apa] 

266 

267description = "w" 

268""" 

269 ) 

270 

271 with pytest.raises(ValueError) as exception_info: 

272 from_toml(self.module_name, self.toml_file) 

273 assert ( 

274 str(exception_info.value) 

275 == f'Register "apa" in {self.toml_file} does not have "mode" field' 

276 ) 

277 

278 def test_two_registers_with_same_name_should_raise_exception(self): 

279 self.create_toml_file_with_extras( 

280 """ 

281[register.irq] 

282 

283mode = "w" 

284""" 

285 ) 

286 

287 with pytest.raises(ValueError) as exception_info: 

288 from_toml(self.module_name, self.toml_file) 

289 expected = ( 

290 f"Error while parsing TOML file {self.toml_file}:\n" 

291 "Cannot declare ('register', 'irq') twice" 

292 ) 

293 assert str(exception_info.value).startswith(expected) 

294 

295 def test_register_with_same_name_as_register_array_should_raise_exception(self): 

296 self.create_toml_file_with_extras( 

297 """ 

298[register.configuration] 

299 

300mode = "w" 

301""" 

302 ) 

303 

304 with pytest.raises(ValueError) as exception_info: 

305 from_toml(self.module_name, self.toml_file) 

306 assert str(exception_info.value) == f'Duplicate name "configuration" in {self.toml_file}' 

307 

308 def test_two_bits_with_same_name_should_raise_exception(self): 

309 self.create_toml_file_with_extras( 

310 """ 

311[register.test_reg] 

312 

313mode = "w" 

314 

315[register.test_reg.bit.test_bit] 

316 

317description = "Declaration 1" 

318 

319[register.test_reg.bit.test_bit] 

320 

321description = "Declaration 2" 

322""" 

323 ) 

324 

325 with pytest.raises(ValueError) as exception_info: 

326 from_toml(self.module_name, self.toml_file) 

327 

328 expected = ( 

329 f"Error while parsing TOML file {self.toml_file}:\n" 

330 "Cannot declare ('register', 'test_reg', 'bit', 'test_bit') twice" 

331 ) 

332 assert str(exception_info.value).startswith(expected) 

333 

334 def test_overriding_default_register(self): 

335 self.create_toml_file_with_extras( 

336 """ 

337[register.config] 

338 

339description = "apa" 

340""" 

341 ) 

342 toml_registers = from_toml(self.module_name, self.toml_file, get_test_default_registers()) 

343 

344 assert toml_registers.get_register("config").description == "apa" 

345 

346 def test_changing_mode_of_default_register_should_raise_exception(self): 

347 self.create_toml_file_with_extras( 

348 """ 

349[register.config] 

350 

351mode = "w" 

352""" 

353 ) 

354 

355 with pytest.raises(ValueError) as exception_info: 

356 from_toml(self.module_name, self.toml_file, get_test_default_registers()) 

357 assert ( 

358 str(exception_info.value) 

359 == f'Overloading register "config" in {self.toml_file}, one can not change "mode" ' 

360 "from default" 

361 ) 

362 

363 def test_unknown_register_field_should_raise_exception(self): 

364 self.create_toml_file_with_extras( 

365 """ 

366[register.test_reg] 

367 

368mode = "w" 

369dummy = 3 

370""" 

371 ) 

372 

373 with pytest.raises(ValueError) as exception_info: 

374 from_toml(self.module_name, self.toml_file) 

375 assert ( 

376 str(exception_info.value) 

377 == f'Error while parsing register "test_reg" in {self.toml_file}: Unknown key "dummy"' 

378 ) 

379 

380 def test_unknown_register_array_field_should_raise_exception(self): 

381 self.create_toml_file_with_extras( 

382 """ 

383[register_array.test_array] 

384 

385array_length = 2 

386dummy = 3 

387 

388[register_array.test_array.hest] 

389 

390mode = "r" 

391""" 

392 ) 

393 

394 with pytest.raises(ValueError) as exception_info: 

395 from_toml(self.module_name, self.toml_file) 

396 assert ( 

397 str(exception_info.value) 

398 == f'Error while parsing register array "test_array" in {self.toml_file}: ' 

399 'Unknown key "dummy"' 

400 ) 

401 

402 def test_unknown_register_field_in_register_array_should_raise_exception(self): 

403 self.create_toml_file_with_extras( 

404 """ 

405[register_array.test_array] 

406 

407array_length = 2 

408 

409[register_array.test_array.register.hest] 

410 

411mode = "r" 

412dummy = 3 

413""" 

414 ) 

415 

416 with pytest.raises(ValueError) as exception_info: 

417 from_toml(self.module_name, self.toml_file) 

418 assert ( 

419 str(exception_info.value) 

420 == f'Error while parsing register "hest" in array "test_array" in {self.toml_file}: ' 

421 'Unknown key "dummy"' 

422 ) 

423 

424 def test_unknown_bit_field_should_raise_exception(self): 

425 self.create_toml_file_with_extras( 

426 """ 

427[register.dummy_reg] 

428 

429mode = "w" 

430 

431[register.dummy_reg.bit.dummy_bit] 

432 

433description = "Stuff" 

434height = 3 

435""" 

436 ) 

437 

438 with pytest.raises(ValueError) as exception_info: 

439 from_toml(self.module_name, self.toml_file) 

440 assert ( 

441 str(exception_info.value) 

442 == f'Error while parsing bit "dummy_bit" in register "dummy_reg" in {self.toml_file}: ' 

443 'Unknown key "height"' 

444 ) 

445 

446 def test_unknown_bit_vector_field_should_raise_exception(self): 

447 self.create_toml_file_with_extras( 

448 """ 

449[register.dummy_reg] 

450 

451mode = "w" 

452 

453[register.dummy_reg.bit_vector.dummy_bit_vector] 

454 

455description = "Stuff" 

456width = 3 

457height = 4 

458 

459""" 

460 ) 

461 

462 with pytest.raises(ValueError) as exception_info: 

463 from_toml(self.module_name, self.toml_file) 

464 assert ( 

465 str(exception_info.value) 

466 == f'Error while parsing bit vector "dummy_bit_vector" in register "dummy_reg" in ' 

467 f'{self.toml_file}: Unknown key "height"' 

468 ) 

469 

470 def test_constants_in_toml(self): 

471 self.create_toml_file_with_extras( 

472 """ 

473[constant.data_width] 

474 

475value = 0xf 

476description = "the width" 

477""" 

478 ) 

479 

480 register_list = from_toml(self.module_name, self.toml_file) 

481 assert len(register_list.constants) == 1 

482 assert register_list.constants[0].name == "data_width" 

483 assert register_list.constants[0].value == 15 

484 assert register_list.constants[0].description == "the width" 

485 

486 def test_unknown_constant_field_should_raise_exception(self): 

487 self.create_toml_file_with_extras( 

488 """ 

489[constant.data_width] 

490 

491value = 0xf 

492default_value = 0xf 

493""" 

494 ) 

495 

496 with pytest.raises(ValueError) as exception_info: 

497 from_toml(self.module_name, self.toml_file) 

498 assert ( 

499 str(exception_info.value) 

500 == f'Error while parsing constant "data_width" in {self.toml_file}: ' 

501 'Unknown key "default_value"' 

502 )