Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the tsfpga project. 

5# https://tsfpga.com 

6# https://gitlab.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8 

9import subprocess 

10import unittest 

11 

12import pytest 

13 

14import tsfpga 

15from tsfpga.system_utils import create_file, run_command 

16from tsfpga.registers.parser import from_toml 

17 

18 

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

20class TestRegisterCompilation(unittest.TestCase): 

21 """ 

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

23 """ 

24 

25 tmp_path = None 

26 

27 def setUp(self): 

28 self.working_dir = self.tmp_path 

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

30 

31 toml_file = tsfpga.TSFPGA_EXAMPLE_MODULES / "artyz7" / "regs_artyz7.toml" 

32 self.registers = from_toml("artyz7", toml_file) 

33 

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

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

36 

37 def _compile_and_test_c_header(self, test_constants, test_registers): 

38 main_function = "" 

39 functions = "" 

40 if test_constants: 

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

42 

43 functions += """ 

44void test_constants() 

45{ 

46 assert(ARTYZ7_DATA_WIDTH == 24); 

47 assert(ARTYZ7_DECREMENT == -8); 

48} 

49""" 

50 

51 if not test_registers: 

52 # If no registers, the constant shall be zero 

53 main_function += " assert(ARTYZ7_NUM_REGS == 0);\n" 

54 else: 

55 main_function += """\ 

56 assert(ARTYZ7_NUM_REGS == 8); 

57 test_addresses(); 

58 test_bit_indexes(); 

59 test_generated_type(); 

60""" 

61 

62 functions += """ 

63void test_addresses() 

64{ 

65 // Assert that indexes are correct 

66 assert(ARTYZ7_PLAIN_DUMMY_REG_INDEX == 0); 

67 assert(ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_INDEX(0) == 1); 

68 assert(ARTYZ7_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_INDEX(0) == 2); 

69 assert(ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_INDEX(1) == 3); 

70 assert(ARTYZ7_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_INDEX(1) == 4); 

71 assert(ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_INDEX(2) == 5); 

72 assert(ARTYZ7_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_INDEX(2) == 6); 

73 

74 // Assert that addresses are correct 

75 assert(ARTYZ7_PLAIN_DUMMY_REG_ADDR == 0); 

76 assert(ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_ADDR(0) == 4); 

77 assert(ARTYZ7_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_ADDR(0) == 8); 

78 assert(ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_ADDR(1) == 12); 

79 assert(ARTYZ7_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_ADDR(1) == 16); 

80 assert(ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_ADDR(2) == 20); 

81 assert(ARTYZ7_DUMMY_REGS_SECOND_ARRAY_DUMMY_REG_ADDR(2) == 24); 

82 assert(ARTYZ7_FURTHER_REGS_DUMMY_REG_ADDR(0) == 28); 

83 // Last register 

84 assert(ARTYZ7_FURTHER_REGS_DUMMY_REG_ADDR(0) == 4 * (ARTYZ7_NUM_REGS - 1)); 

85} 

86 

87void test_bit_indexes() 

88{ 

89 // Assert bit indexes of plain register 

90 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_A_SHIFT == 0); 

91 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_A_MASK == 1); 

92 

93 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_B_SHIFT == 1); 

94 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_B_MASK == 2); 

95 

96 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_SHIFT == 2); 

97 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_MASK == 15 << 2); 

98 

99 // Assert bit indexes of array register 

100 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_A_SHIFT == 0); 

101 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_A_MASK == 1); 

102 

103 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_B_SHIFT == 1); 

104 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_B_MASK == 2); 

105 

106 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_SHIFT == 2); 

107 assert(ARTYZ7_PLAIN_DUMMY_REG_PLAIN_BIT_VECTOR_MASK == 15 << 2); 

108} 

109 

110void test_generated_type() 

111{ 

112 // Assert positions within the generated type 

113 artyz7_regs_t regs; 

114 assert(sizeof(regs) == 4 * ARTYZ7_NUM_REGS); 

115 

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

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

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

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

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

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

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

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

124 

125 // Some dummy code that uses the generated type 

126 regs.plain_dummy_reg = 0; 

127 regs.dummy_regs[0].array_dummy_reg = ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_VECTOR_MASK; 

128 regs.dummy_regs[2].second_array_dummy_reg = 

129 (1 << ARTYZ7_DUMMY_REGS_ARRAY_DUMMY_REG_ARRAY_BIT_B_SHIFT); 

130} 

131""" 

