Coverage for hdl_registers/generator/vhdl/simulation/read_write_package.py: 97%

141 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-07 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_vector import BitVector 

16from hdl_registers.field.numerical_interpretation import Signed, Unsigned 

17 

18# Local folder libraries 

19from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon 

20 

21if TYPE_CHECKING: 

22 # First party libraries 

23 from hdl_registers.field.register_field import RegisterField 

24 from hdl_registers.register import Register 

25 from hdl_registers.register_array import RegisterArray 

26 

27 

28class VhdlSimulationReadWritePackageGenerator(VhdlSimulationGeneratorCommon): 

29 """ 

30 Generate VHDL code with register read/write procedures that simplify simulation. 

31 See the :ref:`generator_vhdl` article for usage details. 

32 

33 * For each readable register, a procedure that reads the register and converts the value to the 

34 natively-typed record. 

35 

36 * For each field in each readable register, a procedure that reads the natively-typed value of 

37 the field. 

38 

39 * For each writeable register, a procedure that writes a given natively-typed record value. 

40 

41 * For each field in each writeable register, a procedure that writes a given field value. 

42 

43 Uses VUnit Verification Component calls to create bus read/write operations. 

44 

45 The generated VHDL file needs also the generated packages from 

46 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

47 """ 

48 

49 __version__ = "1.0.0" 

50 

51 SHORT_DESCRIPTION = "VHDL simulation read/write package" 

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}_register_read_write_pkg.vhd" 

59 

60 def create(self, **kwargs: Any) -> Path: 

61 """ 

62 See super class for API details. 

63 

64 Overloaded here because this package file shall only be created if the register list 

65 actually has any registers. 

66 """ 

67 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

68 

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

70 """ 

71 Get a package with methods for reading/writing registers. 

72 """ 

73 package_name = self.output_file.stem 

74 

75 vhdl = f"""\ 

76{self.header} 

77library ieee; 

78use ieee.numeric_std.all; 

79use ieee.std_logic_1164.all; 

80 

81library vunit_lib; 

82use vunit_lib.bus_master_pkg.bus_master_t; 

83use vunit_lib.bus_master_pkg.read_bus; 

84use vunit_lib.bus_master_pkg.write_bus; 

85use vunit_lib.com_types_pkg.network_t; 

86 

87library common; 

88use common.addr_pkg.addr_t; 

89use common.addr_pkg.addr_width; 

90 

91library reg_file; 

92use reg_file.reg_file_pkg.reg_t; 

93use reg_file.reg_file_pkg.reg_width; 

94use reg_file.reg_operations_pkg.regs_bus_master; 

95 

96use work.{self.name}_regs_pkg.all; 

97use work.{self.name}_register_record_pkg.all; 

98 

99 

100package {package_name} is 

101 

102{self._declarations()}\ 

103end package; 

104 

105package body {package_name} is 

106 

107{self._implementations()}\ 

108end package body; 

109""" 

110 

111 return vhdl 

112 

113 def _declarations(self) -> str: 

114 """ 

115 Get procedure declarations for all procedures. 

116 """ 

117 separator = self.get_separator_line(indent=2) 

118 vhdl = "" 

119 

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

121 register_name = self.qualified_register_name( 

122 register=register, register_array=register_array 

123 ) 

124 declarations = [] 

125 

126 if register.mode.software_can_read: 

127 if register.fields: 

128 # Read the register as a record. 

129 signature = self._register_read_write_signature( 

130 is_read_not_write=True, 

131 register=register, 

132 register_array=register_array, 

133 value_type=f"{register_name}_t", 

134 ) 

135 declarations.append(f"{signature};\n") 

136 else: 

137 # Read the register as a plain SLV, since it has no fields. 

138 signature = self._register_read_write_signature( 

139 is_read_not_write=True, 

140 register=register, 

141 register_array=register_array, 

142 value_type="reg_t", 

143 ) 

