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

244 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-29 06:41 +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 

10from __future__ import annotations 

11 

12from typing import TYPE_CHECKING, Any, Literal 

13 

14from hdl_registers.field.bit import Bit 

15from hdl_registers.field.bit_vector import BitVector 

16from hdl_registers.field.enumeration import Enumeration 

17from hdl_registers.field.integer import Integer 

18from hdl_registers.field.numerical_interpretation import ( 

19 Signed, 

20 SignedFixedPoint, 

21 Unsigned, 

22 UnsignedFixedPoint, 

23) 

24 

25from .cpp_generator_common import CppGeneratorCommon 

26 

27if TYPE_CHECKING: 

28 from pathlib import Path 

29 

30 from hdl_registers.field.register_field import RegisterField 

31 from hdl_registers.register import Register 

32 from hdl_registers.register_array import RegisterArray 

33 

34 

35class CppImplementationGenerator(CppGeneratorCommon): 

36 """ 

37 Generate a C++ class implementation. 

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

39 

40 The class implementation will contain: 

41 

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

43 register as an ``uint``. 

44 

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

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

47 

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

49 depending on the mode of the register. 

50 """ 

51 

52 __version__ = "2.0.2" 

53 

54 SHORT_DESCRIPTION = "C++ implementation" 

55 

56 DEFAULT_INDENTATION_LEVEL = 2 

57 

58 @property 

59 def output_file(self) -> Path: 

60 """ 

61 Result will be placed in this file. 

62 """ 

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

64 

65 def get_code( 

66 self, 

67 **kwargs: Any, # noqa: ANN401, ARG002 

68 ) -> str: 

69 """ 

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

71 """ 

72 cpp_code = f"""\ 

73{self._get_macros()}\ 

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

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

76 m_assertion_handler(assertion_handler) 

77 { 

78 // Empty 

79 } 

80""" 

81 

82 separator = self.get_separator_line() 

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

84 cpp_code += self._get_register_heading( 

85 register=register, register_array=register_array, separator=separator 

86 ) 

87 

88 methods_cpp: list[str] = [] 

89 

90 if register.mode.software_can_read: 

91 methods_cpp.append( 

92 self._get_register_getter(register=register, register_array=register_array) 

93 ) 

94 

95 if register.fields: 

96 # The main getter will perform type conversion. 

97 # Provide a getter that returns the raw value also. 

98 methods_cpp.append( 

99 self._get_register_raw_getter( 

100 register=register, register_array=register_array 

101 ) 

102 ) 

103 

104 for field in register.fields: 

105 methods_cpp.append( 

106 self._get_field_getter( 

107 register=register, register_array=register_array, field=field 

108 ) 

109 ) 

110 methods_cpp.append( 

111 self._get_field_getter_from_raw(register, register_array, field=field) 

112 ) 

113 

114 if register.mode.software_can_write: 

115 methods_cpp.append( 

116 self._get_register_setter(register=register, register_array=register_array) 

117 ) 

118 

119 if register.fields: 

120 # The main getter will perform type conversion. 

121 # Provide a setter that takes a raw value also. 

122 methods_cpp.append( 

123 self._get_register_raw_setter( 

124 register=register, register_array=register_array 

125 ) 

126 ) 

127 

128 for field in register.fields: 

129 methods_cpp.append( 

130 self._get_field_setter( 

131 register=register, register_array=register_array, field=field 

132 ) 

133 ) 

134 methods_cpp.append( 

135 self._get_field_to_raw(register, register_array, field=field) 

136 ) 

137 

138 cpp_code += "\n".join(methods_cpp) 

139 cpp_code += separator 

140 

141 cpp_code += "\n" 

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

143 

144 return cpp_code_top + self._with_namespace(cpp_code) 

145 

146 def _get_macros(self) -> str: 

147 file_name = self.output_file.name 

148 

149 def get_macro(name: str) -> str: 

150 macro_name = f"_{name}_ASSERT_TRUE" 

151 guard_name = f"NO_REGISTER_{name}_ASSERT" 

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

153 file_name_space = " " * (44 - len(file_name)) 

