Coverage for hdl_registers/test/functional/gcc/test_register_compilation.py: 100%

83 statements  

« 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# -------------------------------------------------------------------------------------------------- 

9 

10# Standard libraries 

11import subprocess 

12import unittest 

13 

14# Third party libraries 

15import pytest 

16from tsfpga.system_utils import create_file, run_command 

17 

18# First party libraries 

19from hdl_registers import HDL_REGISTERS_TEST 

20from hdl_registers.parser import from_toml 

21 

22 

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

24class TestRegisterCompilation(unittest.TestCase): 

25 """ 

26 Functional test: TOML -> registers -> Code generation -> compilation -> set/check/assert 

27 """ 

28 

29 tmp_path = None 

30 

31 def setUp(self): 

32 self.working_dir = self.tmp_path 

33 self.include_dir = self.working_dir / "include" 

34 

35 toml_file = HDL_REGISTERS_TEST / "regs_test.toml" 

36 self.registers = from_toml("test", toml_file) 

37 

38 def _compile_and_test_c_header(self, test_constants, test_registers): 

39 main_function = "" 

40 functions = "" 

41 

42 if test_constants: 

43 main_function += " test_constants();\n" 

44 

45 functions += """ 

46void test_constants() 

47{ 

48 assert(TEST_DATA_WIDTH == 24); 

49 assert(TEST_DECREMENT == -8); 

50 

51 assert(TEST_ENABLED); 

52 assert(!TEST_DISABLED); 

53 assert(TEST_ENABLED && !TEST_DISABLED); 

54 

55 assert(TEST_RATE == 3.5); 

56 assert(TEST_RATE != 3.6); 

57} 

58""" 

59 

60 if not test_registers: 

61 # If no registers, the constant shall be zero 

62 main_function += " assert(TEST_NUM_REGS == 0);\n" 

63 else: 

64 main_function += """\ 

65 assert(TEST_NUM_REGS == 12); 

66 test_addresses(); 

67 test_field_indexes(); 

68 test_generated_type(); 

69""" 

70 

71 functions += """ 

72void test_addresses() 

73{ 

74 // Assert that indexes are correct 

75 assert(TEST_PLAIN_DUMMY_REG_INDEX == 0); 

76 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_INDEX(0) == 5); 

77 assert(TEST_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_INDEX(0) == 6); 

78 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_INDEX(1) == 7); 

79 assert(TEST_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_INDEX(1) == 8); 

80 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_INDEX(2) == 9); 

81 assert(TEST_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_INDEX(2) == 10); 

82 

83 // Assert that addresses are correct 

84 assert(TEST_PLAIN_DUMMY_REG_ADDR == 0); 

85 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ADDR(0) == 20); 

86 assert(TEST_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_ADDR(0) == 24); 

87 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ADDR(1) == 28); 

88 assert(TEST_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_ADDR(1) == 32); 

89 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ADDR(2) == 36); 

90 assert(TEST_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_ADDR(2) == 40); 

91 assert(TEST_FURTHER_REGS_DUMMY_REG_ADDR(0) == 44); 

92 // Last register 

93 assert(TEST_FURTHER_REGS_DUMMY_REG_ADDR(0) == 4 * (TEST_NUM_REGS - 1)); 

94} 

95 

96void test_field_indexes() 

97{ 

98 // Assert field indexes of plain register 

99 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_SHIFT == 0); 

100 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_MASK == 1); 

101 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_MASK_INVERSE == 0b11111111111111111111111111111110); 

102 

103 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_SHIFT == 1); 

104 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_MASK == 2); 

105 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_MASK_INVERSE == 0b11111111111111111111111111111101); 

106 

107 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_SHIFT == 2); 

108 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_MASK == 15 << 2); 

109 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_MASK_INVERSE == 0b11111111111111111111111111000011); 

110 

111 // Assert field indexes of array register 

112 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_A_SHIFT == 0); 

113 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_A_MASK == 1); 

114 assert( 

115 TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_A_MASK_INVERSE == 0b11111111111111111111111111111110 

116 ); 

117 

118 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_B_SHIFT == 1); 

119 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_B_MASK == 2); 

120 assert( 

121 TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_B_MASK_INVERSE == 0b11111111111111111111111111111101 

122 ); 

123 

124 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_VECTOR_SHIFT == 2); 

125 assert(TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_VECTOR_MASK == 31 << 2); 

126 assert( 

127 TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_VECTOR_MASK_INVERSE 

128 == 0b11111111111111111111111110000011 

129 ); 

130} 

131 

132void test_generated_type() 

133{ 

134 // Assert positions within the generated type 

135 test_regs_t regs; 

136 assert(sizeof(regs) == 4 * TEST_NUM_REGS); 

137 

138 assert((void *)&regs == (void *)&regs.plain_dummy_reg); 

139 assert((void *)&regs + 20 == (void *)&regs.dummy_regs[0].array_dummy_reg); 

140 assert((void *)&regs + 24 == (void *)&regs.dummy_regs[0].second_array_dummy_reg); 

141 assert((void *)&regs + 28 == (void *)&regs.dummy_regs[1].array_dummy_reg); 

142 assert((void *)&regs + 32 == (void *)&regs.dummy_regs[1].second_array_dummy_reg); 

143 assert((void *)&regs + 36 == (void *)&regs.dummy_regs[2].array_dummy_reg); 

144 assert((void *)&regs + 40 == (void *)&regs.dummy_regs[2].second_array_dummy_reg); 

145 assert((void *)&regs + 44 == (void *)&regs.further_regs[0].dummy_reg); 

146 

147 // Some dummy code that uses the generated type 

148 regs.plain_dummy_reg = 0; 

149 regs.dummy_regs[0].array_dummy_reg = TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_VECTOR_MASK; 

150 regs.dummy_regs[2].second_array_dummy_reg = 

151 (1 << TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_B_SHIFT); 

152} 

153""" 

