Coverage for hdl_registers/generator/vhdl/register_package.py: 96%

193 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 pathlib import Path 

11from re import compile as re_compile 

12from typing import TYPE_CHECKING, Any 

13 

14from hdl_registers.constant.bit_vector_constant import UnsignedVectorConstant 

15from hdl_registers.constant.boolean_constant import BooleanConstant 

16from hdl_registers.constant.float_constant import FloatConstant 

17from hdl_registers.constant.integer_constant import IntegerConstant 

18from hdl_registers.constant.string_constant import StringConstant 

19from hdl_registers.field.bit import Bit 

20from hdl_registers.field.bit_vector import BitVector 

21from hdl_registers.field.enumeration import Enumeration 

22from hdl_registers.field.integer import Integer 

23from hdl_registers.field.numerical_interpretation import ( 

24 Signed, 

25 SignedFixedPoint, 

26 Unsigned, 

27 UnsignedFixedPoint, 

28) 

29from hdl_registers.register import Register 

30 

31from .vhdl_generator_common import VhdlGeneratorCommon 

32 

33if TYPE_CHECKING: 

34 from hdl_registers.field.register_field import RegisterField 

35 from hdl_registers.register_array import RegisterArray 

36 

37 

38class VhdlRegisterPackageGenerator(VhdlGeneratorCommon): 

39 """ 

40 Generate a base VHDL package with basic register information. 

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

42 

43 * For each register constant, the value as a native VHDL constant. 

44 * For each register, the index within the register list. 

45 * For each field in each register 

46 

47 * Register bit index range definitions. 

48 * Native VHDL type corresponding to the field type. 

49 * Conversion of a field value to/from SLV. 

50 

51 Also produces a constant that maps indexes to modes, suitable for use with 

52 :ref:`register_file.axi_lite_register_file` or :class:`.VhdlAxiLiteWrapperGenerator`. 

53 

54 See :ref:`vhdl_dependencies` for a note on dependencies. 

55 """ 

56 

57 __version__ = "2.0.0" 

58 

59 SHORT_DESCRIPTION = "VHDL register package" 

60 

61 @property 

62 def output_file(self) -> Path: 

63 """ 

64 Result will be placed in this file. 

65 """ 

66 return self.output_folder / f"{self.name}_regs_pkg.vhd" 

67 

68 def get_code( 

69 self, 

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

71 ) -> str: 

72 """ 

73 Get a complete VHDL package with register and constant information. 

74 """ 

75 pkg_name = f"{self.name}_regs_pkg" 

76 

77 vhdl = f"""\ 

78library ieee; 

79use ieee.std_logic_1164.all; 

80use ieee.numeric_std.all; 

81use ieee.fixed_pkg.all; 

82 

83library register_file; 

84use register_file.register_file_pkg.all; 

85 

86 

87package {pkg_name} is 

88 

89""" 

90 if self.register_list.constants: 

91 vhdl += self._constants() 

92 

93 if self.register_list.register_objects: 

94 vhdl += f"""\ 

95{self._register_range()}\ 

96{self._array_constants()}\ 

97{self._register_indexes()}\ 

98{self._register_map_head()}\ 

99{self._field_declarations()}\ 

100""" 

101 

102 vhdl += "end package;\n" 

103 

104 if self.register_list.register_objects: 

105 vhdl += f""" 

106package body {pkg_name} is 

107 

108{self._array_index_function_implementations()}\ 

109{self._register_map_body()}\ 

110{self._field_conversion_implementations()}\ 

111end package body; 

112""" 

113 

114 return vhdl 

115 

116 def _constants(self) -> str: 

117 """ 

118 A set of VHDL constants, corresponding to the provided register constants. 

119 """ 

120 vhdl = """\ 

121 -- --------------------------------------------------------------------------- 

122 -- Values of register constants. 

123""" 

124 # Match e.g. 5e60, but not 5.0e60. 

125 re_float_start_with_integer = re_compile(r"^(\d+)e") 

126 

127 for constant in self.iterate_constants(): 

