Coverage for hdl_registers/generator/cpp/implementation.py: 97%

143 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-19 20:51 +0000

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

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

3# 

4# This file is part of the hdl-registers project, an HDL register generator fast enough to run 

5# in real time. 

6# https://hdl-registers.com 

7# https://github.com/hdl-registers/hdl-registers 

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

9 

10# Standard libraries 

11from pathlib import Path 

12from typing import TYPE_CHECKING, Any, Optional 

13 

14# First party libraries 

15from hdl_registers.field.bit import Bit 

16from hdl_registers.field.bit_vector import BitVector 

17from hdl_registers.field.enumeration import Enumeration 

18from hdl_registers.field.integer import Integer 

19 

20# Local folder libraries 

21from .cpp_generator_common import CppGeneratorCommon 

22 

23if TYPE_CHECKING: 

24 # First party libraries 

25 from hdl_registers.field.register_field import RegisterField 

26 from hdl_registers.register import Register 

27 from hdl_registers.register_array import RegisterArray 

28 

29 

30class CppImplementationGenerator(CppGeneratorCommon): 

31 """ 

32 Generate a C++ class implementation. 

33 See the :ref:`generator_cpp` article for usage details. 

34 

35 The class implementation will contain: 

36 

37 * for each register, implementation of getter and setter methods for reading/writing the 

38 register as an ``uint``. 

39 

40 * for each field in each register, implementation of getter and setter methods for 

41 reading/writing the field as its native type (enumeration, positive/negative int, etc.). 

42 

43 * The setter will read-modify-write the register to update only the specified field, 

44 depending on the mode of the register. 

45 """ 

46 

47 __version__ = "2.0.0" 

48 

49 SHORT_DESCRIPTION = "C++ implementation" 

50 

51 DEFAULT_INDENTATION_LEVEL = 4 

52 

53 @property 

54 def output_file(self) -> Path: 

55 """ 

56 Result will be placed in this file. 

57 """ 

58 return self.output_folder / f"{self.name}.cpp" 

59 

60 def get_code(self, **kwargs: Any) -> str: 

61 """ 

62 Get a complete C++ class implementation with all methods. 

63 """ 

64 cpp_code = f"""\ 

65{self._macros()}\ 

66 {self._class_name}::{self._constructor_signature()} 

67 : m_registers(reinterpret_cast<volatile uint32_t *>(base_address)), 

68 m_assertion_handler(assertion_handler) 

69 { 

70 // Empty 

71 } 

72 

73 void {self._class_name}::_assert_failed(const std::string *message) const 

74 { 

75 m_assertion_handler(message); 

76 } 

77 

78""" 

79 

80 for register, register_array in self.iterate_registers(): 

81 cpp_code += f"{self.get_separator_line(indent=2)}" 

82 

83 description = self._get_methods_description( 

84 register=register, register_array=register_array 

85 ) 

86 cpp_code += self.comment_block( 

87 text=[description, "See interface header for documentation."], indent=2 

88 ) 

89 cpp_code += "\n" 

90 

91 if register.mode.software_can_read: 

92 cpp_code += self._register_getter_function(register, register_array) 

93 

94 for field in register.fields: 

95 cpp_code += self._field_getter_function(register, register_array, field=field) 

96 cpp_code += self._field_getter_function_from_value( 

97 register, register_array, field=field 

98 ) 

99 

100 if register.mode.software_can_write: 

101 cpp_code += self._register_setter_function(register, register_array) 

102 

103 for field in register.fields: 

104 cpp_code += self._field_setter_function(register, register_array, field=field) 

105 cpp_code += self._field_setter_function_from_value( 

106 register, register_array, field=field 

107 ) 

108 

109 cpp_code_top = f"{self.header}\n" 

110 cpp_code_top += f'#include "include/{self.name}.h"\n\n' 

111 

112 return cpp_code_top + self._with_namespace(cpp_code) 

113 

114 def _macros(self) -> str: 

