Coverage for hdl_registers/register_cpp_generator.py: 99%

358 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# Local folder libraries 

11from .register import REGISTER_MODES 

12from .register_array import RegisterArray 

13from .register_code_generator import RegisterCodeGenerator 

14 

15 

16class RegisterCppGenerator: 

17 """ 

18 Generate a C++ class with register definitions and methods. 

19 

20 There is only a very limited unit test of this class that checks the generated code. 

21 It is instead functionally tested in the file test_register_compilation.py. 

22 That test generates C++ code from an example register set, compiles it and performs some 

23 run-time assertions in a C++ program. 

24 That test is considered more meaningful and exhaustive than a unit test would be. 

25 """ 

26 

27 def __init__(self, module_name, generated_info): 

28 """ 

29 Arguments: 

30 module_name (str): The name of the register map. 

31 generated_info (list(str)): Will be placed in the file headers. 

32 """ 

33 self.module_name = module_name 

34 self.generated_info = generated_info 

35 

36 def get_interface(self, register_objects, constants): 

37 """ 

38 Get a complete C++ interface class header with all constant values and the signatures of 

39 all methods. 

40 

41 Arguments: 

42 register_objects (list): Register arrays and registers to be included. 

43 constants (list(Constant)): Constants to be included. 

44 

45 Returns: 

46 str: C++ code. 

47 """ 

48 generator = InterfaceGenerator( 

49 module_name=self.module_name, generated_info=self.generated_info 

50 ) 

51 return generator.get_interface(register_objects=register_objects, constants=constants) 

52 

53 def get_header(self, register_objects): 

54 """ 

55 Get a complete C++ class header for the implementation of all methods. 

56 

57 Arguments: 

58 register_objects (list): Register arrays and registers to be included. 

59 

60 Returns: 

61 str: C++ code. 

62 """ 

63 generator = HeaderGenerator( 

64 module_name=self.module_name, generated_info=self.generated_info 

65 ) 

66 return generator.get_header(register_objects=register_objects) 

67 

68 def get_implementation(self, register_objects): 

69 """ 

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

71 

72 Arguments: 

73 register_objects (list): Register arrays and registers to be included. 

74 

75 Returns: 

76 str: C++ code. 

77 """ 

78 generator = ImplementationGenerator( 

79 module_name=self.module_name, generated_info=self.generated_info 

80 ) 

81 return generator.get_implementation(register_objects=register_objects) 

82 

83 

84class CommonGenerator(RegisterCodeGenerator): 

85 """ 

86 Class with common methods for generating C++ code. 

87 Do not use this directly, should use :class:`.RegisterCppGenerator`: 

88 """ 

89 

90 def __init__(self, module_name, generated_info): 

91 self.module_name = module_name 

92 

93 self._class_name = self._to_pascal_case(module_name) 

94 self._file_header = "".join([self._comment(header_line) for header_line in generated_info]) 

95 

96 @staticmethod 

97 def _get_separator_line(indentation): 

98 """ 

99 Get a separator line, e.g. " // ---------------------------------\n" 

100 """ 

101 result = " " * indentation + "// " 

102 num_dash = 80 - len(result) 

103 result += "-" * num_dash 

104 result += "\n" 

105 return result 

106 

107 @staticmethod 

108 def _comment(comment, indentation=0): 

109 indent = " " * indentation 

110 return f"{indent}// {comment}\n" 

111 

112 @staticmethod 

113 def _with_namespace(cpp_code_body): 

114 cpp_code = "namespace fpga_regs\n" 

115 cpp_code += "{\n\n" 

116 cpp_code += f"{cpp_code_body}" 

117 cpp_code += "\n} /* namespace fpga_regs */\n" 

118 return cpp_code 

119 

120 def _constructor_signature(self): 

121 return f"{self._class_name}(volatile uint8_t *base_address)" 

122 

123 @staticmethod 

124 def _array_length_constant_name(register_array): 

125 return f"{register_array.name}_array_length" 

126 

127 @staticmethod 

128 def _get_methods_description(register, register_array): 

129 result = f'Methods for the "{register.name}" register' 

130 if register_array: 

131 result += f' within the "{register_array.name}" register array' 

132 result += "." 