128 if isinstance(constant, BooleanConstant): 

129 type_declaration = "boolean" 

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

131 elif isinstance(constant, IntegerConstant): 

132 type_declaration = "integer" 

133 value = str(constant.value) 

134 elif isinstance(constant, FloatConstant): 

135 # At least 64 bits (IEEE 1076-2008, 5.2.5.1). 

136 type_declaration = "real" 

137 # Note that casting a Python float to string guarantees full precision in the 

138 # resulting string: https://stackoverflow.com/a/60026172 

139 value = str(constant.value) 

140 

141 match = re_float_start_with_integer.match(value) 

142 if match: 

143 # "1e-3" is not valid VHDL, but "1.0e-3" is. 

144 base = match.group(1) 

145 exponent = value[match.end(1) :] 

146 value = f"{base}.0{exponent}" 

147 elif isinstance(constant, StringConstant): 

148 type_declaration = "string" 

149 value = f'"{constant.value}"' 

150 elif isinstance(constant, UnsignedVectorConstant): 

151 type_declaration = f"unsigned({constant.width} - 1 downto 0)" 

152 

153 if constant.is_hexadecimal_not_binary: 

154 # Underscore separator is allowed in VHDL when defining a hexadecimal SLV. 

155 value = f'x"{constant.value}"' 

156 else: 

157 # But not when defining a binary SLV. 

158 value = f'"{constant.value_without_separator}"' 

159 else: 

160 raise TypeError(f"Got unexpected constant type. {constant}") 

161 

162 vhdl += ( 

163 " constant " 

164 f"{self.name}_constant_{constant.name} : {type_declaration} := {value};\n" 

165 ) 

166 

167 vhdl += "\n" 

168 

169 return vhdl 

170 

171 @property 

172 def _register_range_type_name(self) -> str: 

173 """ 

174 Name of the type which is the legal index range of registers. 

175 """ 

176 return f"{self.name}_register_range" 

177 

178 def _register_range(self) -> str: 

179 """ 

180 A VHDL type that defines the legal range of register indexes. 

181 Note that this method is only called if there are any registers, so 

182 the indexing is safe. 

183 """ 

184 last_index = self.register_list.register_objects[-1].index 

185 index_width = 1 if last_index == 0 else last_index.bit_length() 

186 address_width = index_width + 2 

187 

188 return f"""\ 

189 -- --------------------------------------------------------------------------- 

190 -- The valid range of register indexes. 

191 subtype {self._register_range_type_name} is natural range 0 to {last_index}; 

192 

193 -- --------------------------------------------------------------------------- 

194 -- The number of bits needed to address all {last_index + 1} registers on a register bus. 

195 -- Note that this figure includes the lowest two address bits that are assumed zero, since 

196 -- registers are 32-bit and unaligned accesses are not supported. 

197 constant {self.name}_address_width : positive := {address_width}; 

198 

199""" 

200 

201 def _array_constants(self) -> str: 

202 """ 

203 A list of constants defining how many times each register array is repeated. 

204 """ 

205 vhdl = "" 

206 for register_array in self.iterate_register_arrays(): 

207 array_name = self.qualified_register_array_name(register_array=register_array) 

208 

209 vhdl += f"""\ 

210 -- Number of times the '{register_array.name}' register array is repeated. 

211 constant {array_name}_array_length : natural := {register_array.length}; 

212 -- Range for indexing '{register_array.name}' register array repetitions. 

213 subtype {array_name}_range is natural range 0 to {register_array.length - 1}; 

214 

215""" 

216 

217 return vhdl 

218 

219 def _array_register_index_function_signature( 

220 self, register: Register, register_array: "RegisterArray" 

221 ) -> str: 

222 """ 

223 Signature for the function that returns a register index for the specified index in a 

224 register array. 

225 """ 

226 array_name = self.qualified_register_array_name(register_array=register_array) 

227 return f"""\ 

228 function {self.qualified_register_name(register, register_array)}( 

229 array_index : {array_name}_range 

230 ) return {self._register_range_type_name}""" 

231 