144 declarations.append(f"{signature};\n") 

145 

146 # Read the register as a plain SLV casted to integer. 

147 signature = self._register_read_write_signature( 

148 is_read_not_write=True, 

149 register=register, 

150 register_array=register_array, 

151 value_type="integer", 

152 ) 

153 declarations.append(f"{signature};\n") 

154 

155 for field in register.fields: 

156 # Read the field as its native type. 

157 value_type = self.field_type_name( 

158 register=register, register_array=register_array, field=field 

159 ) 

160 signature = self._field_read_write_signature( 

161 is_read_not_write=True, 

162 register=register, 

163 register_array=register_array, 

164 field=field, 

165 value_type=value_type, 

166 ) 

167 declarations.append(f"{signature};\n") 

168 

169 if self._should_be_able_to_access_field_as_integer(field=field): 

170 # Read the field casted to an integer. 

171 signature = self._field_read_write_signature( 

172 is_read_not_write=True, 

173 register=register, 

174 register_array=register_array, 

175 field=field, 

176 value_type="integer", 

177 ) 

178 declarations.append(f"{signature};\n") 

179 

180 if register.mode.software_can_write: 

181 if register.fields: 

182 # Write the register as a record. 

183 signature = self._register_read_write_signature( 

184 is_read_not_write=False, 

185 register=register, 

186 register_array=register_array, 

187 value_type=f"{register_name}_t", 

188 ) 

189 declarations.append(f"{signature};\n") 

190 else: 

191 # Write the register as a plain SLV, since it has no fields. 

192 signature = self._register_read_write_signature( 

193 is_read_not_write=False, 

194 register=register, 

195 register_array=register_array, 

196 value_type="reg_t", 

197 ) 

198 declarations.append(f"{signature};\n") 

199 

200 # Write the register as an integer. 

201 signature = self._register_read_write_signature( 

202 is_read_not_write=False, 

203 register=register, 

204 register_array=register_array, 

205 value_type="integer", 

206 ) 

207 declarations.append(f"{signature};\n") 

208 

209 for field in register.fields: 

210 # Write the field as its native type. 

211 value_type = self.field_type_name( 

212 register=register, register_array=register_array, field=field 

213 ) 

214 signature = self._field_read_write_signature( 

215 is_read_not_write=False, 

216 register=register, 

217 register_array=register_array, 

218 field=field, 

219 value_type=value_type, 

220 ) 

221 declarations.append(f"{signature};\n") 

222 

223 if self._should_be_able_to_access_field_as_integer(field=field): 

224 # Write the field casted to an integer. 

225 signature = self._field_read_write_signature( 

226 is_read_not_write=False, 

227 register=register, 

228 register_array=register_array, 

229 field=field, 

230 value_type="integer", 

231 ) 

232 declarations.append(f"{signature};\n") 

233 

234 vhdl += separator 

235 vhdl += "\n".join(declarations) 

236 vhdl += separator 

237 vhdl += "\n" 

238 

239 return vhdl 

240 

241 def _register_read_write_signature( 

242 self, 

243 is_read_not_write: bool, 

244 register: "Register", 

245 register_array: Optional["RegisterArray"], 

246 value_type: str, 

247 ) -> str: 

248 """ 

249 Get signature for a 'read_reg'/'write_reg' procedure. 

250 """ 

251 direction = "read" if is_read_not_write else "write" 

252 value_direction = "out" if is_read_not_write else "in" 

253 

254 register_name = self.qualified_register_name( 

255 register=register, register_array=register_array 

256 ) 

257 register_description = self.register_description( 

258 register=register, register_array=register_array 

259 ) 

260 # If it is not either of these, then it is the native type which shall not have a comment 

261 # since it is the default. 

262 type_comment = ( 

263 " as a plain 'reg_t'" 

264 if value_type == "reg_t" 

265 else " as an 'integer'" if value_type == "integer" else "" 

266 ) 