133 

134 return result 

135 

136 @staticmethod 

137 def _field_value_type_name(): 

138 """ 

139 We represent all fields as uint32_t. In the future we might support enum types. 

140 """ 

141 return "uint32_t" 

142 

143 @staticmethod 

144 def _register_getter_function_name(register, register_array): 

145 result = "get" 

146 

147 if register_array: 

148 result += f"_{register_array.name}" 

149 

150 result += f"_{register.name}" 

151 

152 return result 

153 

154 def _register_getter_function_signature(self, register, register_array): 

155 function_name = self._register_getter_function_name( 

156 register=register, register_array=register_array 

157 ) 

158 result = f"{function_name}(" 

159 

160 if register_array: 

161 result += "size_t array_index" 

162 

163 result += ")" 

164 

165 return result 

166 

167 @staticmethod 

168 def _field_getter_function_name(register, register_array, field, from_value): 

169 result = "get" 

170 

171 if register_array: 

172 result += f"_{register_array.name}" 

173 

174 result += f"_{register.name}_{field.name}" 

175 

176 if from_value: 

177 result += "_from_value" 

178 

179 return result 

180 

181 def _field_getter_function_signature(self, register, register_array, field, from_value): 

182 function_name = self._field_getter_function_name( 

183 register=register, register_array=register_array, field=field, from_value=from_value 

184 ) 

185 result = f"{function_name}(" 

186 

187 if from_value: 

188 # Value is supplied by user 

189 result += "uint32_t register_value" 

190 elif register_array: 

191 # Value shall be read from bus, in which case we need to know array index if this 

192 # is an array 

193 result += "size_t array_index" 

194 

195 result += ")" 

196 

197 return result 

198 

199 @staticmethod 

200 def _get_shift_and_mask(field): 

201 cpp_code = f" const uint32_t shift = {field.base_index}uL;\n" 

202 cpp_code += f' const uint32_t mask_at_base = 0b{"1" * field.width}uL;\n' 

203 cpp_code += " const uint32_t mask_shifted = mask_at_base << shift;\n" 

204 return cpp_code 

205 

206 @staticmethod 

207 def _register_setter_function_name(register, register_array): 

208 result = "set" 

209 

210 if register_array: 

211 result += f"_{register_array.name}" 

212 

213 result += f"_{register.name}" 

214 

215 return result 

216 

217 def _register_setter_function_signature(self, register, register_array): 

218 function_name = self._register_setter_function_name( 

219 register=register, register_array=register_array 

220 ) 

221 result = f"{function_name}(" 

222 

223 if register_array: 

224 result += "size_t array_index, " 

225 

226 result += "uint32_t register_value)" 

227 

228 return result 

229 

230 @staticmethod 

231 def _field_setter_function_name(register, register_array, field, from_value): 

232 result = "set" 

233 

234 if register_array: 

235 result += f"_{register_array.name}" 

236 

237 result += f"_{register.name}_{field.name}" 

238 

239 if from_value: 

240 result += "_from_value" 

241 

242 return result 

243 

244 def _field_setter_function_signature(self, register, register_array, field, from_value): 

245 function_name = self._field_setter_function_name( 

246 register=register, register_array=register_array, field=field, from_value=from_value 

247 ) 

248 result = f"{function_name}(" 

249 

250 if from_value: 

251 # Current register value is supplied by user 

252 result += "uint32_t register_value, " 

253 elif register_array: 

254 # Current register value shall be read from bus, in which case we need to know array 

255 # index if this is an array 

256 result += "size_t array_index, " 

257 

258 result += f"{self._field_value_type_name()} field_value)" 

259 

260 return result 

261 

262 

263class InterfaceGenerator(CommonGenerator): 

264 """ 

265 Class to generate a C++ interface header. 

266 Do not use this directly, should use :class:`.RegisterCppGenerator`: 

267 """ 

268 

269 def get_interface(self, register_objects, constants): 

270 """ 

271 Get a complete C++ interface class header. See :meth:`.RegisterCppGenerator.get_interface` 

272 for more details. 

273 """ 

274 cpp_code = f"class I{self._class_name}\n" 

275 cpp_code += "{\n" 

276 cpp_code += "public:\n" 