115 def get_macro(name: str, message: str) -> str: 

116 macro_name = f"_{name}_ASSERT_TRUE" 

117 guard_name = f"NO_REGISTER_{name}_ASSERT" 

118 name_space = " " * (38 - len(name)) 

119 message_space = " " * (21 - len(message)) 

120 base = """\ 

121#ifdef {guard_name} 

122 

123#define {macro_name}(expression, message) ((void)0) 

124 

125#else // Not {guard_name}. 

126 

127// This macro is called by the register code to check for runtime errors. 

128#define {macro_name}(expression, message) {name_space}\\ 

129 {{ \\ 

130 if (!static_cast<bool>(expression)) {{ \\ 

131 std::ostringstream diagnostics; \\ 

132 diagnostics << "{message} out of range in " << __FILE__ << ":" {message_space}\\ 

133 << __LINE__ << ", message: " << message << "."; \\ 

134 std::string diagnostic_message = diagnostics.str(); \\ 

135 _assert_failed(&diagnostic_message); \\ 

136 }} \\ 

137 }} 

138 

139#endif // {guard_name}. 

140""" 

141 return base.format( 

142 guard_name=guard_name, 

143 macro_name=macro_name, 

144 name=name, 

145 name_space=name_space, 

146 message=message, 

147 message_space=message_space, 

148 ) 

149 

150 setter_assert = get_macro(name="SETTER", message="Tried to set value") 

151 getter_assert = get_macro(name="GETTER", message="Got read value") 

152 array_index_assert = get_macro(name="ARRAY_INDEX", message="Provided array index") 

153 return f"""\ 

154{setter_assert} 

155{getter_assert} 

156{array_index_assert} 

157""" 

158 

159 def _register_setter_function( 

160 self, register: "Register", register_array: Optional["RegisterArray"] 

161 ) -> str: 

162 signature = self._register_setter_function_signature( 

163 register=register, register_array=register_array, indent=2 

164 ) 

165 cpp_code = f" void {self._class_name}::{signature} const\n" 

166 cpp_code += " {\n" 

167 

168 if register_array: 

169 cpp_code += f"""\ 

170 _ARRAY_INDEX_ASSERT_TRUE( 

171 array_index < {self.name}::{register_array.name}::array_length, 

172 "'{register_array.name}' array index out of range, got '" << array_index << "'" 

173 ); 

174""" 

175 cpp_code += ( 

176 f" const size_t index = {register_array.base_index} " 

177 f"+ array_index * {len(register_array.registers)} + {register.index};\n" 

178 ) 

179 else: 

180 cpp_code += f" const size_t index = {register.index};\n" 

181 

182 cpp_code += " m_registers[index] = register_value;\n" 

183 cpp_code += " }\n\n" 

184 return cpp_code 

185 

186 def _field_setter_function( 

187 self, 

188 register: "Register", 

189 register_array: Optional["RegisterArray"], 

190 field: "RegisterField", 

191 ) -> str: 

192 signature = self._field_setter_function_signature( 

193 register=register, 

194 register_array=register_array, 

195 field=field, 

196 from_value=False, 

197 indent=2, 

198 ) 

199 

200 cpp_code = f" void {self._class_name}::{signature} const\n" 

201 cpp_code += " {\n" 

202 

203 if self.field_setter_should_read_modify_write(register=register): 

204 register_getter_function_name = self._register_getter_function_name( 

205 register=register, register_array=register_array 

206 ) 

207 cpp_code += self.comment( 

208 comment="Get the current value of other fields by reading register on the bus." 

209 ) 

210 current_register_value = f"{register_getter_function_name}(" 

211 if register_array: 

212 current_register_value += "array_index" 

213 current_register_value += ")" 

214 

215 else: 

216 cpp_code += self.comment( 

217 "Set everything except for the field to default when writing the value." 

218 ) 

219 current_register_value = str(register.default_value) 

220 

221 cpp_code += f" const uint32_t current_register_value = {current_register_value};\n" 