154 

155 main_file = self.working_dir / "main.c" 

156 main = f"""\ 

157#include <assert.h> 

158#include <stdbool.h> 

159#include <stdint.h> 

160#include <stdio.h> 

161#include "test_regs.h" 

162 

163{functions} 

164 

165int main() 

166{{ 

167{main_function} 

168 

169 return 0; 

170}} 

171""" 

172 create_file(main_file, main) 

173 self.registers.create_c_header(self.include_dir) 

174 

175 executable = self.working_dir / "test.o" 

176 cmd = ["gcc", main_file, f"-o{executable}", f"-I{self.include_dir}"] 

177 run_command(cmd) 

178 run_command([executable]) 

179 

180 def test_c_header_with_registers_and_constants(self): 

181 self._compile_and_test_c_header(test_registers=True, test_constants=True) 

182 

183 def test_c_header_with_only_registers(self): 

184 self.registers.constants = [] 

185 self._compile_and_test_c_header(test_registers=True, test_constants=False) 

186 

187 def test_c_header_with_only_constants(self): 

188 self.registers.register_objects = [] 

189 self._compile_and_test_c_header(test_registers=False, test_constants=True) 

190 

191 def _compile_and_test_cpp(self, test_registers, test_constants): 

192 main_function = "" 

193 functions = "" 

194 if test_constants: 

195 main_function += " test_constants();\n" 

196 

197 functions += """\ 

198void test_constants() 

199{ 

200 assert(fpga_regs::Test::data_width == 24); 

201 assert(fpga_regs::Test::decrement == -8); 

202 

203 assert(fpga_regs::Test::enabled); 

204 assert(!fpga_regs::Test::disabled); 

205 assert(fpga_regs::Test::enabled && !fpga_regs::Test::disabled); 

206 

207 assert(fpga_regs::Test::rate == 3.5); 

208 assert(fpga_regs::Test::rate != 3.6); 

209} 

210 

211""" 

212 

213 if not test_registers: 

214 # If no registers, the constant shall be zero 

215 main_function += " assert(fpga_regs::Test::num_registers == 0);\n" 

216 else: 