267 

268 return f"""\ 

269 -- {direction.capitalize()} the {register_description}{type_comment}. 

270 procedure {direction}_{register_name}( 

271 signal net : inout network_t; 

272{self.get_array_index_port(register_array=register_array)}\ 

273 value : {value_direction} {value_type}; 

274 base_address : in addr_t := (others => '0'); 

275 bus_handle : in bus_master_t := regs_bus_master 

276 )\ 

277""" 

278 

279 def _field_read_write_signature( 

280 self, 

281 is_read_not_write: bool, 

282 register: "Register", 

283 register_array: Optional["RegisterArray"], 

284 field: "RegisterField", 

285 value_type: str, 

286 ) -> str: 

287 """ 

288 Get signature for a 'read_field'/'write_field' procedure. 

289 """ 

290 direction = "read" if is_read_not_write else "write" 

291 

292 field_name = self.qualified_field_name( 

293 register=register, register_array=register_array, field=field 

294 ) 

295 field_description = self.field_description( 

296 register=register, field=field, register_array=register_array 

297 ) 

298 # If its not integer, then it is the native type which shall shall not have a comment 

299 # since it is the default. 

300 type_comment = " as an 'integer'" if value_type == "integer" else "" 

301 

302 comment = "" 

303 if not is_read_not_write: 

304 if self.field_setter_should_read_modify_write(register=register): 

305 comment = ( 

306 " -- Will read-modify-write the register to set the field to the " 

307 "supplied 'value'.\n" 

308 ) 

309 else: 

310 comment = ( 

311 " -- Will write the whole register, with the field set to the \n" 

312 " -- supplied 'value' and everything else set to default.\n" 

313 ) 

314 

315 value_direction = "out" if is_read_not_write else "in" 

316 

317 return f"""\ 

318 -- {direction.capitalize()} the {field_description}{type_comment}. 

319{comment}\ 

320 procedure {direction}_{field_name}( 

321 signal net : inout network_t; 

322{self.get_array_index_port(register_array=register_array)}\ 

323 value : {value_direction} {value_type}; 

324 base_address : in addr_t := (others => '0'); 

325 bus_handle : in bus_master_t := regs_bus_master 

326 )\ 

327""" 

328 

329 @staticmethod 

330 def _should_be_able_to_access_field_as_integer(field: "RegisterField") -> bool: 

331 """ 

332 Return True if the field is of a type where there should be procedures to read/write it 

333 casted as an integer. 

334 """ 

335 return isinstance(field, BitVector) and isinstance( 

336 field.numerical_interpretation, (Signed, Unsigned) 

337 ) 

338 

339 def _implementations(self) -> str: 

340 """ 

341 Get implementations of all procedures. 

342 """ 

343 separator = self.get_separator_line(indent=2) 

344 vhdl = "" 

345 

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

347 register_name = self.qualified_register_name( 

348 register=register, register_array=register_array 

349 ) 

350 implementations = [] 

351 

352 if register.mode.software_can_read: 

353 if register.fields: 

354 # Read the register as a record. 

355 implementations.append( 

356 self._register_read_implementation( 

357 register=register, 

358 register_array=register_array, 

359 value_type=f"{register_name}_t", 

360 value_conversion=f"to_{register_name}(reg_value)", 

361 ) 

362 ) 

363 else: 

364 # Read the register as a plain SLV, since it has no fields. 

365 implementations.append( 

366 self._register_read_implementation( 

367 register=register, 

368 register_array=register_array, 

369 value_type="reg_t", 

370 value_conversion="reg_value", 

371 ) 

372 ) 

373 

374 # Read the register as a plain SLV casted to integer. 

375 implementations.append( 

376 self._register_read_implementation( 

377 register=register, 

378 register_array=register_array, 

379 value_type="integer", 

380 value_conversion="to_integer(unsigned(reg_value))", 

381 ) 

382 ) 