132 

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

134 main = f"""\ 

135#include <assert.h> 

136#include <stdint.h> 

137#include "artyz7_regs.h" 

138 

139{functions} 

140 

141int main() 

142{{ 

143{main_function} 

144 

145 return 0; 

146}} 

147""" 

148 create_file(main_file, main) 

149 self.registers.create_c_header(self.include_dir) 

150 

151 executable = self.working_dir / "artyz7.o" 

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

153 run_command(cmd) 

154 run_command([executable]) 

155 

156 def test_c_header_with_registers_and_constants(self): 

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

158 

159 def test_c_header_with_only_registers(self): 

160 self.registers.constants = [] 

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

162 

163 def test_c_header_with_only_constants(self): 

164 self.registers.register_objects = [] 

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

166 

167 def _compile_and_test_cpp(self, test_registers, test_constants): 

168 main_function = "" 

169 functions = "" 

170 if test_constants: 

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

172 

173 functions += """\ 

174void test_constants() 

175{ 

176 assert(fpga_regs::Artyz7::data_width == 24); 

177 assert(fpga_regs::Artyz7::decrement == -8); 

178} 

179 

180""" 

181 

182 if not test_registers: 

183 # If no registers, the constant shall be zero 

184 main_function += " assert(fpga_regs::Artyz7::num_registers == 0);\n" 

185 else: 

186 main_function += """\ 

187 assert(fpga_regs::Artyz7::num_registers == 8); 

188 assert(fpga_regs::Artyz7::dummy_regs_array_length == 3); 

189 

190 // Allocate memory and instantiate the register class 

191 uint32_t memory[fpga_regs::Artyz7::num_registers]; 

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

193 fpga_regs::Artyz7 artyz7 = fpga_regs::Artyz7(base_address); 

194 

195 test_read_write_registers(&artyz7, memory); 

196 test_bit_indexes(); 

197""" 

198 