217 main_function += """\ 

218 assert(fpga_regs::Test::num_registers == 12); 

219 assert(fpga_regs::Test::dummy_regs_array_length == 3); 

220 

221 // Allocate memory and instantiate the register class 

222 uint32_t memory[fpga_regs::Test::num_registers]; 

223 volatile uint8_t *base_address = reinterpret_cast<volatile uint8_t *>(memory); 

224 fpga_regs::Test test = fpga_regs::Test(base_address); 

225 

226 test_read_write_registers(&test, memory); 

227 test_field_getters(&test); 

228 test_field_getters_from_value(&test); 

229 test_field_setters(&test); 

230 test_field_setter_on_write_only_register(&test, memory); 

231 test_field_setter_on_write_pulse_register(&test, memory); 

232 test_field_setter_on_read_write_pulse_register(&test, memory); 

233""" 

234 

235 functions += """\ 

236void test_read_write_registers(fpga_regs::Test *test, uint32_t *memory) 

237{ 

238 // Set data and then check, according to the expected register addresses. 

239 // Data is a ramp 0-6. 

240 test->set_plain_dummy_reg(0); 

241 test->set_dummy_regs_array_dummy_reg(0, 1); 

242 // second_array_dummy_reg is read only, so set the value in the memory straight away 

243 memory[6] = 2; 

244 test->set_dummy_regs_array_dummy_reg(1, 3); 

245 memory[8] = 4; 

246 test->set_dummy_regs_array_dummy_reg(2, 5); 

247 memory[10] = 6; 

248 test->set_further_regs_dummy_reg(0, 7); 

249 

250 assert(test->get_plain_dummy_reg() == 0); 

251 assert(memory[0] == 0); 

252 

253 assert(test->get_dummy_regs_array_dummy_reg(0) == 1); 

254 assert(memory[5] == 1); 

255 

256 assert(test->get_dummy_regs_second_array_dummy_reg(0) == 2); 

257 assert(memory[6] == 2); 

258 

259 assert(test->get_dummy_regs_array_dummy_reg(1) == 3); 

260 assert(memory[7] == 3); 

261 

262 assert(test->get_dummy_regs_second_array_dummy_reg(1) == 4); 

263 assert(memory[8] == 4); 

264 

265 assert(test->get_dummy_regs_array_dummy_reg(2) == 5); 

266 assert(memory[9] == 5); 

267 

268 assert(test->get_dummy_regs_second_array_dummy_reg(2) == 6); 

269 assert(memory[10] == 6); 

270 

271 assert(test->get_further_regs_dummy_reg(0) == 7); 

272 assert(memory[11] == 7); 

273} 

274 

275void test_field_getters(fpga_regs::Test *test) 

276{ 

277 // Assert field getters of plain register 

278 test->set_plain_dummy_reg((0b1010 << 2) | (0b0 << 1) | (0b1 << 0)); 

279 assert(test->get_plain_dummy_reg_plain_bit_a() == 1); 

280 assert(test->get_plain_dummy_reg_plain_bit_b() == 0); 

281 assert(test->get_plain_dummy_reg_plain_bit_vector() == 10); 

282 

283 test->set_plain_dummy_reg((0b1011 << 2) | (0b1 << 1) | (0b0 << 0)); 

284 assert(test->get_plain_dummy_reg_plain_bit_a() == 0); 

285 assert(test->get_plain_dummy_reg_plain_bit_b() == 1); 

286 assert(test->get_plain_dummy_reg_plain_bit_vector() == 11); 

287 

288 // Assert field getters of array register 

289 test->set_dummy_regs_array_dummy_reg(0, (0b1010 << 2) | (0b0 << 1) | (0b1 << 0)); 

290 test->set_dummy_regs_array_dummy_reg(1, (0b1011 << 2) | (0b1 << 1) | (0b0 << 0)); 

291 

292 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a(0) == 1); 

293 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b(0) == 0); 

294 assert(test->get_dummy_regs_array_dummy_reg_array_bit_vector(0) == 10); 

295 

296 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a(1) == 0); 

297 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b(1) == 1); 

298 assert(test->get_dummy_regs_array_dummy_reg_array_bit_vector(1) == 11); 

299} 

300 

301void test_field_getters_from_value(fpga_regs::Test *test) 

302{ 

303 uint32_t register_value = 0; 

304 

305 // Assert field getters of plain register 

306 

307 register_value = (0b1010 << 2) | (0b0 << 1) | (0b1 << 0); 

308 assert(test->get_plain_dummy_reg_plain_bit_a_from_value(register_value) == 1); 

309 assert(test->get_plain_dummy_reg_plain_bit_b_from_value(register_value) == 0); 

310 assert(test->get_plain_dummy_reg_plain_bit_vector_from_value(register_value) == 10); 

311 

312 register_value = (0b1011 << 2) | (0b1 << 1) | (0b0 << 0); 

313 assert(test->get_plain_dummy_reg_plain_bit_a_from_value(register_value) == 0); 

314 assert(test->get_plain_dummy_reg_plain_bit_b_from_value(register_value) == 1); 

315 assert(test->get_plain_dummy_reg_plain_bit_vector_from_value(register_value) == 11); 

316 

317 

318 // Assert field getters of array register 

319 

320 register_value = (0b01010 << 2) | (0b0 << 1) | (0b1 << 0); 

321 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a_from_value(register_value) == 1); 

322 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b_from_value(register_value) == 0); 

323 assert( 

324 test->get_dummy_regs_array_dummy_reg_array_bit_vector_from_value(register_value) == 10 

325 ); 

326 

327 register_value = (0b11011 << 2) | (0b1 << 1) | (0b0 << 0); 

328 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a_from_value(register_value) == 0); 

329 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b_from_value(register_value) == 1); 

330 assert( 

331 test->get_dummy_regs_array_dummy_reg_array_bit_vector_from_value(register_value) == 27 

332 ); 

333} 

334 

335void test_field_setters(fpga_regs::Test *test) 

336{ 

337 // Assert field getters of plain register 

338 

339 test->set_plain_dummy_reg_plain_bit_a(1); 

340 test->set_plain_dummy_reg_plain_bit_b(0); 

341 test->set_plain_dummy_reg_plain_bit_vector(0b1010); 

342 assert(test->get_plain_dummy_reg_plain_bit_a() == 1); 

343 assert(test->get_plain_dummy_reg_plain_bit_b() == 0); 

344 assert(test->get_plain_dummy_reg_plain_bit_vector() == 10); 

345 

346 test->set_plain_dummy_reg_plain_bit_a(0); 

347 test->set_plain_dummy_reg_plain_bit_b(1); 

348 test->set_plain_dummy_reg_plain_bit_vector(0b1011); 

349 assert(test->get_plain_dummy_reg_plain_bit_a() == 0); 

350 assert(test->get_plain_dummy_reg_plain_bit_b() == 1); 

351 assert(test->get_plain_dummy_reg_plain_bit_vector() == 11); 

352 

353 // Assert field setters of array register 

354 

355 test->set_dummy_regs_array_dummy_reg_array_bit_a(0, 1); 

356 test->set_dummy_regs_array_dummy_reg_array_bit_b(0, 0); 

357 test->set_dummy_regs_array_dummy_reg_array_bit_vector(0, 0b1010); 

358 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a(0) == 1); 

359 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b(0) == 0); 

360 assert(test->get_dummy_regs_array_dummy_reg_array_bit_vector(0) == 10); 

361 

362 test->set_dummy_regs_array_dummy_reg_array_bit_a(1, 0); 

363 test->set_dummy_regs_array_dummy_reg_array_bit_b(1, 1); 

364 test->set_dummy_regs_array_dummy_reg_array_bit_vector(1, 0b1011); 

365 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a(1) == 0); 

366 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b(1) == 1); 

367 assert(test->get_dummy_regs_array_dummy_reg_array_bit_vector(1) == 11); 

368 

369 // Index 0 should not have been affected 

370 assert(test->get_dummy_regs_array_dummy_reg_array_bit_a(0) == 1); 

371 assert(test->get_dummy_regs_array_dummy_reg_array_bit_b(0) == 0); 

372 assert(test->get_dummy_regs_array_dummy_reg_array_bit_vector(0) == 10); 

373} 

374 

375void test_field_setter_on_write_pulse_register(fpga_regs::Test *test, uint32_t *memory) 

376{ 

377 int reg_index = 2; 

378 

379 test->set_command(1337); 

380 assert(memory[reg_index] == 1337); 

381 

382 // All other bits should be zero when writing a field in a "write pulse" register 

383 

384 test->set_command_a(1); 

385 assert(memory[reg_index] == 1); 

386 

387 test->set_command_b(1); 

388 assert(memory[reg_index] == 1 << 1); 

389} 

390 

391void test_field_setter_on_read_write_pulse_register(fpga_regs::Test *test, uint32_t *memory) 

392{ 

393 int reg_index = 3; 

394 

395 test->set_irq_status(1337); 

396 assert(memory[reg_index] == 1337); 

397 

398 // All other bits should be zero when writing a field in a "read, write pulse" register 

399 

400 test->set_irq_status_a(1); 

401 assert(memory[reg_index] == 1); 

402 

403 test->set_irq_status_b(1); 

404 assert(memory[reg_index] == 1 << 1); 

405} 

406 

407void test_field_setter_on_write_only_register(fpga_regs::Test *test, uint32_t *memory) 

408{ 

409 int reg_index = 4; 

410 

411 test->set_address(1337); 

412 assert(memory[reg_index] == 1337); 

413 

414 // All other bits should be zero when writing a field in a "write only" register 

415 

416 test->set_address_a(244); 

417 assert(memory[reg_index] == 244); 

418 

419 test->set_address_b(213); 

420 assert(memory[reg_index] == 213 << 8); 

421} 

422 

423""" 