154 base = """\ 

155#ifdef {guard_name} 

156 

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

158 

159#else // Not {guard_name}. 

160 

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

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

163 {{ \\ 

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

165 std::ostringstream diagnostics; \\ 

166 diagnostics << "{file_name}:" << __LINE__ {file_name_space}\\ 

167 << ": " << message << "."; \\ 

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

169 m_assertion_handler(&diagnostic_message); \\ 

170 }} \\ 

171 }} 

172 

173#endif // {guard_name}. 

174""" 

175 return base.format( 

176 guard_name=guard_name, 

177 macro_name=macro_name, 

178 name=name, 

179 name_space=name_space, 

180 file_name=file_name, 

181 file_name_space=file_name_space, 

182 ) 

183 

184 setter_assert = get_macro(name="SETTER") 

185 getter_assert = get_macro(name="GETTER") 

186 array_index_assert = get_macro(name="ARRAY_INDEX") 

187 return f"""\ 

188{setter_assert} 

189{getter_assert} 

190{array_index_assert} 

191""" 

192 

193 def _get_register_getter(self, register: Register, register_array: RegisterArray | None) -> str: 

194 comment = self._get_getter_comment() 

195 return_type = self._get_register_value_type( 

196 register=register, register_array=register_array 

197 ) 

198 signature = self._register_getter_signature( 

199 register=register, register_array=register_array 

200 ) 

201 

202 if register.fields: 

203 raw_value = self._get_read_raw_value_call( 

204 register=register, register_array=register_array 

205 ) 

206 

207 fields = "" 

208 values: list[str] = [] 

209 for field in register.fields: 

210 field_type = self._get_field_value_type( 

211 register=register, register_array=register_array, field=field 

212 ) 

213 getter_name = self._field_getter_name( 

214 register=register, register_array=register_array, field=field, from_raw=True 

215 ) 

216 fields += f" const {field_type} {field.name}_value = {getter_name}(raw_value);\n" 

217 values.append(f"{field.name}_value") 

218 

219 value = ", ".join(values) 

220 result = f"""\ 

221{raw_value} 

222{fields} 

223 return { {value}} ;\ 

224""" 

225 else: 

226 raw_value = self._get_read_raw_value_code( 

227 register=register, register_array=register_array 

228 ) 

229 result = f"""\ 

230{raw_value} 

231 return raw_value;\ 

232""" 

233 

234 return f"""\ 

235{comment}\ 

236 {return_type} {self._class_name}::{signature} 

237 { 

238{result} 

239 } 

240""" 

241 

242 def _get_read_raw_value_call( 

243 self, register: Register, register_array: RegisterArray | None 

244 ) -> str: 

245 getter_name = self._register_getter_name( 

246 register=register, register_array=register_array, raw=True 

247 ) 

248 array_index = "array_index" if register_array else "" 

249 return f"""\ 

250 const uint32_t raw_value = {getter_name}({array_index}); 

251""" 

252 

253 def _get_read_raw_value_code( 

254 self, 

255 register: Register, 

256 register_array: RegisterArray | None, 

257 include_index: bool = True, 

258 ) -> str: 

259 index = ( 

260 self._get_index(register=register, register_array=register_array) 

261 if include_index 

262 else "" 

263 ) 

264 return f"""\ 

265{index}\ 

266 const uint32_t raw_value = m_registers[index]; 

267""" 

268 

269 def _get_index(self, register: Register, register_array: RegisterArray | None) -> str: 

270 if register_array: 

271 checker = f"""\ 

272 _ARRAY_INDEX_ASSERT_TRUE( 

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

274 "Got '{register_array.name}' array index out of range: " << array_index 

275 ); 

276""" 

277 index = ( 

278 f"{register_array.base_index} " 

279 f"+ array_index * {len(register_array.registers)} + {register.index}" 

280 ) 

281 else: 

282 checker = "" 

283 index = str(register.index) 

284 

285 return f"""\ 

286{checker}\ 

287 const size_t index = {index}; 

288""" 

289 

290 def _get_register_raw_getter( 

291 self, register: Register, register_array: RegisterArray | None 

292 ) -> str: 

293 comment = self._get_getter_comment(raw=True) 

294 signature = self._register_getter_signature( 

295 register=register, register_array=register_array, raw=True 

296 ) 

297 raw_value = self._get_read_raw_value_code(register=register, register_array=register_array) 

298 

299 return f"""\ 

300{comment}\ 

301 uint32_t {self._class_name}::{signature} 

302 { 

303{raw_value} 

304 return raw_value; 

305 } 

306""" 

