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

142 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-28 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""" 

74 

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

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

77 

78 description = self._get_methods_description( 

79 register=register, register_array=register_array 

80 ) 

81 cpp_code += self.comment_block( 

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

83 ) 

84 cpp_code += "\n" 

85 

86 if register.mode.software_can_read: 

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

88 

89 for field in register.fields: 

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

91 cpp_code += self._field_getter_function_from_value( 

92 register, register_array, field=field 

93 ) 

94 

95 if register.mode.software_can_write: 

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

97 

98 for field in register.fields: 

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

100 cpp_code += self._field_setter_function_from_value( 

101 register, register_array, field=field 

102 ) 

103 

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

105 

106 return cpp_code_top + self._with_namespace(cpp_code) 

107 

108 def _macros(self) -> str: 

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

110 macro_name = f"_{name}_ASSERT_TRUE" 

111 guard_name = f"NO_REGISTER_{name}_ASSERT" 

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

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

114 base = """\ 

115#ifdef {guard_name} 

116 

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

118 

119#else // Not {guard_name}. 

120 

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

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

123 {{ \\ 

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

125 std::ostringstream diagnostics; \\ 

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

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

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

129 m_assertion_handler(&diagnostic_message); \\ 

130 }} \\ 

131 }} 

132 

133#endif // {guard_name}. 

134""" 

135 return base.format( 

136 guard_name=guard_name, 

137 macro_name=macro_name, 

138 name=name, 

139 name_space=name_space, 

140 message=message, 

141 message_space=message_space, 

142 ) 

143 

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

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

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

147 return f"""\ 

148{setter_assert} 

149{getter_assert} 

150{array_index_assert} 

151""" 

152 

153 def _register_setter_function( 

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

155 ) -> str: 

156 signature = self._register_setter_function_signature( 

157 register=register, register_array=register_array, indent=2 

158 ) 

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

160 cpp_code += " {\n" 

161 

162 if register_array: 

163 cpp_code += f"""\ 

164 _ARRAY_INDEX_ASSERT_TRUE( 

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

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

167 ); 

168 

169""" 

170 cpp_code += ( 

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

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

173 ) 

174 else: 

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

176 

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

178 cpp_code += " }\n\n" 

179 return cpp_code 

180 

181 def _field_setter_function( 

182 self, 

183 register: "Register", 

184 register_array: Optional["RegisterArray"], 

185 field: "RegisterField", 

186 ) -> str: 

187 signature = self._field_setter_function_signature( 

188 register=register, 

189 register_array=register_array, 

190 field=field, 

191 from_value=False, 

192 indent=2, 

193 ) 

194 

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

196 cpp_code += " {\n" 

197 

198 if self.field_setter_should_read_modify_write(register=register): 

199 register_getter_function_name = self._register_getter_function_name( 

200 register=register, register_array=register_array 

201 ) 

202 cpp_code += self.comment( 

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

204 ) 

205 current_register_value = f"{register_getter_function_name}(" 

206 if register_array: 

207 current_register_value += "array_index" 

208 current_register_value += ")" 

209 

210 else: 

211 cpp_code += self.comment( 

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

213 ) 

214 current_register_value = str(register.default_value) 

215 

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

217 

218 signature = self._field_setter_function_name( 

219 register=register, register_array=register_array, field=field, from_value=True 

220 ) 

221 cpp_code += ( 

222 " const uint32_t result_register_value = " 

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

224 ) 

225 

226 register_setter_function_name = self._register_setter_function_name( 

227 register=register, register_array=register_array 

228 ) 

229 cpp_code += f" {register_setter_function_name}(" 

230 if register_array: 

231 cpp_code += "array_index, " 

232 cpp_code += "result_register_value);\n" 

233 

234 cpp_code += " }\n\n" 

235 

236 return cpp_code 

237 

238 def _field_setter_function_from_value( 

239 self, 

240 register: "Register", 

241 register_array: Optional["RegisterArray"], 

242 field: "RegisterField", 

243 ) -> str: 

244 signature = self._field_setter_function_signature( 

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

246 ) 

247 

248 return f"""\ 

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

250 { 

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

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

253 const uint32_t field_value_masked = field_value & mask_at_base; 

254 const uint32_t field_value_masked_and_shifted = field_value_masked << shift; 

255 

256 const uint32_t mask_shifted_inverse = ~mask_shifted; 

257 const uint32_t register_value_masked = register_value & mask_shifted_inverse; 

258 

259 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted; 

260 

261 return result_register_value; 

262 } 