424 

425 main_file = self.working_dir / "main.cpp" 

426 main = f"""\ 

427#include <assert.h> 

428#include <iostream> 

429 

430#include "include/test.h" 

431 

432{functions} 

433int main() 

434{{ 

435{main_function} 

436 return 0; 

437}} 

438""" 

439 create_file(main_file, main) 

440 self.registers.create_cpp_interface(self.include_dir) 

441 self.registers.create_cpp_header(self.include_dir) 

442 self.registers.create_cpp_implementation(self.working_dir) 

443 cpp_class_file = self.working_dir / "test.cpp" 

444 

445 executable = self.working_dir / "test.o" 

446 cmd = ["g++", main_file, cpp_class_file, f"-o{executable}", f"-I{self.include_dir}"] 

447 run_command(cmd) 

448 run_command([executable]) 

449 

450 def test_cpp_with_registers_and_constants(self): 

451 self._compile_and_test_cpp(test_registers=True, test_constants=True) 

452 

453 def test_cpp_with_only_registers(self): 

454 self.registers.constants = [] 

455 self._compile_and_test_cpp(test_registers=True, test_constants=False) 

456 

457 def test_cpp_with_only_constants(self): 

458 self.registers.register_objects = [] 

459 self._compile_and_test_cpp(test_registers=False, test_constants=True) 