222 

223 signature = self._field_setter_function_name( 

224 register=register, register_array=register_array, field=field, from_value=True 

225 ) 

226 cpp_code += ( 

227 " const uint32_t result_register_value = " 

228 f"{signature}(current_register_value, field_value);\n" 

229 ) 

230 

231 register_setter_function_name = self._register_setter_function_name( 

232 register=register, register_array=register_array 

233 ) 

234 cpp_code += f" {register_setter_function_name}(" 

235 if register_array: 

236 cpp_code += "array_index, " 

237 cpp_code += "result_register_value);\n" 

238 

239 cpp_code += " }\n\n" 

240 

241 return cpp_code 

242 

243 def _field_setter_function_from_value( 

244 self, 

245 register: "Register", 

246 register_array: Optional["RegisterArray"], 

247 field: "RegisterField", 

248 ) -> str: 

249 signature = self._field_setter_function_signature( 

250 register=register, register_array=register_array, field=field, from_value=True, indent=2 

251 ) 

252 

253 return f"""\ 

254 uint32_t {self._class_name}::{signature} const\ 

255 { 

256{self._get_field_shift_and_mask(field=field)}\ 

257{self._get_field_value_checker(field=field, setter_or_getter="setter")}\ 

258 const uint32_t field_value_masked = field_value & mask_at_base; 

259 const uint32_t field_value_masked_and_shifted = field_value_masked << shift; 

260 

261 const uint32_t mask_shifted_inverse = ~mask_shifted; 

262 const uint32_t register_value_masked = register_value & mask_shifted_inverse; 

263 

264 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted; 

265 

266 return result_register_value; 

267 } 

268 

269""" 

270 

271 @staticmethod 

272 def _get_field_shift_and_mask(field: "RegisterField") -> str: 

273 return f"""\ 

274 const uint32_t shift = {field.base_index}uL; 

275 const uint32_t mask_at_base = 0b{"1" * field.width}uL; 

276 const uint32_t mask_shifted = mask_at_base << shift; 

277 

278""" 

279 

280 @staticmethod 

281 def _get_field_value_checker(field: "RegisterField", setter_or_getter: str) -> str: 

282 comment = "// Check that field value is within the legal range." 

283 assertion = f"_{setter_or_getter.upper()}_ASSERT_TRUE" 

284 

285 if isinstance(field, Integer): 

286 return f"""\ 

287 {comment} 

288 {assertion}( 

289 field_value >= {field.min_value}, 

290 "'{field.name}' value too small, got '" << field_value << "'" 

291 ); 

292 {assertion}( 

293 field_value <= {field.max_value}, 

294 "'{field.name}' value too large, got '" << field_value << "'" 

295 ); 

296 

297""" 

298 

299 if isinstance(field, (Bit, BitVector)): 

300 return f"""\ 

301 {comment} 

302 const uint32_t mask_at_base_inverse = ~mask_at_base; 

303 {assertion}( 

304 (field_value & mask_at_base_inverse) == 0, 

305 "'{field.name}' value too many bits used, got '" << field_value << "'" 

306 ); 

307 

308""" 

309 

310 return "" 

311 

312 def _get_field_getter_value_checker(self, field: "RegisterField") -> str: 

313 if isinstance(field, Integer): 

314 return self._get_field_value_checker(field=field, setter_or_getter="getter") 

315 

316 return "" 

317 

318 def _register_getter_function( 

319 self, register: "Register", register_array: Optional["RegisterArray"] 

320 ) -> str: 

321 signature = self._register_getter_function_signature( 

322 register=register, register_array=register_array, indent=2 

323 ) 

324 cpp_code = f" uint32_t {self._class_name}::{signature} const\n" 

325 cpp_code += " {\n" 

326 

327 if register_array: 

328 cpp_code += f""" 

329 _ARRAY_INDEX_ASSERT_TRUE( 

330 array_index < {self.name}::{register_array.name}::array_length, 

331 "'{register_array.name}' array index out of range, got '" << array_index << "'" 

332 ); 

333""" 