307 

308 def _get_field_getter( 

309 self, register: Register, register_array: RegisterArray | None, field: RegisterField 

310 ) -> str: 

311 comment = self._get_getter_comment(field=field) 

312 field_type = self._get_field_value_type( 

313 register=register, register_array=register_array, field=field 

314 ) 

315 signature = self._field_getter_signature( 

316 register=register, 

317 register_array=register_array, 

318 field=field, 

319 from_raw=False, 

320 ) 

321 raw_value = self._get_read_raw_value_call(register=register, register_array=register_array) 

322 from_raw_name = self._field_getter_name( 

323 register=register, register_array=register_array, field=field, from_raw=True 

324 ) 

325 

326 return f"""\ 

327{comment}\ 

328 {field_type} {self._class_name}::{signature} 

329 { 

330{raw_value} 

331 return {from_raw_name}(raw_value); 

332 } 

333""" 

334 

335 def _get_field_getter_from_raw( 

336 self, register: Register, register_array: RegisterArray | None, field: RegisterField 

337 ) -> str: 

338 namespace = self._get_namespace( 

339 register=register, register_array=register_array, field=field 

340 ) 

341 comment = self._get_from_raw_comment(field=field) 

342 field_type = self._get_field_value_type( 

343 register=register, register_array=register_array, field=field 

344 ) 

345 signature = self._field_getter_signature( 

346 register=register, register_array=register_array, field=field, from_raw=True 

347 ) 

348 cast = self._get_from_raw_cast(field=field, field_type=field_type) 

349 checker = self._get_field_checker(field=field, setter_or_getter="getter") 

350 

351 return f"""\ 

352{comment}\ 

353 {field_type} {self._class_name}::{signature} 

354 { 

355 const uint32_t result_masked = register_value & {namespace}mask_shifted; 

356 const uint32_t result_shifted = result_masked >> {namespace}shift; 

357 

358{cast} 

359{checker}\ 

360 return field_value; 

361 } 

362""" 

363 

364 def _get_from_raw_cast(self, field: RegisterField, field_type: str) -> str: # noqa: PLR0911 

365 no_cast = """\ 

366 // No casting needed. 

367 const uint32_t field_value = result_shifted; 

368""" 

369 

370 if isinstance(field, Bit): 

371 return """\ 

372 // Convert to the result type. 

373 const bool field_value = static_cast<bool>(result_shifted); 

374""" 

375 

376 if isinstance(field, BitVector): 

377 if isinstance(field.numerical_interpretation, Unsigned): 

378 return no_cast 

379 

380 if isinstance(field.numerical_interpretation, Signed): 

381 return self._get_field_to_negative(field=field) 

382 

383 if isinstance(field.numerical_interpretation, UnsignedFixedPoint): 

384 return self._get_field_to_real( 

385 field=field, field_type=field_type, variable="result_shifted" 

386 ) 

387 

388 if isinstance(field.numerical_interpretation, SignedFixedPoint): 

389 return ( 

390 self._get_field_to_negative(field=field, variable="result_negative") 

391 + "\n" 

392 + self._get_field_to_real( 

393 field=field, field_type=field_type, variable="result_negative" 

394 ) 

395 ) 

396 

397 raise TypeError( 

398 f"Got unexpected numerical interpretation type: {field.numerical_interpretation}" 

399 ) 

400 

401 if isinstance(field, Enumeration): 

402 return f"""\ 

403 // "Cast" to the enum type. 

404 const auto field_value = {field_type}(result_shifted); 

405""" 

406 

407 if isinstance(field, Integer): 

408 if field.is_signed: 

409 return self._get_field_to_negative(field=field) 

410 

411 return no_cast 

412 

413 raise TypeError(f"Got unexpected field type: {field}") 

414 

415 def _get_field_to_negative(self, field: Integer, variable: str = "field_value") -> str: 

416 # Note that the shift result has maximum value of '1 << 31', which always 

417 # fits in a 32-bit unsigned integer. 