277 

278 cpp_code += self._constants(constants=constants) 

279 

280 cpp_code += self._num_registers(register_objects) 

281 

282 for register_object in register_objects: 

283 if isinstance(register_object, RegisterArray): 

284 cpp_code += self._comment( 

285 f'Length of the "{register_object.name}" register array', indentation=2 

286 ) 

287 constant_name = self._array_length_constant_name(register_object) 

288 cpp_code += ( 

289 f" static const size_t {constant_name} = {register_object.length}uL;\n\n" 

290 ) 

291 

292 cpp_code += f" virtual ~I{self._class_name}() {{ }}\n" 

293 

294 for register, register_array in self._iterate_registers(register_objects): 

295 cpp_code += f"\n{self._get_separator_line(indentation=2)}" 

296 

297 description = self._get_methods_description( 

298 register=register, register_array=register_array 

299 ) 

300 description += f' Mode "{REGISTER_MODES[register.mode].mode_readable}".' 

301 

302 cpp_code += self._comment(comment=description, indentation=2) 

303 cpp_code += "\n" 

304 

305 if register.is_bus_readable: 

306 cpp_code += self._comment( 

307 "Getter that will read the whole register's value over the register bus.", 

308 indentation=2, 

309 ) 

310 signature = self._register_getter_function_signature( 

311 register=register, register_array=register_array 

312 ) 

313 cpp_code += f" virtual uint32_t {signature} const = 0;\n\n" 

314 

315 if register.is_bus_writeable: 

316 cpp_code += self._comment( 

317 "Setter that will write the whole register's value over the register bus.", 

318 indentation=2, 

319 ) 

320 signature = self._register_setter_function_signature( 

321 register=register, register_array=register_array 

322 ) 

323 cpp_code += f" virtual void {signature} const = 0;\n\n" 

324 

325 cpp_code += self._field_interface(register, register_array) 

326 

327 cpp_code += "};\n" 

328 

329 cpp_code_top = f"""\ 

330{self._file_header} 

331#pragma once 

332 

333#include <cassert> 

334#include <cstdint> 

335#include <cstdlib> 

336 

337""" 

338 return cpp_code_top + self._with_namespace(cpp_code) 

339 

340 def _constants(self, constants): 

341 cpp_code = "" 

342 

343 for constant in constants: 

344 if constant.is_boolean: 

345 type_declaration = " bool" 

346 value = str(constant.value).lower() 

347 elif constant.is_integer: 

348 type_declaration = " int" 

349 value = str(constant.value) 

350 elif constant.is_float: 

351 # Expand "const" to "constexpr", which is needed for static floats. See 

352 # https://stackoverflow.com/questions/9141950/ 

353 # initializing-const-member-within-class-declaration-in-c 

354 type_declaration = "expr float" 

355 value = str(constant.value) 

356 else: 

357 raise ValueError(f"Got unexpected constant type. {constant}") 

358 

359 cpp_code += self._comment("Register constant.", indentation=2) 

360 cpp_code += f" static const{type_declaration} {constant.name} = {value};\n" 

361 

362 if constants: 

363 cpp_code += "\n" 

364 

365 return cpp_code 

366 

367 def _num_registers(self, register_objects): 

368 # It is possible that we have constants but no registers 

369 num_registers = 0 

370 if register_objects: 

371 num_registers = register_objects[-1].index + 1 

372 

373 cpp_code = self._comment("Number of registers within this register map.", indentation=2) 

374 cpp_code += f" static const size_t num_registers = {num_registers}uL;\n\n" 

375 return cpp_code 

376 

377 def _field_interface(self, register, register_array): 

378 def function(return_type_name, signature): 

379 return f" virtual {return_type_name} {signature} const = 0;\n" 

380 

381 cpp_code = "" 

382 for field in register.fields: 

383 register_description = f'in the "{register.name}" register' 

384 if register_array is not None: 

385 register_description += f' within the "{register_array.name}" register array' 

386 

387 if register.is_bus_readable: 

388 comment = ( 

389 f'Getter for the "{field.name}" field {register_description},\n' 

390 "which will read register value over the register bus." 

391 ) 

392 

393 cpp_code += self._comment_block(text=comment, indentation=2) 