460 

461 def test_setting_cpp_register_array_out_of_bounds_should_crash(self): 

462 main_file = self.working_dir / "main.cpp" 

463 main = """\ 

464#include <assert.h> 

465 

466#include "include/test.h" 

467 

468int main() 

469{ 

470 uint32_t data[fpga_regs::Test::num_registers]; 

471 volatile uint8_t *base_address = reinterpret_cast<volatile uint8_t *>(data); 

472 fpga_regs::Test test = fpga_regs::Test(base_address); 

473 

474 // Index 3 is out of bounds (should be less than 3) 

475 test.set_dummy_regs_array_dummy_reg(3, 1337); 

476 

477 return 0; 

478} 

479""" 

480 create_file(main_file, main) 

481 self.registers.create_cpp_interface(self.include_dir) 

482 self.registers.create_cpp_header(self.include_dir) 

483 self.registers.create_cpp_implementation(self.working_dir) 

484 cpp_class_file = self.working_dir / "test.cpp" 

485 

486 executable = self.working_dir / "test.o" 

487 cmd = ["g++", main_file, cpp_class_file, f"-o{executable}", f"-I{self.include_dir}"] 

488 run_command(cmd) 

489 

490 with subprocess.Popen([executable], stderr=subprocess.PIPE) as process: 

491 stderr = process.communicate() 

492 assert "Assertion `array_index < dummy_regs_array_length' failed" in str(stderr), stderr