418 return f"""\ 

419 const uint32_t sign_bit_mask = 1uL << {field.width - 1}; 

420 int32_t {variable}; 

421 if (result_shifted & sign_bit_mask) 

422 { 

423 // Value is to be interpreted as negative. 

424 // This can be seen as a sign extension from the width of the field to the width of 

425 // the result variable. 

426 {variable} = result_shifted - 2 * sign_bit_mask; 

427 } 

428 else 

429 { 

430 // Value is positive. 

431 {variable} = result_shifted; 

432 } 

433""" 

434 

435 def _get_field_to_real(self, field: BitVector, field_type: str, variable: str) -> str: 

436 divisor = 2**field.numerical_interpretation.fraction_bit_width 

437 return f"""\ 

438 const {field_type} result_real = static_cast<{field_type}>({variable}); 

439 const {field_type} field_value = result_real / {divisor}; 

440""" 

441 

442 def _get_field_checker( 

443 self, field: RegisterField, setter_or_getter: Literal["setter", "getter"] 

444 ) -> str: 

445 if isinstance(field, Bit): 

446 # Values is represented as boolean in C++, and in HDL it is a single bit. 

447 # Can not be out of range in either direction. 

448 return "" 

449 

450 if isinstance(field, BitVector): 

451 if setter_or_getter == "getter": 

452 # HDL can by definition not use bits outside the field. 

453 return "" 

454 

455 # If the maximum value is the natural maximum value of the field, this check is moot. 

456 # Add guard for this in the future. 

457 # https://github.com/hdl-registers/hdl-registers/issues/169 

458 max_value = field.numerical_interpretation.max_value 

459 

460 # Minimum value check would be moot if unsigned, since the C++ type used will 

461 # be 'uint32_t'. 

462 min_value = ( 

463 None 

464 if isinstance(field.numerical_interpretation, Unsigned) 

465 else field.numerical_interpretation.min_value 

466 ) 

467 

468 return self._get_checker( 

469 field_name=field.name, 

470 setter_or_getter=setter_or_getter, 

471 min_value=min_value, 

472 max_value=max_value, 

473 ) 

474 

475 if isinstance(field, Enumeration): 

476 # There should be a check that getter value is within range. 

477 # Unless, the maximum value is the natural maximum value of the field. 

478 # https://github.com/hdl-registers/hdl-registers/issues/169 

479 return "" 

480 

481 if isinstance(field, Integer): 

482 # If the maximum value is the natural maximum value of the field, this check is moot. 

483 # But only for getters, the setter can still be out of range 

484 # unless the field width is 32. 

485 # Add guard for this in the future. 

486 # https://github.com/hdl-registers/hdl-registers/issues/169 

487 max_value = field.max_value 

488 

489 # Minimum value check would be moot if unsigned and minimum value zero, since the C++ 

490 # type used will be 'uint32_t'. 

491 # Note that there is the case where the field is unsigned, but has a minimum allowed 

492 # value that is greater than zero. 

493 # In that case, the minimum value check is still needed. 

494 # 

495 # If the minimum value is the natural minimum value of the field, signed or unsigned, 

496 # this check is moot. 

497 # Add guard for this in the future. 

498 # https://github.com/hdl-registers/hdl-registers/issues/169 

499 min_value = field.min_value if field.is_signed or field.min_value != 0 else None 

500 

501 return self._get_checker( 

502 field_name=field.name, 

503 setter_or_getter=setter_or_getter, 

504 min_value=min_value, 

505 max_value=max_value, 

506 ) 

507 

508 raise TypeError(f"Got unexpected field type: {field}") 

509 

510 def _get_checker( 

511 self, 

512 field_name: str, 

513 setter_or_getter: Literal["setter", "getter"], 

514 min_value: str | None = None, 

515 max_value: str | None = None, 

516 ) -> str: 

517 checks: list[str] = [] 

518 if min_value is not None: 

519 checks.append(f"field_value >= {min_value}") 

520 if max_value is not None: 

521 checks.append(f"field_value <= {max_value}") 

522 check = " && ".join(checks) 

523 

524 if not check: 

525 return "" 

526 

527 macro = f"_{setter_or_getter.upper()}_ASSERT_TRUE" 

528 

529 return f"""\ 

530 {macro}( 

531 {check}, 

532 "Got '{field_name}' value out of range: " << field_value 

533 ); 

534 

535""" 

536 

537 def _get_register_setter(self, register: Register, register_array: RegisterArray | None) -> str: 

538 comment = self._get_setter_comment(register=register) 