334 cpp_code += ( 

335 f" const size_t index = {register_array.base_index} " 

336 f"+ array_index * {len(register_array.registers)} + {register.index};\n" 

337 ) 

338 else: 

339 cpp_code += f" const size_t index = {register.index};\n" 

340 

341 cpp_code += " const uint32_t result = m_registers[index];\n\n" 

342 cpp_code += " return result;\n" 

343 cpp_code += " }\n\n" 

344 return cpp_code 

345 

346 def _field_getter_function( 

347 self, 

348 register: "Register", 

349 register_array: Optional["RegisterArray"], 

350 field: "RegisterField", 

351 ) -> str: 

352 signature = self._field_getter_function_signature( 

353 register=register, 

354 register_array=register_array, 

355 field=field, 

356 from_value=False, 

357 indent=2, 

358 ) 

359 

360 field_type_name = self._field_value_type_name( 

361 register=register, register_array=register_array, field=field 

362 ) 

363 

364 cpp_code = f" {field_type_name} {self._class_name}::{signature} const\n" 

365 cpp_code += " {\n" 

366 

367 register_getter_function_name = self._register_getter_function_name( 

368 register=register, register_array=register_array 

369 ) 

370 

371 field_getter_from_value_function_name = self._field_getter_function_name( 

372 register=register, register_array=register_array, field=field, from_value=True 

373 ) 

374 

375 cpp_code += f" const uint32_t register_value = {register_getter_function_name}(" 

376 if register_array: 

377 cpp_code += "array_index" 

378 cpp_code += ");\n" 

379 

380 cpp_code += ( 

381 f" const {field_type_name} field_value = " 

382 f"{field_getter_from_value_function_name}(register_value);\n" 

383 ) 

384 cpp_code += "\n return field_value;\n" 

385 cpp_code += " }\n\n" 

386 

387 return cpp_code 

388 

389 def _field_getter_function_from_value( 

390 self, 

391 register: "Register", 

392 register_array: Optional["RegisterArray"], 

393 field: "RegisterField", 

394 ) -> str: 

395 signature = self._field_getter_function_signature( 

396 register=register, register_array=register_array, field=field, from_value=True, indent=2 

397 ) 

398 

399 type_name = self._field_value_type_name( 

400 register=register, register_array=register_array, field=field 

401 ) 

402 

403 cpp_code = f"""\ 

404 {type_name} {self._class_name}::{signature} const 

405 { 

406{self._get_field_shift_and_mask(field=field)}\ 

407 const uint32_t result_masked = register_value & mask_shifted; 

408 const uint32_t result_shifted = result_masked >> shift; 

409 

410 {type_name} field_value; 

411 

412""" 

413 

414 if type_name == "uint32_t": 

415 cpp_code += """\ 

416 // No casting needed. 

417 field_value = result_shifted; 

418""" 

419 

420 else: 

421 if isinstance(field, Enumeration): 

422 cpp_code += f"""\ 

423 // "Cast" to the enum type. 

424 field_value = {type_name}(result_shifted); 

425""" 

426 

427 elif isinstance(field, Integer) and field.is_signed: 

428 cpp_code += f"""\ 

429 const {type_name} sign_bit_mask = 1 << {field.width - 1}; 

430 

431 if (result_shifted & sign_bit_mask) 

432 { 

433 // Value is to be interpreted as negative. 

434 // Sign extend it from the width of the field to the width of the return type. 

435 field_value = result_shifted - 2 * sign_bit_mask; 

436 } 

437 else 

438 { 

439 // Value is positive. 

440 field_value = result_shifted; 

441 } 

442""" 

443 else: 

444 raise ValueError(f"Got unexpected field type: {type_name}") 

445 

446 cpp_code += f""" 

447{self._get_field_getter_value_checker(field=field)}\ 

448 return field_value; 

449 } 

450 

451""" 

452 

453 return cpp_code