394 

395 signature = self._field_getter_function_signature( 

396 register=register, 

397 register_array=register_array, 

398 field=field, 

399 from_value=False, 

400 ) 

401 cpp_code += function( 

402 return_type_name=self._field_value_type_name(), signature=signature 

403 ) 

404 

405 comment = ( 

406 f'Getter for the "{field.name}" field {register_description},\n' 

407 "given the register's current value." 

408 ) 

409 cpp_code += self._comment_block(text=comment, indentation=2) 

410 

411 signature = self._field_getter_function_signature( 

412 register=register, 

413 register_array=register_array, 

414 field=field, 

415 from_value=True, 

416 ) 

417 cpp_code += function( 

418 return_type_name=self._field_value_type_name(), signature=signature 

419 ) 

420 

421 if register.is_bus_writeable: 

422 comment = f'Setter for the "{field.name}" field {register_description},\n' 

423 if register.mode == "r_w": 

424 comment += "which will read-modify-write over the register bus." 

425 elif register.mode in ["w", "wpulse", "r_wpulse"]: 

426 comment += ( 

427 "which will set the field to the given value, and all other bits to zero." 

428 ) 

429 else: 

430 raise ValueError(f"Can not handle this register's mode: {register}") 

431 

432 cpp_code += self._comment_block(text=comment, indentation=2) 

433 

434 signature = self._field_setter_function_signature( 

435 register=register, 

436 register_array=register_array, 

437 field=field, 

438 from_value=False, 

439 ) 

440 cpp_code += function(return_type_name="void", signature=signature) 

441 

442 comment = ( 

443 f'Setter for the "{field.name}" field {register_description},\n' 

444 "given the register's current value, which will return an updated value." 

445 ) 

446 cpp_code += self._comment_block(text=comment, indentation=2) 

447 

448 signature = self._field_setter_function_signature( 

449 register=register, 

450 register_array=register_array, 

451 field=field, 

452 from_value=True, 

453 ) 

454 cpp_code += function(return_type_name="uint32_t", signature=signature) 

455 

456 cpp_code += "\n" 

457 

458 return cpp_code 

459 

460 

461class HeaderGenerator(CommonGenerator): 

462 """ 

463 Class to generate a C++ header. 

464 Do not use this directly, should use :class:`.RegisterCppGenerator`: 

465 """ 

466 

467 def get_header(self, register_objects): 

468 """ 

469 Get a complete C++ class header for the implementation of all methods. 

470 See :meth:`.RegisterCppGenerator.get_header` for more details. 

471 """ 

472 cpp_code = f"class {self._class_name} : public I{self._class_name}\n" 

473 cpp_code += "{\n" 

474 

475 cpp_code += "private:\n" 

476 cpp_code += " volatile uint32_t *m_registers;\n\n" 

477 

478 cpp_code += "public:\n" 

479 cpp_code += f" {self._constructor_signature()};\n\n" 

480 cpp_code += f" virtual ~{self._class_name}() {{ }}\n" 

481 

482 def function(return_type_name, signature): 

483 return f" virtual {return_type_name} {signature} const override;\n" 

484 

485 for register, register_array in self._iterate_registers(register_objects): 

486 cpp_code += f"\n{self._get_separator_line(indentation=2)}" 

487 

488 description = self._get_methods_description( 

489 register=register, register_array=register_array 

490 ) 

491 description += " See interface header for documentation." 

492 cpp_code += self._comment(comment=description, indentation=2) 

493 

494 if register.is_bus_readable: 

495 signature = self._register_getter_function_signature( 

496 register=register, 

497 register_array=register_array, 

498 ) 

499 cpp_code += function(return_type_name="uint32_t", signature=signature) 

500 

501 for field in register.fields: 

502 signature = self._field_getter_function_signature( 

503 register=register, 

504 register_array=register_array, 

505 field=field, 

506 from_value=False, 

507 ) 

508 cpp_code += function( 

509 return_type_name=self._field_value_type_name(), signature=signature 

510 ) 

511 

512 signature = self._field_getter_function_signature( 

513 register=register, 

514 register_array=register_array, 

515 field=field, 

516 from_value=True, 

517 ) 