539 signature = self._register_setter_signature( 

540 register=register, register_array=register_array 

541 ) 

542 

543 if register.fields: 

544 cast = "" 

545 values: list[str] = [] 

546 for field in register.fields: 

547 to_raw_name = self._field_to_raw_name( 

548 register=register, register_array=register_array, field=field 

549 ) 

550 cast += ( 

551 f" const uint32_t {field.name}_value = " 

552 f"{to_raw_name}(register_value.{field.name});\n" 

553 ) 

554 values.append(f"{field.name}_value") 

555 

556 value = " | ".join(values) 

557 cast += f"""\ 

558 const uint32_t raw_value = {value}; 

559 

560""" 

561 set_raw_value = self._get_write_raw_value_call( 

562 register=register, register_array=register_array 

563 ) 

564 else: 

565 cast = "" 

566 set_raw_value = self._get_write_raw_value_code( 

567 register=register, register_array=register_array 

568 ) 

569 

570 return f"""\ 

571{comment}\ 

572 void {self._class_name}::{signature} 

573 { 

574{cast}\ 

575{set_raw_value}\ 

576 } 

577""" 

578 

579 def _get_write_raw_value_call( 

580 self, register: Register, register_array: RegisterArray | None 

581 ) -> str: 

582 setter_name = self._register_setter_name( 

583 register=register, register_array=register_array, raw=True 

584 ) 

585 array_index = "array_index, " if register_array else "" 

586 return f"""\ 

587 {setter_name}({array_index}raw_value); 

588""" 

589 

590 def _get_write_raw_value_code( 

591 self, 

592 register: Register, 

593 register_array: RegisterArray | None, 

594 include_index: bool = True, 

595 ) -> str: 

596 index = ( 

597 self._get_index(register=register, register_array=register_array) 

598 if include_index 

599 else "" 

600 ) 

601 return f"""\ 

602{index}\ 

603 m_registers[index] = register_value; 

604""" 

605 

606 def _get_register_raw_setter( 

607 self, register: Register, register_array: RegisterArray | None 

608 ) -> str: 

609 comment = self._get_setter_comment(register=register, raw=True) 

610 signature = self._register_setter_signature( 

611 register=register, register_array=register_array, raw=True 

612 ) 

613 set_raw_value = self._get_write_raw_value_code( 

614 register=register, register_array=register_array 

615 ) 

616 

617 return f"""\ 

618{comment}\ 

619 void {self._class_name}::{signature} 

620 { 

621{set_raw_value}\ 

622 } 

623""" 

624 

625 def _get_field_setter( 

626 self, register: Register, register_array: RegisterArray | None, field: RegisterField 

627 ) -> str: 

628 comment = self._get_setter_comment(register=register, field=field) 

629 signature = self._field_setter_signature( 

630 register=register, 

631 register_array=register_array, 

632 field=field, 

633 from_raw=False, 

634 ) 

635 index = self._get_index(register=register, register_array=register_array) 

636 

637 if self.field_setter_should_read_modify_write(register=register): 

638 namespace = self._get_namespace( 

639 register=register, register_array=register_array, field=field 

640 ) 

641 raw_value = self._get_read_raw_value_code( 

642 register=register, register_array=register_array, include_index=False 

643 ) 

644 base_value = f"""\ 

645{raw_value}\ 

646 const uint32_t mask_shifted_inverse = ~{namespace}mask_shifted; 

647 const uint32_t base_value = raw_value & mask_shifted_inverse; 

648""" 

649 

650 else: 

651 # The '0' is needed in case there are no other fields than the one we are writing. 

652 default_values = ["0"] 

653 for loop_field in register.fields: 

654 if loop_field.name != field.name: 

655 namespace = self._get_namespace( 

656 register=register, register_array=register_array, field=loop_field 

657 ) 

658 default_values.append(f"{namespace}default_value_raw") 

659 

660 default_value = " | ".join(default_values) 

661 base_value = f"""\ 

662 const uint32_t base_value = {default_value}; 

663""" 

664 

665 to_raw_name = self._field_to_raw_name( 

666 register=register, register_array=register_array, field=field 

667 ) 

668 write_raw_value = self._get_write_raw_value_code( 

669 register=register, register_array=register_array, include_index=False 

670 ) 

671 