232 def _register_indexes(self) -> str: 

233 """ 

234 A set of named constants for the register index of each register. 

235 """ 

236 vhdl = " -- Register indexes, within the list of registers.\n" 

237 

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

239 if register_array is None: 

240 vhdl += ( 

241 f" constant {self.qualified_register_name(register)} : " 

242 f"natural := {register.index};\n" 

243 ) 

244 else: 

245 vhdl += ( 

246 f"{self._array_register_index_function_signature(register, register_array)};\n" 

247 ) 

248 

249 vhdl += "\n" 

250 

251 return vhdl 

252 

253 def _register_map_head(self) -> str: 

254 """ 

255 Get constants mapping the register indexes to register modes. 

256 """ 

257 map_name = f"{self.name}_register_map" 

258 

259 return f"""\ 

260 -- Declare 'register_map' and 'regs_init' constants here but define them in 

261 -- the package body (deferred constants). 

262 -- So that functions have been elaborated when they are called. 

263 -- Needed for ModelSim compilation to pass. 

264 

265 -- To be used as the 'registers' generic of 'axi_lite_register_file.vhd'. 

266 constant {map_name} : register_definition_vec_t({self._register_range_type_name}); 

267 

268 -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_register_file.vhd'. 

269 subtype {self.name}_regs_t is register_vec_t({self._register_range_type_name}); 

270 -- To be used as the 'default_values' generic of 'axi_lite_register_file.vhd'. 

271 constant {self.name}_regs_init : {self.name}_regs_t; 

272 

273 -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_register_file.vhd'. 

274 subtype {self.name}_reg_was_accessed_t is \ 

275std_ulogic_vector({self._register_range_type_name}); 

276 

277""" 

278 

279 def _field_declarations(self) -> str: 

280 """ 

281 For every field in every register (plain or in array): 

282 

283 * Bit index range 

284 * VHDL type 

285 * width constant 

286 * conversion function declarations to/from type and SLV 

287 """ 

288 vhdl = "" 

289 

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

291 if not register.fields: 

292 continue 

293 

294 register_description = self.register_description( 

295 register=register, register_array=register_array 

296 ) 

297 

298 vhdl += f"""\ 

299 -- ----------------------------------------------------------------------------- 

300 -- Fields in the {register_description}. 

301""" 

302 

303 for field in register.fields: 

304 field_name = self.qualified_field_name( 

305 register=register, register_array=register_array, field=field 

306 ) 

307 field_is_bit = isinstance(field, Bit) 

308 

309 vhdl += f" -- Range of the '{field.name}' field.\n" 

310 if field_is_bit: 

311 # A bit field's "range" is simply an index, so the indexing a register SLV 

312 # gives a std_logic value. 

313 vhdl += f" constant {field_name} : natural := {field.base_index};\n" 

314 else: 

315 # For other fields its an actual range. 

316 vhdl += f"""\ 

317 subtype {field_name} is natural \ 

318range {field.width + field.base_index - 1} downto {field.base_index}; 

319 -- Width of the '{field.name}' field. 

320 constant {field_name}_width : positive := {field.width}; 

321 -- Type for the '{field.name}' field. 

322{self._field_type_declaration(field=field, field_name=field_name)} 

323""" 

324 

325 vhdl += f"""\ 

326 -- Default value of the '{field.name}' field. 

327 {self._field_init_value(field=field, field_name=field_name)} 

328{self._field_conversion_function_declarations(field=field, field_name=field_name)} 

329""" 

330 

331 return vhdl 

332 

333 def _field_type_declaration(self, field: "RegisterField", field_name: str) -> str: 

334 """ 

335 Get a type declaration for the native VHDL type that corresponds to the field's type. 

336 

337 Arguments: 

338 field: A field. 

339 field_name: The field's qualified name. 

340 """ 

341 if isinstance(field, BitVector): 

342 if isinstance(field.numerical_interpretation, Unsigned): 

343 return f" subtype {field_name}_t is u_unsigned({field.width - 1} downto 0);" 

344 