263 

264""" 

265 

266 @staticmethod 

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

268 return f"""\ 

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

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

271 const uint32_t mask_shifted = mask_at_base << shift; 

272 

273""" 

274 

275 @staticmethod 

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

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

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

279 

280 if isinstance(field, Integer): 

281 return f"""\ 

282 {comment} 

283 {assertion}( 

284 field_value >= {field.min_value}, 

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

286 ); 

287 {assertion}( 

288 field_value <= {field.max_value}, 

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

290 ); 

291 

292""" 

293 

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

295 return f"""\ 

296 {comment} 

297 const uint32_t mask_at_base_inverse = ~mask_at_base; 

298 {assertion}( 

299 (field_value & mask_at_base_inverse) == 0, 

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

301 ); 

302 

303""" 

304 

305 return "" 

306 

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

308 if isinstance(field, Integer): 

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

310 

311 return "" 

312 

313 def _register_getter_function( 

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

315 ) -> str: 

316 signature = self._register_getter_function_signature( 

317 register=register, register_array=register_array, indent=2 

318 ) 

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

320 cpp_code += " {\n" 

321 

322 if register_array: 

323 cpp_code += f"""\ 

324 _ARRAY_INDEX_ASSERT_TRUE( 

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

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

327 ); 

328 

329""" 

330 cpp_code += ( 

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

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

333 ) 

334 else: 

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

336 

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

338 cpp_code += " return result;\n" 

339 cpp_code += " }\n\n" 

340 return cpp_code 

341 

342 def _field_getter_function( 

343 self, 

344 register: "Register", 

345 register_array: Optional["RegisterArray"], 

346 field: "RegisterField", 

347 ) -> str: 

348 signature = self._field_getter_function_signature( 

349 register=register, 

350 register_array=register_array, 

351 field=field, 

352 from_value=False, 

353 indent=2, 

354 ) 

355 

356 field_type_name = self._field_value_type_name( 

357 register=register, register_array=register_array, field=field 

358 ) 

359 

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

361 cpp_code += " {\n" 

362 

363 register_getter_function_name = self._register_getter_function_name( 

364 register=register, register_array=register_array 

365 ) 

366 

367 field_getter_from_value_function_name = self._field_getter_function_name( 

368 register=register, register_array=register_array, field=field, from_value=True 

369 ) 

370 

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

372 if register_array: 

373 cpp_code += "array_index" 

374 cpp_code += ");\n" 

375 

376 cpp_code += ( 

377 f" const {field_type_name} field_value = " 

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

379 ) 

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

381 cpp_code += " }\n\n" 

382 

383 return cpp_code 

384 

385 def _field_getter_function_from_value( 

386 self, 

387 register: "Register", 

388 register_array: Optional["RegisterArray"], 

389 field: "RegisterField", 

390 ) -> str: 

391 signature = self._field_getter_function_signature( 

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

393 ) 

394 

395 type_name = self._field_value_type_name( 

396 register=register, register_array=register_array, field=field 

397 ) 

398 

399 cpp_code = f"""\ 

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

401 { 

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

403 const uint32_t result_masked = register_value & mask_shifted; 

404 const uint32_t result_shifted = result_masked >> shift; 

405 

406 {type_name} field_value; 

407 

408""" 

409 

410 if type_name == "uint32_t": 

411 cpp_code += """\ 

412 // No casting needed. 

413 field_value = result_shifted; 

414""" 

415 

416 else: 

417 if isinstance(field, Enumeration): 

418 cpp_code += f"""\ 

419 // "Cast" to the enum type. 

420 field_value = {type_name}(result_shifted); 

421""" 

422 

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

424 cpp_code += f"""\ 

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

426 

427 if (result_shifted & sign_bit_mask) 

428 { 

429 // Value is to be interpreted as negative. 

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

431 field_value = result_shifted - 2 * sign_bit_mask; 

432 } 

433 else 

434 { 

435 // Value is positive. 

436 field_value = result_shifted; 

437 } 

438""" 

439 else: 

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

441 

442 cpp_code += f""" 

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

444 return field_value; 

445 } 

446 

447""" 

448 

449 return cpp_code