383 

384 for field in register.fields: 

385 # Read the field as its native type. 

386 value_type = self.field_type_name( 

387 register=register, register_array=register_array, field=field 

388 ) 

389 implementations.append( 

390 self._field_read_implementation( 

391 register=register, 

392 register_array=register_array, 

393 field=field, 

394 value_type=value_type, 

395 value_conversion=f"reg_value.{field.name}", 

396 ) 

397 ) 

398 

399 if self._should_be_able_to_access_field_as_integer(field=field): 

400 # Read the field casted to an integer. 

401 implementations.append( 

402 self._field_read_implementation( 

403 register=register, 

404 register_array=register_array, 

405 field=field, 

406 value_type="integer", 

407 value_conversion=f"to_integer(reg_value.{field.name})", 

408 ) 

409 ) 

410 

411 if register.mode.software_can_write: 

412 if register.fields: 

413 # Write the register as a record. 

414 implementations.append( 

415 self._register_write_implementation( 

416 register=register, 

417 register_array=register_array, 

418 value_type=f"{register_name}_t", 

419 value_conversion="to_slv(value)", 

420 ) 

421 ) 

422 else: 

423 # Write the register as a plain SLV, since it has no fields. 

424 implementations.append( 

425 self._register_write_implementation( 

426 register=register, 

427 register_array=register_array, 

428 value_type="reg_t", 

429 value_conversion="value", 

430 ) 

431 ) 

432 

433 # Write the register as an integer. 

434 implementations.append( 

435 self._register_write_implementation( 

436 register=register, 

437 register_array=register_array, 

438 value_type="integer", 

439 value_conversion="std_ulogic_vector(to_unsigned(value, reg_width))", 

440 ) 

441 ) 

442 

443 for field in register.fields: 

444 # Write the field as its native type. 

445 value_type = self.field_type_name( 

446 register=register, register_array=register_array, field=field 

447 ) 

448 implementations.append( 

449 self._field_write_implementation( 

450 register=register, 

451 register_array=register_array, 

452 field=field, 

453 value_type=value_type, 

454 ) 

455 ) 

456 

457 if self._should_be_able_to_access_field_as_integer(field=field): 

458 # Read the field casted to an integer. 

459 implementations.append( 

460 self._field_write_implementation( 

461 register=register, 

462 register_array=register_array, 

463 field=field, 

464 value_type="integer", 

465 ) 

466 ) 

467 

468 vhdl += separator 

469 vhdl += "\n".join(implementations) 

470 vhdl += separator 

471 vhdl += "\n" 

472 

473 return vhdl 

474 

475 def _register_read_implementation( 

476 self, 

477 register: "Register", 

478 register_array: Optional["RegisterArray"], 

479 value_type: str, 

480 value_conversion: str, 

481 ) -> str: 

482 """ 

483 Get implementation for a 'read_reg' procedure. 

484 """ 

485 signature = self._register_read_write_signature( 

486 is_read_not_write=True, 

487 register=register, 

488 register_array=register_array, 

489 value_type=value_type, 

490 ) 

491 

492 return f"""\ 

493{signature} is 

494{self.reg_index_constant(register=register, register_array=register_array)}\ 

495{self.reg_address_constant()}\ 

496 variable reg_value : reg_t := (others => '0'); 

497 begin 

498 read_bus( 

499 net => net, 

500 bus_handle => bus_handle, 

501 address => std_logic_vector(reg_address), 

502 data => reg_value 

503 ); 

504 value := {value_conversion}; 

505 end procedure; 

506""" 

507 

508 def _register_write_implementation( 

509 self, 

510 register: "Register", 

511 register_array: Optional["RegisterArray"], 

512 value_type: str, 

513 value_conversion: str, 

514 ) -> str: 

515 """ 

516 Get implementation for a 'write_reg' procedure. 

517 """ 