345 if isinstance(field.numerical_interpretation, Signed): 

346 return f" subtype {field_name}_t is u_signed({field.width - 1} downto 0);" 

347 

348 if isinstance(field.numerical_interpretation, UnsignedFixedPoint): 

349 return ( 

350 f" subtype {field_name}_t is ufixed(" 

351 f"{field.numerical_interpretation.max_bit_index} downto " 

352 f"{field.numerical_interpretation.min_bit_index});" 

353 ) 

354 

355 if isinstance(field.numerical_interpretation, SignedFixedPoint): 

356 return ( 

357 f" subtype {field_name}_t is sfixed(" 

358 f"{field.numerical_interpretation.max_bit_index} downto " 

359 f"{field.numerical_interpretation.min_bit_index});" 

360 ) 

361 

362 raise TypeError(f'Got unexpected bit vector type for field: "{field}".') 

363 

364 if isinstance(field, Enumeration): 

365 # Enum element names in VHDL are exported to the surrounding scope, causing huge 

366 # risk of name clashes. 

367 # At the same time, we want the elements to have somewhat concise names so they are 

368 # easy to work with. 

369 # Compromise by prefixing the element names with the field name. 

370 element_names = [f"{field.name}_{element.name}" for element in field.elements] 

371 elements = ",\n ".join(element_names) 

372 return f"""\ 

373 type {field_name}_t is ( 

374 {elements} 

375 );\ 

376""" 

377 

378 if isinstance(field, Integer): 

379 return ( 

380 f" subtype {field_name}_t is integer range {field.min_value} to {field.max_value};" 

381 ) 

382 

383 raise TypeError(f'Got unexpected type for field: "{field}".') 

384 

385 def _field_init_value(self, field: "RegisterField", field_name: str) -> str: 

386 """ 

387 Get an init value constant for the field. 

388 Uses the native VHDL type that corresponds to the field's type. 

389 

390 Arguments: 

391 field: A field. 

392 field_name: The field's qualified name. 

393 """ 

394 result = f"constant {field_name}_init :" 

395 

396 if isinstance(field, Bit): 

397 return f"{result} std_ulogic := '{field.default_value}';" 

398 

399 if isinstance(field, BitVector): 

400 return f'{result} {field_name}_t := "{field.default_value}";' 

401 

402 if isinstance(field, Enumeration): 

403 return f"{result} {field_name}_t := {field.name}_{field.default_value.name};" 

404 

405 if isinstance(field, Integer): 

406 return f"{result} {field_name}_t := {field.default_value};" 

407 

408 raise TypeError(f'Got unexpected type for field: "{field}".') 

409 

410 def _field_conversion_function_declarations( 

411 self, field: "RegisterField", field_name: str 

412 ) -> str: 

413 """ 

414 Function declarations for functions that convert the field's native VHDL representation 

415 to/from SLV. 

416 

417 Arguments: 

418 field: A field. 

419 field_name: The field's qualified name. 

420 """ 

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

422 return "" 

423 

424 if isinstance(field, (Enumeration, Integer)): 

425 to_slv_name = self.field_to_slv_function_name(field=field, field_name=field_name) 

426 

427 return f"""\ 

428 -- Type for the '{field.name}' field as an SLV. 

429 subtype {field_name}_slv_t is std_ulogic_vector({field.width - 1} downto 0); 

430 -- Cast a '{field.name}' field value to SLV. 

431 function {to_slv_name}(data : {field_name}_t) return {field_name}_slv_t; 

432 -- Get a '{field.name}' field value from a register value. 

433 function to_{field_name}(data : register_t) return {field_name}_t; 

434""" 

435 

436 raise TypeError(f'Got unexpected type for field: "{field}".') 

437 

438 def _array_index_function_implementations(self) -> str: 

439 """ 

440 Implementation for the functions that return a register index for the specified index in a 

441 register array. 

442 """ 

443 vhdl = "" 

444 for register_array in self.iterate_register_arrays(): 

445 num_registers = len(register_array.registers) 

446 for register in register_array.registers: 

