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

191 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 11:11 +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 

55 __version__ = "2.0.0" 

56 

57 SHORT_DESCRIPTION = "VHDL register package" 

58 

59 @property 

60 def output_file(self) -> Path: 

61 """ 

62 Result will be placed in this file. 

63 """ 

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

65 

66 def get_code( 

67 self, 

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

69 ) -> 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 # Match e.g. 5e60, but not 5.0e60. 

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

124 

125 for constant in self.iterate_constants(): 

126 if isinstance(constant, BooleanConstant): 

127 type_declaration = "boolean" 

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

129 elif isinstance(constant, IntegerConstant): 

130 type_declaration = "integer" 

131 value = str(constant.value) 

132 elif isinstance(constant, FloatConstant): 

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

134 type_declaration = "real" 

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

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

137 value = str(constant.value) 

138 

139 match = re_float_start_with_integer.match(value) 

140 if match: 

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

142 base = match.group(1) 

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

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

145 elif isinstance(constant, StringConstant): 

146 type_declaration = "string" 

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

148 elif isinstance(constant, UnsignedVectorConstant): 

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

150 

151 if constant.is_hexadecimal_not_binary: 

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

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

154 else: 

155 # But not when defining a binary SLV. 

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

157 else: 

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

159 

160 vhdl += ( 

161 " constant " 

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

163 ) 

164 

165 vhdl += "\n" 

166 

167 return vhdl 

168 

169 @property 

170 def _register_range_type_name(self) -> str: 

171 """ 

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

173 """ 

174 return f"{self.name}_register_range" 

175 

176 def _register_range(self) -> str: 

177 """ 

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

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

180 the indexing is safe. 

181 """ 

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

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

184 address_width = index_width + 2 

185 

186 return f"""\ 

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

188 -- The valid range of register indexes. 

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

190 

191 -- --------------------------------------------------------------------------- 

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

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

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

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

196 

197""" 

198 

199 def _array_constants(self) -> str: 

200 """ 

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

202 """ 

203 vhdl = "" 

204 for register_array in self.iterate_register_arrays(): 

205 array_name = self.qualified_register_array_name(register_array=register_array) 

206 

207 vhdl += f"""\ 

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

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

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

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

212 

213""" 

214 

215 return vhdl 

216 

217 def _array_register_index_function_signature( 

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

219 ) -> str: 

220 """ 

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

222 register array. 

223 """ 

224 array_name = self.qualified_register_array_name(register_array=register_array) 

225 return f"""\ 

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

227 array_index : {array_name}_range 

228 ) return {self._register_range_type_name}""" 

229 

230 def _register_indexes(self) -> str: 

231 """ 

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

233 """ 

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

235 

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

237 if register_array is None: 

238 vhdl += ( 

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

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

241 ) 

242 else: 

243 vhdl += ( 

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

245 ) 

246 

247 vhdl += "\n" 

248 

249 return vhdl 

250 

251 def _register_map_head(self) -> str: 

252 """ 

253 Get constants mapping the register indexes to register modes. 

254 """ 

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

256 

257 return f"""\ 

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

259 -- the package body (deferred constants). 

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

261 -- Needed for ModelSim compilation to pass. 

262 

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

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

265 

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

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

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

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

270 

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

272 subtype {self.name}_reg_was_accessed_t is \ 

273std_ulogic_vector({self._register_range_type_name}); 

274 

275""" 

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 range {field.min_value} to {field.max_value};" 

379 ) 

380 

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

382 

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

384 """ 

385 Get an init value constant for the field. 

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

387 

388 Arguments: 

389 field: A field. 

390 field_name: The field's qualified name. 

391 """ 

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

393 

394 if isinstance(field, Bit): 

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

396 

397 if isinstance(field, BitVector): 

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

399 

400 if isinstance(field, Enumeration): 

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

402 

403 if isinstance(field, Integer): 

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

405 

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

407 

408 def _field_conversion_function_declarations( 

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

410 ) -> str: 

411 """ 

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

413 to/from SLV. 

414 

415 Arguments: 

416 field: A field. 

417 field_name: The field's qualified name. 

418 """ 

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

420 return "" 

421 

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

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

424 

425 return f"""\ 

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

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

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

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

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

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

432""" 

433 

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

435 

436 def _array_index_function_implementations(self) -> str: 

437 """ 

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

439 register array. 

440 """ 

441 vhdl = "" 

442 for register_array in self.iterate_register_arrays(): 

443 num_registers = len(register_array.registers) 

444 for register in register_array.registers: 

445 vhdl += f"""\ 

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

447 begin 

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

449 end function; 

450 

451""" 

452 

453 return vhdl 

454 

455 def _register_map_body(self) -> str: 

456 """ 

457 Get the body of the register map definition constants. 

458 """ 

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

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

461 

462 register_definitions = [] 

463 default_values = [] 

464 index = 0 

465 

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

467 register_definitions.append( 

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

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

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

471 ) 

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

473 

474 for register_object in self.iterate_register_objects(): 

475 if isinstance(register_object, Register): 

476 add( 

477 register=register_object, 

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

479 ) 

480 index += 1 

481 else: 

482 for array_index in range(register_object.length): 

483 for register in register_object.registers: 

484 register_name = self.qualified_register_name( 

485 register=register, register_array=register_object 

486 ) 

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

488 

489 add(register=register, index_name=index_name) 

490 index += 1 

491 

492 array_element_separator = ",\n " 

493 return f"""\ 

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

495 {array_element_separator.join(register_definitions)} 

496 ); 

497 

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

499 {array_element_separator.join(default_values)} 

500 ); 

501 

502""" 

503 

504 def _field_conversion_implementations(self) -> str: 

505 """ 

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

507 to/from SLV. 

508 """ 

509 vhdl = "" 

510 

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

512 for field in register.fields: 

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

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

515 # be implemented. 

516 continue 

517 

518 name = self.qualified_field_name( 

519 register=register, register_array=register_array, field=field 

520 ) 

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

522 

523 if isinstance(field, Enumeration): 

524 to_slv = f"""\ 

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

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

527 to_unsigned(data_int, {name}_width) 

528 ); 

529""" 

530 from_slv = f"""\ 

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

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

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

534""" 

535 elif isinstance(field, Integer): 

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

537 to_slv = f"""\ 

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

539""" 

540 from_slv = f"""\ 

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

542""" 

543 else: 

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

545 

546 vhdl += f"""\ 

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

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

549{to_slv}\ 

550 begin 

551 return result; 

552 end function; 

553 

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

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

556{from_slv}\ 

557 begin 

558 return result; 

559 end function; 

560 

561""" 

562 

563 return vhdl