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

193 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-28 20:51 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the hdl-registers project, an HDL register generator fast enough to run 

5# in real time. 

6# https://hdl-registers.com 

7# https://github.com/hdl-registers/hdl-registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10# Standard libraries 

11from pathlib import Path 

12from re import compile as re_compile 

13from typing import TYPE_CHECKING, Any 

14 

15# First party libraries 

16from hdl_registers.constant.bit_vector_constant import UnsignedVectorConstant 

17from hdl_registers.constant.boolean_constant import BooleanConstant 

18from hdl_registers.constant.float_constant import FloatConstant 

19from hdl_registers.constant.integer_constant import IntegerConstant 

20from hdl_registers.constant.string_constant import StringConstant 

21from hdl_registers.field.bit import Bit 

22from hdl_registers.field.bit_vector import BitVector 

23from hdl_registers.field.enumeration import Enumeration 

24from hdl_registers.field.integer import Integer 

25from hdl_registers.field.numerical_interpretation import ( 

26 Signed, 

27 SignedFixedPoint, 

28 Unsigned, 

29 UnsignedFixedPoint, 

30) 

31from hdl_registers.register import Register 

32 

33if TYPE_CHECKING: 

34 from hdl_registers.field.register_field import RegisterField 

35 from hdl_registers.register_array import RegisterArray 

36 

37# Local folder libraries 

38from .vhdl_generator_common import VhdlGeneratorCommon 

39 

40 

41class VhdlRegisterPackageGenerator(VhdlGeneratorCommon): 

42 """ 

43 Generate a base VHDL package with basic register information. 

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

45 

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

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

48 * For each field in each register 

49 

50 * Register bit index range definitions. 

51 * Native VHDL type corresponding to the field type. 

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

53 

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

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

56 """ 

57 

58 __version__ = "2.0.0" 

59 

60 SHORT_DESCRIPTION = "VHDL register package" 

61 

62 @property 

63 def output_file(self) -> Path: 

64 """ 

65 Result will be placed in this file. 

66 """ 

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

68 

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

70 """ 

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

72 """ 

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

74 

75 vhdl = f"""\ 

76library ieee; 

77use ieee.std_logic_1164.all; 

78use ieee.numeric_std.all; 

79use ieee.fixed_pkg.all; 

80 

81library register_file; 

82use register_file.register_file_pkg.all; 

83 

84 

85package {pkg_name} is 

86 

87""" 

88 if self.register_list.constants: 

89 vhdl += self._constants() 

90 

91 if self.register_list.register_objects: 

92 vhdl += f"""\ 

93{self._register_range()}\ 

94{self._array_constants()}\ 

95{self._register_indexes()}\ 

96{self._register_map_head()}\ 

97{self._field_declarations()}\ 

98""" 

99 

100 vhdl += "end package;\n" 

101 

102 if self.register_list.register_objects: 

103 vhdl += f""" 

104package body {pkg_name} is 

105 

106{self._array_index_function_implementations()}\ 

107{self._register_map_body()}\ 

108{self._field_conversion_implementations()}\ 

109end package body; 

110""" 

111 

112 return vhdl 

113 

114 def _constants(self) -> str: 

115 """ 

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

117 """ 

118 vhdl = """\ 

119 -- --------------------------------------------------------------------------- 

120 -- Values of register constants. 

121""" 

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

123 

124 for constant in self.iterate_constants(): 

125 if isinstance(constant, BooleanConstant): 

126 type_declaration = "boolean" 

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

128 elif isinstance(constant, IntegerConstant): 

129 type_declaration = "integer" 

130 value = str(constant.value) 

131 elif isinstance(constant, FloatConstant): 

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

133 type_declaration = "real" 

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

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

136 value = str(constant.value) 

137 match = re_float_start_with_integer.match(value) 

138 if match: 

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

140 value = f"{match.group(1)}.0{value[match.end(1):]}" 

141 elif isinstance(constant, StringConstant): 

142 type_declaration = "string" 

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