447 vhdl += f"""\ 

448{self._array_register_index_function_signature(register, register_array)} is 

449 begin 

450 return {register_array.base_index} + array_index * {num_registers} + {register.index}; 

451 end function; 

452 

453""" 

454 

455 return vhdl 

456 

457 def _register_map_body(self) -> str: 

458 """ 

459 Get the body of the register map definition constants. 

460 """ 

461 map_name = f"{self.name}_register_map" 

462 range_name = f"{self.name}_register_range" 

463 

464 register_definitions = [] 

465 default_values = [] 

466 index = 0 

467 

468 def add(register: Register, index_name: str) -> None: 

469 utilized_width = self.register_utilized_width(register=register) 

470 register_definitions.append( 

471 f"{index} => (index => {index_name}, " 

472 f"mode => {register.mode.shorthand}, " 

473 f"utilized_width => {utilized_width})" 

474 ) 

475 

476 default_value = self.register_default_value_uint(register=register) 

477 default_values.append(f'{index} => "{default_value:032b}"') 

478 

479 for register_object in self.iterate_register_objects(): 

480 if isinstance(register_object, Register): 

481 add( 

482 register=register_object, 

483 index_name=self.qualified_register_name(register=register_object), 

484 ) 

485 index += 1 

486 else: 

487 for array_index in range(register_object.length): 

488 for register in register_object.registers: 

489 register_name = self.qualified_register_name( 

490 register=register, register_array=register_object 

491 ) 

492 index_name = f"{register_name}({array_index})" 

493 

494 add(register=register, index_name=index_name) 

495 index += 1 

496 

497 array_element_separator = ",\n " 

498 return f"""\ 

499 constant {map_name} : register_definition_vec_t({range_name}) := ( 

500 {array_element_separator.join(register_definitions)} 

501 ); 

502 

503 constant {self.name}_regs_init : {self.name}_regs_t := ( 

504 {array_element_separator.join(default_values)} 

505 ); 

506 

507""" 

508 

509 def _field_conversion_implementations(self) -> str: 

510 """ 

511 Implementation of functions that convert a register field's native VHDL representation 

512 to/from SLV. 

513 """ 

514 vhdl = "" 

515 

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

517 for field in register.fields: 

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

519 # Skip all field types that do not have any functions that need to 

520 # be implemented. 

521 continue 

522 

523 name = self.qualified_field_name( 

524 register=register, register_array=register_array, field=field 

525 ) 

526 to_slv_name = self.field_to_slv_function_name(field=field, field_name=name) 

527 

528 if isinstance(field, Enumeration): 

529 to_slv = f"""\ 

530 constant data_int : natural := {name}_t'pos(data); 

531 constant result : {name}_slv_t := std_ulogic_vector( 

532 to_unsigned(data_int, {name}_width) 

533 ); 

534""" 

535 from_slv = f"""\ 

536 constant field_slv : {name}_slv_t := data({name}); 

537 constant field_int : natural := to_integer(unsigned(field_slv)); 

538 constant result : {name}_t := {name}_t'val(field_int); 

539""" 

540 elif isinstance(field, Integer): 

541 vector_type = "signed" if field.is_signed else "unsigned" 

542 to_slv = f"""\ 

543 constant result : {name}_slv_t := std_ulogic_vector(to_{vector_type}(data, {name}_width)); 

544""" 

545 from_slv = f"""\ 

546 constant result : integer := to_integer({vector_type}(data({name}))); 

547""" 

548 else: 

549 raise TypeError(f'Got unexpected field type: "{field}".') 

550 

551 vhdl += f"""\ 

552 -- Cast a '{field.name}' field value to SLV. 

553 function {to_slv_name}(data : {name}_t) return {name}_slv_t is 

554{to_slv}\ 

555 begin 

556 return result; 

557 end function; 

558 

559 -- Get a '{field.name}' field value from a register value. 

560 function to_{name}(data : register_t) return {name}_t is 

561{from_slv}\ 

562 begin 

563 return result; 

564 end function; 

565 

566""" 

567 

568 return vhdl