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

85 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 subprocess 

11import unittest 

12 

13import pytest 

14 

15from tsfpga.system_utils import create_file, run_command 

16 

17from hdl_registers import HDL_REGISTERS_TEST 

18from hdl_registers.parser import from_toml 

19 

20 

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

22class TestRegisterCompilation(unittest.TestCase): 

23 """ 

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

25 """ 

26 

27 tmp_path = None 

28 

29 def setUp(self): 

30 self.working_dir = self.tmp_path 

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

32 

33 toml_file = HDL_REGISTERS_TEST / "regs_test.toml" 

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

35 

36 self.registers.add_constant("data_width", 24) 

37 self.registers.add_constant("decrement", -8) 

38 

39 def _compile_and_test_c_header(self, test_constants, test_registers): 

40 main_function = "" 

41 functions = "" 

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""" 

52 

53 if not test_registers: 

54 # If no registers, the constant shall be zero 

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

56 else: 

57 main_function += """\ 

58 assert(TEST_NUM_REGS == 12); 

59 test_addresses(); 

60 test_field_indexes(); 

61 test_generated_type(); 

62""" 

63 

64 functions += """ 

65void test_addresses() 

66{ 

67 // Assert that indexes are correct 

68 assert(TEST_PLAIN_DUMMY_REG_INDEX == 0); 

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

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

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

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

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

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

75 

76 // Assert that addresses are correct 

77 assert(TEST_PLAIN_DUMMY_REG_ADDR == 0); 

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

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

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

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

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

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

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

85 // Last register 

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

87} 

88 

89void test_field_indexes() 

90{ 

91 // Assert field indexes of plain register 

92 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_SHIFT == 0); 

93 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_MASK == 1); 

94 

95 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_SHIFT == 1); 

96 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_MASK == 2); 

97 

98 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_SHIFT == 2); 

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

100 

101 // Assert field indexes of array register 

102 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_SHIFT == 0); 

103 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_A_MASK == 1); 

104 

105 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_SHIFT == 1); 

106 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_B_MASK == 2); 

107 

108 assert(TEST_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_SHIFT == 2); 

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

110} 

111 

112void test_generated_type() 

113{ 

114 // Assert positions within the generated type 

115 test_regs_t regs; 

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

117 

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

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

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

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

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

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

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

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

126 

127 // Some dummy code that uses the generated type 

128 regs.plain_dummy_reg = 0; 

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

130 regs.dummy_regs[2].second_array_dummy_reg = 

131 (1 << TEST_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_B_SHIFT); 

132} 

133""" 

134 

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

136 main = f"""\ 

137#include <assert.h> 

138#include <stdint.h> 

139#include "test_regs.h" 

140 

141{functions} 

142 

143int main() 

144{{ 

145{main_function} 

146 

147 return 0; 

148}} 

149""" 

150 create_file(main_file, main) 

151 self.registers.create_c_header(self.include_dir) 

152 

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

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

155 run_command(cmd) 

156 run_command([executable]) 

157 

158 def test_c_header_with_registers_and_constants(self): 

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

160 

161 def test_c_header_with_only_registers(self): 

162 self.registers.constants = [] 

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

164 

165 def test_c_header_with_only_constants(self): 

166 self.registers.register_objects = [] 

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

168 

169 def _compile_and_test_cpp(self, test_registers, test_constants): 

170 main_function = "" 

171 functions = "" 

172 if test_constants: 

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

174 

175 functions += """\ 

176void test_constants() 

177{ 

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

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

180} 

181 

182""" 

183 

184 if not test_registers: 

185 # If no registers, the constant shall be zero 

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

187 else: 

188 main_function += """\ 

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

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

191 

192 // Allocate memory and instantiate the register class 

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

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

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

196 

197 test_read_write_registers(&test, memory); 

198 test_field_getters(&test); 

199 test_field_getters_from_value(&test); 

200 test_field_setters(&test); 

201 test_field_setter_on_write_only_register(&test, memory); 

202 test_field_setter_on_write_pulse_register(&test, memory); 

203 test_field_setter_on_read_write_pulse_register(&test, memory); 

204""" 

205 

206 functions += """\ 

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

208{ 

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

210 // Data is a ramp 0-6. 

211 test->set_plain_dummy_reg(0); 

212 test->set_dummy_regs_array_dummy_reg(0, 1); 

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

214 memory[6] = 2; 

215 test->set_dummy_regs_array_dummy_reg(1, 3); 

216 memory[8] = 4; 

217 test->set_dummy_regs_array_dummy_reg(2, 5); 

218 memory[10] = 6; 

219 test->set_further_regs_dummy_reg(0, 7); 

220 

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

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

223 

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

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

226 

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

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

229 

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

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

232 

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

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

235 

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

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

238 

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

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

241 

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

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

244} 