518 cpp_code += function( 

519 return_type_name=self._field_value_type_name(), signature=signature 

520 ) 

521 

522 if register.is_bus_writeable: 

523 signature = self._register_setter_function_signature( 

524 register=register, register_array=register_array 

525 ) 

526 

527 cpp_code += function(return_type_name="void", signature=signature) 

528 

529 for field in register.fields: 

530 signature = self._field_setter_function_signature( 

531 register=register, 

532 register_array=register_array, 

533 field=field, 

534 from_value=False, 

535 ) 

536 cpp_code += function(return_type_name="void", signature=signature) 

537 

538 signature = self._field_setter_function_signature( 

539 register=register, 

540 register_array=register_array, 

541 field=field, 

542 from_value=True, 

543 ) 

544 cpp_code += function(return_type_name="uint32_t", signature=signature) 

545 

546 cpp_code += "};\n" 

547 

548 cpp_code_top = f"""\ 

549{self._file_header} 

550#pragma once 

551 

552#include "i_{self.module_name}.h" 

553 

554""" 

555 return cpp_code_top + self._with_namespace(cpp_code) 

556 

557 

558class ImplementationGenerator(CommonGenerator): 

559 """ 

560 Class to generate a C++ implementation. 

561 Do not use this directly, should use :class:`.RegisterCppGenerator`: 

562 """ 

563 

564 def get_implementation(self, register_objects): 

565 """ 

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

567 See :meth:`.RegisterCppGenerator.get_implementation` for more details. 

568 """ 

569 cpp_code = f"{self._class_name}::{self._constructor_signature()}\n" 

570 cpp_code += " : m_registers(reinterpret_cast<volatile uint32_t *>(base_address))\n" 

571 cpp_code += "{\n" 

572 cpp_code += " // Empty\n" 

573 cpp_code += "}\n\n" 

574 

575 for register, register_array in self._iterate_registers(register_objects): 

576 cpp_code += f"\n{self._get_separator_line(indentation=0)}" 

577 

578 description = self._get_methods_description( 

579 register=register, register_array=register_array 

580 ) 

581 description += " See interface header for documentation.\n" 

582 cpp_code += self._comment(comment=description, indentation=0) 

583 

584 if register.is_bus_readable: 

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

586 

587 for field in register.fields: 

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

589 cpp_code += self._field_getter_function_from_value( 

590 register, register_array, field=field 

591 ) 

592 

593 if register.is_bus_writeable: 

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

595 

596 for field in register.fields: 

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

598 cpp_code += self._field_setter_function_from_value( 

599 register, register_array, field=field 

600 ) 

601 

602 cpp_code_top = f"{self._file_header}\n" 

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

604 

605 return cpp_code_top + self._with_namespace(cpp_code) 

606 

607 def _register_setter_function(self, register, register_array): 

608 signature = self._register_setter_function_signature( 

609 register=register, register_array=register_array 

610 ) 

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

612 cpp_code += "{\n" 

613 

614 if register_array: 

615 cpp_code += ( 

616 f" assert(array_index < {self._array_length_constant_name(register_array)});\n" 

617 ) 

618 cpp_code += ( 

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

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

621 ) 

622 else: 

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

624 

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

626 cpp_code += "}\n\n" 

627 return cpp_code 

628 

629 def _field_setter_function(self, register, register_array, field): 

630 signature = self._field_setter_function_signature( 

631 register=register, register_array=register_array, field=field, from_value=False 

632 ) 

633 

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

635 cpp_code += "{\n" 

636 

637 if register.mode == "r_w": 

638 register_getter_function_name = self._register_getter_function_name( 

639 register=register, register_array=register_array 

640 ) 

641 cpp_code += self._comment( 

642 "Get the current value of any other fields by reading register on the bus.", 

643 indentation=2, 

644 ) 

645 current_register_value = f"{register_getter_function_name}(" 

646 if register_array: 

647 current_register_value += "array_index" 

648 current_register_value += ")" 

649 elif register.mode in ["w", "wpulse", "r_wpulse"]: 

650 cpp_code += self._comment_block( 

651 "This register type's currently written value can not be read back.\n" 

652 "Hence set all other bits to zero when writing the value.", 

653 indentation=2, 

654 ) 