518 signature = self._register_read_write_signature( 

519 is_read_not_write=False, 

520 register=register, 

521 register_array=register_array, 

522 value_type=value_type, 

523 ) 

524 

525 return f"""\ 

526{signature} is 

527{self.reg_index_constant(register=register, register_array=register_array)}\ 

528{self.reg_address_constant()}\ 

529 constant reg_value : reg_t := {value_conversion}; 

530 begin 

531 write_bus( 

532 net => net, 

533 bus_handle => bus_handle, 

534 address => std_logic_vector(reg_address), 

535 data => reg_value 

536 ); 

537 end procedure; 

538""" 

539 

540 def _field_read_implementation( 

541 self, 

542 register: "Register", 

543 register_array: Optional["RegisterArray"], 

544 field: "RegisterField", 

545 value_type: str, 

546 value_conversion: str, 

547 ) -> str: 

548 """ 

549 Get implementation for a 'read_field' procedure. 

550 """ 

551 signature = self._field_read_write_signature( 

552 is_read_not_write=True, 

553 register=register, 

554 register_array=register_array, 

555 field=field, 

556 value_type=value_type, 

557 ) 

558 

559 register_name = self.qualified_register_name( 

560 register=register, register_array=register_array 

561 ) 

562 

563 return f"""\ 

564{signature} is 

565 variable reg_value : {register_name}_t := {register_name}_init; 

566 begin 

567 read_{register_name}( 

568 net => net, 

569{self.get_array_index_association(register_array=register_array)}\ 

570 value => reg_value, 

571 base_address => base_address, 

572 bus_handle => bus_handle 

573 ); 

574 value := {value_conversion}; 

575 end procedure; 

576""" 

577 

578 def _field_write_implementation( 

579 self, 

580 register: "Register", 

581 register_array: Optional["RegisterArray"], 

582 field: "RegisterField", 

583 value_type: str, 

584 ) -> str: 

585 """ 

586 Get implementation for a 'write_field' procedure. 

587 """ 

588 signature = self._field_read_write_signature( 

589 is_read_not_write=False, 

590 register=register, 

591 register_array=register_array, 

592 field=field, 

593 value_type=value_type, 

594 ) 

595 register_name = self.qualified_register_name( 

596 register=register, register_array=register_array 

597 ) 

598 

599 if self.field_setter_should_read_modify_write(register=register): 

600 set_base_value = f"""\ 

601 read_{register_name}( 

602 net => net, 

603{self.get_array_index_association(register_array=register_array)}\ 

604 value => reg_value, 

605 base_address => base_address, 

606 bus_handle => bus_handle 

607 ); 

608""" 

609 else: 

610 set_base_value = "" 

611 

612 if value_type == "integer": 

613 field_name = self.qualified_field_name( 

614 register=register, register_array=register_array, field=field 

615 ) 

616 field_width = f"{field_name}_width" 

617 

618 if isinstance(field, BitVector) and isinstance( 

619 field.numerical_interpretation, Unsigned 

620 ): 

621 field_conversion = f"to_unsigned(value, {field_width})" 

622 elif isinstance(field, BitVector) and isinstance( 

623 field.numerical_interpretation, Signed 

624 ): 

625 field_conversion = f"to_signed(value, {field_width})" 

626 else: 

627 raise ValueError(f"Should not end up here for field: {field}") 

628 else: 

629 field_conversion = "value" 

630 

631 return f"""\ 

632{signature} is 

633 variable reg_value : {register_name}_t := {register_name}_init; 

634 begin 

635{set_base_value}\ 

636 reg_value.{field.name} := {field_conversion}; 

637 

638 write_{register_name}( 

639 net => net, 

640{self.get_array_index_association(register_array=register_array)}\ 

641 value => reg_value, 

642 base_address => base_address, 

643 bus_handle => bus_handle 

644 ); 

645 end procedure; 

646"""