245 

246void test_field_getters(fpga_regs::Test *test) 

247{ 

248 // Assert field getters of plain register 

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

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

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

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

253 

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

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

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

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

258 

259 // Assert field getters of array register 

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

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

262 

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

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

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

266 

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

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

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

270} 

271 

272void test_field_getters_from_value(fpga_regs::Test *test) 

273{ 

274 uint32_t register_value = 0; 

275 

276 // Assert field getters of plain register 

277 

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

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

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

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

282 

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

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

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

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

287 

288 

289 // Assert field getters of array register 

290 

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

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

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

294 assert( 

295 test->get_dummy_regs_array_dummy_reg_array_bit_vector_from_value(register_value) == 10 

296 ); 

297 

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

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

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

301 assert( 

302 test->get_dummy_regs_array_dummy_reg_array_bit_vector_from_value(register_value) == 27 

303 ); 

304} 

305 

306void test_field_setters(fpga_regs::Test *test) 

307{ 

308 // Assert field getters of plain register 

309 

310 test->set_plain_dummy_reg_plain_bit_a(1); 

311 test->set_plain_dummy_reg_plain_bit_b(0); 

312 test->set_plain_dummy_reg_plain_bit_vector(0b1010); 

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

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

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

316 

317 test->set_plain_dummy_reg_plain_bit_a(0); 

318 test->set_plain_dummy_reg_plain_bit_b(1); 

319 test->set_plain_dummy_reg_plain_bit_vector(0b1011); 

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

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

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

323 

324 // Assert field setters of array register 

325 

326 test->set_dummy_regs_array_dummy_reg_array_bit_a(0, 1); 

327 test->set_dummy_regs_array_dummy_reg_array_bit_b(0, 0); 

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

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

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

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

332 

333 test->set_dummy_regs_array_dummy_reg_array_bit_a(1, 0); 

334 test->set_dummy_regs_array_dummy_reg_array_bit_b(1, 1); 

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

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

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

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

339 

340 // Index 0 should not have been affected 

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

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

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

344} 

345 

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

347{ 

348 int reg_index = 2; 

349 

350 test->set_command(1337); 

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

352 

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

354 

355 test->set_command_a(1); 

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

357 

358 test->set_command_b(1); 

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

360} 

361 

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

363{ 

364 int reg_index = 3; 

365 

366 test->set_irq_status(1337); 

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

368 

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

370 

371 test->set_irq_status_a(1); 

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

373 

374 test->set_irq_status_b(1); 

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

376} 

377 

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

379{ 

380 int reg_index = 4; 

381 

382 test->set_address(1337); 

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

384 

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

386 

387 test->set_address_a(244); 

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

389 

390 test->set_address_b(213); 

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

392} 

393 

394""" 

395 

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

397 main = f"""\ 

398#include <assert.h> 

399#include <iostream> 

400 

401#include "include/test.h" 

402 

403{functions} 

404int main() 

405{{ 

406{main_function} 

407 return 0; 

408}} 

409""" 

410 create_file(main_file, main) 

411 self.registers.create_cpp_interface(self.include_dir) 

412 self.registers.create_cpp_header(self.include_dir) 

413 self.registers.create_cpp_implementation(self.working_dir) 

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

415 

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

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

418 run_command(cmd) 

419 run_command([executable]) 

420 

421 def test_cpp_with_registers_and_constants(self): 

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

423 

424 def test_cpp_with_only_registers(self): 

425 self.registers.constants = [] 

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

427 

428 def test_cpp_with_only_constants(self): 

429 self.registers.register_objects = [] 

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

431 

432 def test_setting_cpp_register_array_out_of_bounds_should_crash(self): 

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

434 main = """\ 

435#include <assert.h> 

436 

437#include "include/test.h" 

438 

439int main() 

440{ 

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

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

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

444 

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

446 test.set_dummy_regs_array_dummy_reg(3, 1337); 

447 

448 return 0; 

449} 

450""" 

451 create_file(main_file, main) 

452 self.registers.create_cpp_interface(self.include_dir) 

453 self.registers.create_cpp_header(self.include_dir) 

454 self.registers.create_cpp_implementation(self.working_dir) 

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

456 

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

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

459 run_command(cmd) 

460 

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

462 stderr = process.communicate() 

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