655 current_register_value = 0 

656 else: 

657 raise ValueError(f"Can not handle this register's mode: {register}") 

658 

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

660 

661 signature = self._field_setter_function_name( 

662 register=register, register_array=register_array, field=field, from_value=True 

663 ) 

664 cpp_code += ( 

665 " const uint32_t result_register_value = " 

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

667 ) 

668 

669 register_setter_function_name = self._register_setter_function_name( 

670 register=register, register_array=register_array 

671 ) 

672 cpp_code += f" {register_setter_function_name}(" 

673 if register_array: 

674 cpp_code += "array_index, " 

675 cpp_code += "result_register_value);\n" 

676 

677 cpp_code += "}\n\n" 

678 

679 return cpp_code 

680 

681 def _field_setter_function_from_value(self, register, register_array, field): 

682 signature = self._field_setter_function_signature( 

683 register=register, register_array=register_array, field=field, from_value=True 

684 ) 

685 

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

687 cpp_code += "{\n" 

688 

689 cpp_code += self._get_shift_and_mask(field=field) 

690 cpp_code += "\n const uint32_t field_value_masked = field_value & mask_at_base;\n" 

691 cpp_code += ( 

692 " const uint32_t field_value_masked_and_shifted = field_value_masked << shift;\n\n" 

693 ) 

694 

695 cpp_code += " const uint32_t mask_shifted_inverse = ~mask_shifted;\n" 

696 cpp_code += ( 

697 " const uint32_t register_value_masked = register_value & mask_shifted_inverse;\n\n" 

698 ) 

699 

700 cpp_code += ( 

701 " const uint32_t result_register_value = " 

702 "register_value_masked | field_value_masked_and_shifted;\n\n" 

703 ) 

704 cpp_code += " return result_register_value;\n" 

705 

706 cpp_code += "}\n\n" 

707 

708 return cpp_code 

709 

710 def _register_getter_function(self, register, register_array): 

711 signature = self._register_getter_function_signature( 

712 register=register, register_array=register_array 

713 ) 

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

715 cpp_code += "{\n" 

716 

717 if register_array: 

718 cpp_code += ( 

719 f" assert(array_index < {self._array_length_constant_name(register_array)});\n" 

720 ) 

721 cpp_code += ( 

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

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

724 ) 

725 else: 

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

727 

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

729 cpp_code += " return result;\n" 

730 cpp_code += "}\n\n" 

731 return cpp_code 

732 

733 def _field_getter_function(self, register, register_array, field): 

734 signature = self._field_getter_function_signature( 

735 register=register, register_array=register_array, field=field, from_value=False 

736 ) 

737 

738 cpp_code = f"{self._field_value_type_name()} {self._class_name}::{signature} const\n" 

739 cpp_code += "{\n" 

740 

741 register_getter_function_name = self._register_getter_function_name( 

742 register=register, register_array=register_array 

743 ) 

744 

745 field_getter_from_value_function_name = self._field_getter_function_name( 

746 register=register, register_array=register_array, field=field, from_value=True 

747 ) 

748 

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

750 if register_array: 

751 cpp_code += "array_index" 

752 cpp_code += ");\n" 

753 

754 cpp_code += ( 

755 f" const uint32_t result = {field_getter_from_value_function_name}(register_value);\n" 

756 ) 

757 cpp_code += "\n return result;\n" 

758 cpp_code += "}\n\n" 

759 

760 return cpp_code 

761 

762 def _field_getter_function_from_value(self, register, register_array, field): 

763 signature = self._field_getter_function_signature( 

764 register=register, register_array=register_array, field=field, from_value=True 

765 ) 

766 

767 cpp_code = f"{self._field_value_type_name()} {self._class_name}::{signature} const\n" 

768 cpp_code += "{\n" 

769 

770 cpp_code += self._get_shift_and_mask(field=field) 

771 

772 cpp_code += "\n const uint32_t result_masked = register_value & mask_shifted;\n" 

773 cpp_code += " const uint32_t result_shifted = result_masked >> shift;\n" 

774 cpp_code += "\n return result_shifted;\n" 

775 cpp_code += "}\n\n" 

776 

777 return cpp_code