199 functions += """\ 

200void test_read_write_registers(fpga_regs::Artyz7 *artyz7, uint32_t *memory) 

201{ 

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

203 // Data is a ramp 0-6. 

204 artyz7->set_plain_dummy_reg(0); 

205 artyz7->set_dummy_regs_array_dummy_reg(0, 1); 

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

207 memory[2] = 2; 

208 artyz7->set_dummy_regs_array_dummy_reg(1, 3); 

209 memory[4] = 4; 

210 artyz7->set_dummy_regs_array_dummy_reg(2, 5); 

211 memory[6] = 6; 

212 artyz7->set_further_regs_dummy_reg(0, 7); 

213 

214 assert(artyz7->get_plain_dummy_reg() == 0); 

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

216 

217 assert(artyz7->get_dummy_regs_array_dummy_reg(0) == 1); 

218 assert(memory[1] == 1); 

219 

220 assert(artyz7->get_dummy_regs_second_array_dummy_reg(0) == 2); 

221 assert(memory[2] == 2); 

222 

223 assert(artyz7->get_dummy_regs_array_dummy_reg(1) == 3); 

224 assert(memory[3] == 3); 

225 

226 assert(artyz7->get_dummy_regs_second_array_dummy_reg(1) == 4); 

227 assert(memory[4] == 4); 

228 

229 assert(artyz7->get_dummy_regs_array_dummy_reg(2) == 5); 

230 assert(memory[5] == 5); 

231 

232 assert(artyz7->get_dummy_regs_second_array_dummy_reg(2) == 6); 

233 assert(memory[6] == 6); 

234 

235 assert(artyz7->get_further_regs_dummy_reg(0) == 7); 

236 assert(memory[7] == 7); 

237} 

238 

239void test_bit_indexes() 

240{ 

241 // Assert bit indexes of plain register 

242 assert(fpga_regs::Artyz7::plain_dummy_reg_plain_bit_a_shift == 0); 

243 assert(fpga_regs::Artyz7::plain_dummy_reg_plain_bit_a_mask == 1); 

244 assert(fpga_regs::Artyz7::plain_dummy_reg_plain_bit_b_shift == 1); 

245 assert(fpga_regs::Artyz7::plain_dummy_reg_plain_bit_b_mask == 2); 

246 assert(fpga_regs::Artyz7::plain_dummy_reg_plain_bit_vector_shift == 2); 

247 assert(fpga_regs::Artyz7::plain_dummy_reg_plain_bit_vector_mask == 15 << 2); 

248 

249 // Assert bit indexes of array register 

250 assert(fpga_regs::Artyz7::dummy_regs_array_dummy_reg_array_bit_a_shift == 0); 

251 assert(fpga_regs::Artyz7::dummy_regs_array_dummy_reg_array_bit_a_mask == 1); 

252 assert(fpga_regs::Artyz7::dummy_regs_array_dummy_reg_array_bit_b_shift == 1); 

253 assert(fpga_regs::Artyz7::dummy_regs_array_dummy_reg_array_bit_b_mask == 2); 

254 assert(fpga_regs::Artyz7::dummy_regs_array_dummy_reg_array_bit_vector_shift == 2); 

255 assert(fpga_regs::Artyz7::dummy_regs_array_dummy_reg_array_bit_vector_mask == 31 << 2); 

256} 

257 

258""" 

259 

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

261 main = f"""\ 

262#include <assert.h> 

263 

264#include "include/artyz7.h" 

265 

266{functions} 

267int main() 

268{{ 

269{main_function} 

270 return 0; 

271}} 

272""" 

273 create_file(main_file, main) 

274 self.registers.create_cpp_interface(self.include_dir) 

275 self.registers.create_cpp_header(self.include_dir) 

276 self.registers.create_cpp_implementation(self.working_dir) 

277 cpp_class_file = self.working_dir / "artyz7.cpp" 

278 

279 executable = self.working_dir / "artyz7.o" 

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

281 run_command(cmd) 

282 run_command([executable]) 

283 

284 def test_cpp_with_registers_and_constants(self): 

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

286 

287 def test_cpp_with_only_registers(self): 

288 self.registers.constants = [] 

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

290 

291 def test_cpp_with_only_constants(self): 

292 self.registers.register_objects = [] 

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

294 

295 def test_setting_cpp_register_array_out_of_bounds_should_crash(self): 

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

297 main = """\ 

298#include <assert.h> 

299 

300#include "include/artyz7.h" 

301 

302int main() 

303{ 

304 uint32_t data[fpga_regs::Artyz7::num_registers]; 

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

306 fpga_regs::Artyz7 artyz7 = fpga_regs::Artyz7(base_address); 

307 

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

309 artyz7.set_dummy_regs_array_dummy_reg(3, 1337); 

310 

311 return 0; 

312} 

313""" 

314 create_file(main_file, main) 

315 self.registers.create_cpp_interface(self.include_dir) 

316 self.registers.create_cpp_header(self.include_dir) 

317 self.registers.create_cpp_implementation(self.working_dir) 

318 cpp_class_file = self.working_dir / "artyz7.cpp" 

319 

320 executable = self.working_dir / "artyz7.o" 

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

322 run_command(cmd) 

323 

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

325 stderr = process.communicate() 

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