144 elif isinstance(constant, UnsignedVectorConstant): 

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

146 

147 if constant.is_hexadecimal_not_binary: 

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

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

150 else: 

151 # But not when defining a binary SLV. 

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

153 else: 

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

155 

156 vhdl += ( 

157 " constant " 

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

159 ) 

160 

161 vhdl += "\n" 

162 

163 return vhdl 

164 

165 @property 

166 def _register_range_type_name(self) -> str: 

167 """ 

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

169 """ 

170 return f"{self.name}_register_range" 

171 

172 def _register_range(self) -> str: 

173 """ 

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

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

176 the indexing is safe. 

177 """ 

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

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

180 address_width = index_width + 2 

181 

182 vhdl = f"""\ 

183 -- --------------------------------------------------------------------------- 

184 -- The valid range of register indexes. 

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

186 

187 -- --------------------------------------------------------------------------- 

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

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

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

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

192 

193""" 

194 return vhdl 

195 

196 def _array_constants(self) -> str: 

197 """ 

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

199 """ 

200 vhdl = "" 

201 for register_array in self.iterate_register_arrays(): 

202 array_name = self.qualified_register_array_name(register_array=register_array) 

203 

204 vhdl += f"""\ 

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

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

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

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

209 

210""" 

211 

212 return vhdl 

213 

214 def _array_register_index_function_signature( 

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

216 ) -> str: 

217 """ 

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

219 register array. 

220 """ 

221 array_name = self.qualified_register_array_name(register_array=register_array) 

222 vhdl = f"""\ 

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

224 array_index : {array_name}_range 

225 ) return {self._register_range_type_name}""" 

226 return vhdl 

227 

228 def _register_indexes(self) -> str: 

229 """ 

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

231 """ 

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

233 

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

235 if register_array is None: 

236 vhdl += ( 

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

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

239 ) 

240 else: 

241 vhdl += ( 

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

243 ) 

244 

245 vhdl += "\n" 

246 

247 return vhdl 

248 

249 def _register_map_head(self) -> str: 

250 """ 

251 Get constants mapping the register indexes to register modes. 

252 """ 

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

254 

255 vhdl = f"""\ 

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

257 -- the package body (deferred constants). 

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

259 -- Needed for ModelSim compilation to pass. 

260 

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

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

263 

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

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

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

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

268 

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

270 subtype {self.name}_reg_was_accessed_t is \ 

271std_ulogic_vector({self._register_range_type_name}); 

272 

273""" 

274 

275 return vhdl 

276 

277 def _field_declarations(self) -> str: 

278 """ 

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

280 

281 * Bit index range 

282 * VHDL type 

283 * width constant 

284 * conversion function declarations to/from type and SLV 

285 """ 

286 vhdl = "" 

287 

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

289 if not register.fields: 

290 continue 

291 

292 register_description = self.register_description( 

293 register=register, register_array=register_array 

294 ) 

295 

296 vhdl += f"""\ 

297 -- ----------------------------------------------------------------------------- 

298 -- Fields in the {register_description}. 

299""" 

300 

301 for field in register.fields: 

302 field_name = self.qualified_field_name( 

303 register=register, register_array=register_array, field=field 

304 ) 

305 field_is_bit = isinstance(field, Bit) 

306 

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

308 if field_is_bit: 

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

310 # gives a std_logic value. 

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

312 else: 

313 # For other fields its an actual range. 

314 vhdl += f"""\ 

315 subtype {field_name} is natural \ 

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

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

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

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

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

321""" 

322 

323 vhdl += f"""\ 

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

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

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

327""" 

328 

329 return vhdl 

330 

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

332 """ 

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

334 

335 Arguments: 

336 field: A field. 

337 field_name: The field's qualified name. 

338 """ 

339 if isinstance(field, BitVector): 

340 if isinstance(field.numerical_interpretation, Unsigned): 

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

342 

343 if isinstance(field.numerical_interpretation, Signed): 

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

345 

346 if isinstance(field.numerical_interpretation, UnsignedFixedPoint): 