672 return f"""\ 

673{comment}\ 

674 void {self._class_name}::{signature} 

675 { 

676{index} 

677{base_value}\ 

678 

679 const uint32_t field_value_raw = {to_raw_name}(field_value); 

680 const uint32_t register_value = base_value | field_value_raw; 

681 

682{write_raw_value}\ 

683 } 

684""" 

685 

686 def _get_field_to_raw( 

687 self, register: Register, register_array: RegisterArray | None, field: RegisterField 

688 ) -> str: 

689 comment = self._get_to_raw_comment(field=field) 

690 signature = self._field_to_raw_signature( 

691 register=register, register_array=register_array, field=field 

692 ) 

693 namespace = self._get_namespace( 

694 register=register, register_array=register_array, field=field 

695 ) 

696 checker = self._get_field_checker(field=field, setter_or_getter="setter") 

697 cast, variable = self._get_to_raw_cast( 

698 register=register, register_array=register_array, field=field 

699 ) 

700 

701 return f"""\ 

702{comment}\ 

703 uint32_t {self._class_name}::{signature} 

704 { 

705{checker}\ 

706{cast}\ 

707 const uint32_t field_value_shifted = {variable} << {namespace}shift; 

708 

709 return field_value_shifted; 

710 } 

711""" 

712 

713 def _get_to_raw_cast( # noqa: C901, PLR0911 

714 self, register: Register, register_array: RegisterArray | None, field: RegisterField 

715 ) -> tuple[str, str]: 

716 # Useful for values that are in an unsigned integer representation, but not 

717 # explicitly 'uint32_t'. 

718 cast_to_uint32 = """\ 

719 const uint32_t field_value_casted = static_cast<uint32_t>(field_value); 

720""" 

721 

722 def _get_reinterpret_as_uint32(variable: str = "field_value") -> str: 

723 # Reinterpret as unsigned and then mask out all the sign extended bits above 

724 # the field. Useful for signed integer values. 

725 # Signed to unsigned static cast produces no change in the bit pattern 

726 # https://stackoverflow.com/a/1751368 

727 namespace = self._get_namespace( 

728 register=register, register_array=register_array, field=field 

729 ) 

730 return f"""\ 

731 const uint32_t field_value_unsigned = (uint32_t){variable}; 

732 const uint32_t field_value_masked = field_value_unsigned & {namespace}mask_at_base; 

733""" 

734 

735 if isinstance(field, Bit): 

736 return (cast_to_uint32, "field_value_casted") 

737 

738 if isinstance(field, BitVector): 

739 if isinstance(field.numerical_interpretation, Unsigned): 

740 return ("", "field_value") 

741 

742 if isinstance(field.numerical_interpretation, Signed): 

743 return (_get_reinterpret_as_uint32(), "field_value_masked") 

744 

745 value_type = self._get_field_value_type( 

746 register=register, register_array=register_array, field=field 

747 ) 

748 multiplier = 2**field.numerical_interpretation.fraction_bit_width 

749 

750 fixed_type = "int32_t" if field.numerical_interpretation.is_signed else "uint32_t" 

751 

752 # Static cast implies truncation, which should guarantee that the 

753 # fixed-point representation fits in the field. 

754 to_fixed = f"""\ 

755 const {value_type} field_value_multiplied = field_value * {multiplier}; 

756 const {fixed_type} field_value_fixed = static_cast<{fixed_type}>(field_value_multiplied); 

757""" 

758 

759 if isinstance(field.numerical_interpretation, UnsignedFixedPoint): 

760 return (to_fixed, "field_value_fixed") 

761 

762 if isinstance(field.numerical_interpretation, SignedFixedPoint): 

763 return ( 

764 to_fixed + _get_reinterpret_as_uint32(variable="field_value_fixed"), 

765 "field_value_masked", 

766 ) 

767 

768 raise TypeError( 

769 f"Got unexpected numerical interpretation: {field.numerical_interpretation}" 

770 ) 

771 

772 if isinstance(field, Enumeration): 

773 return (cast_to_uint32, "field_value_casted") 

774 

775 if isinstance(field, Integer): 

776 if field.is_signed: 

777 return (_get_reinterpret_as_uint32(), "field_value_masked") 

778 

779 return ("", "field_value") 

780 

781 raise TypeError(f"Got unexpected field type: {field}")