347 return ( 

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

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

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

351 ) 

352 

353 if isinstance(field.numerical_interpretation, SignedFixedPoint): 

354 return ( 

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

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

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

358 ) 

359 

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

361 

362 if isinstance(field, Enumeration): 

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

364 # risk of name clashes. 

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

366 # easy to work with. 

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

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

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

370 return f"""\ 

371 type {field_name}_t is ( 

372 {elements} 

373 );\ 

374""" 

375 

376 if isinstance(field, Integer): 

377 return ( 

378 f" subtype {field_name}_t is integer " 

379 f"range {field.min_value} to {field.max_value};" 

380 ) 

381 

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

383 

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

385 """ 

386 Get an init value constant for the field. 

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

388 

389 Arguments: 

390 field: A field. 

391 field_name: The field's qualified name. 

392 """ 

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

394 

395 if isinstance(field, Bit): 

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

397 

398 if isinstance(field, BitVector): 

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

400 

401 if isinstance(field, Enumeration): 

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

403 

404 if isinstance(field, Integer): 

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

406 

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

408 

409 def _field_conversion_function_declarations( 

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

411 ) -> str: 

412 """ 

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

414 to/from SLV. 

415 

416 Arguments: 

417 field: A field. 

418 field_name: The field's qualified name. 

419 """ 

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

421 return "" 

422 

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

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

425 

426 return f"""\ 

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

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

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

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

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

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

433""" 

434 

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

436 

437 def _array_index_function_implementations(self) -> str: 

438 """ 

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

440 register array. 

441 """ 

442 vhdl = "" 

443 for register_array in self.iterate_register_arrays(): 

444 num_registers = len(register_array.registers) 

445 for register in register_array.registers: 

446 vhdl += f"""\ 

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

448 begin 

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

450 end function; 

451 

452""" 

453 

454 return vhdl 

455 

456 def _register_map_body(self) -> str: 

457 """ 

458 Get the body of the register map definition constants. 

459 """ 

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

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

462 

463 register_definitions = [] 

464 default_values = [] 

465 index = 0 

466 

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

468 register_definitions.append( 

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

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

471 f"utilized_width => {register.utilized_width})" 

472 ) 

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

474 

475 for register_object in self.iterate_register_objects(): 

476 if isinstance(register_object, Register): 

477 add( 

478 register=register_object, 

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

480 ) 

481 index += 1 

482 else: 

483 for array_index in range(register_object.length): 

484 for register in register_object.registers: 

485 register_name = self.qualified_register_name( 

486 register=register, register_array=register_object 

487 ) 

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

489 

490 add(register=register, index_name=index_name) 

491 index += 1 

492 

493 array_element_separator = ",\n " 

494 vhdl = f"""\ 

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

496 {array_element_separator.join(register_definitions)} 

497 ); 

498 

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

500 {array_element_separator.join(default_values)} 

501 ); 

502 

503""" 

504 

505 return vhdl 

506 

507 def _field_conversion_implementations(self) -> str: 

508 """ 

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

510 to/from SLV. 

511 """ 

512 vhdl = "" 

513 

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

515 for field in register.fields: 

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

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

518 # be implemented. 

519 continue 

520 

521 name = self.qualified_field_name( 

522 register=register, register_array=register_array, field=field 

523 ) 

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

525 

526 if isinstance(field, Enumeration): 

527 to_slv = f"""\ 

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

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

530 to_unsigned(data_int, {name}_width) 

531 ); 

532""" 

533 from_slv = f"""\ 

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

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

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

537""" 

538 elif isinstance(field, Integer): 

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

540 to_slv = f"""\ 

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

542""" 

543 from_slv = f"""\ 

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

545""" 

546 else: 

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

548 

549 vhdl += f"""\ 

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

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

552{to_slv}\ 

553 begin 

554 return result; 

555 end function; 

556 

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

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

559{from_slv}\ 

560 begin 

561 return result; 

562 end function; 

563 

564""" 

565 

566 return vhdl