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

188 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 

13 

14# First party libraries 

15from hdl_registers.constant.bit_vector_constant import UnsignedVectorConstant 

16from hdl_registers.constant.boolean_constant import BooleanConstant 

17from hdl_registers.constant.float_constant import FloatConstant 

18from hdl_registers.constant.integer_constant import IntegerConstant 

19from hdl_registers.constant.string_constant import StringConstant 

20from hdl_registers.field.bit import Bit 

21from hdl_registers.field.bit_vector import BitVector 

22from hdl_registers.field.enumeration import Enumeration 

23from hdl_registers.field.integer import Integer 

24from hdl_registers.field.numerical_interpretation import ( 

25 Signed, 

26 SignedFixedPoint, 

27 Unsigned, 

28 UnsignedFixedPoint, 

29) 

30from hdl_registers.register import Register 

31 

32if TYPE_CHECKING: 

33 from hdl_registers.field.register_field import RegisterField 

34 from hdl_registers.register_array import RegisterArray 

35 

36# Local folder libraries 

37from .vhdl_generator_common import VhdlGeneratorCommon 

38 

39 

40class VhdlRegisterPackageGenerator(VhdlGeneratorCommon): 

41 """ 

42 Generate a base VHDL package with basic register information. 

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

44 

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

46 * For each register, the index within the register map. 

47 * For each field in each register 

48 

49 * Register bit index range definitions. 

50 * Native VHDL type corresponding to the field type. 

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

52 

53 Also produces a register map constant, mapping indexes to modes, suitable for use with 

54 :ref:`reg_file.axi_lite_reg_file` or :class:`.VhdlAxiLiteWrapperGenerator`. 

55 """ 

56 

57 __version__ = "1.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(self, **kwargs: Any) -> str: 

69 """ 

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

71 """ 

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

73 

74 vhdl = f"""\ 

75{self.header} 

76library ieee; 

77use ieee.std_logic_1164.all; 

78use ieee.numeric_std.all; 

79use ieee.fixed_pkg.all; 

80 

81library reg_file; 

82use reg_file.reg_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 

123 for constant in self.iterate_constants(): 

124 if isinstance(constant, BooleanConstant): 

125 type_declaration = "boolean" 

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

127 elif isinstance(constant, IntegerConstant): 

128 type_declaration = "integer" 

129 value = str(constant.value) 

130 elif isinstance(constant, FloatConstant): 

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

132 type_declaration = "real" 

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

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

135 value = str(constant.value) 

136 elif isinstance(constant, StringConstant): 

137 type_declaration = "string" 

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

139 elif isinstance(constant, UnsignedVectorConstant): 

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

141 

142 if constant.is_hexadecimal_not_binary: 

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

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

145 else: 

146 # But not when defining a binary SLV. 

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

148 else: 

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

150 

151 vhdl += ( 

152 " constant " 

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

154 ) 

155 

156 vhdl += "\n" 

157 

158 return vhdl 

159 

160 @property 

161 def _register_range_type_name(self) -> str: 

162 """ 

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

164 """ 

165 return f"{self.name}_reg_range" 

166 

167 def _register_range(self) -> str: 

168 """ 

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

170 """ 

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

172 vhdl = f"""\ 

173 -- --------------------------------------------------------------------------- 

174 -- The valid range of register indexes. 

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

176 

177""" 

178 return vhdl 

179 

180 def _array_constants(self) -> str: 

181 """ 

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

183 """ 

184 vhdl = "" 

185 for register_array in self.iterate_register_arrays(): 

186 array_name = self.qualified_register_array_name(register_array=register_array) 

187 

188 vhdl += f"""\ 

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

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

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

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

193 

194""" 

195 

196 return vhdl 

197 

198 def _array_register_index_function_signature( 

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

200 ) -> str: 

201 """ 

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

203 register array. 

204 """ 

205 array_name = self.qualified_register_array_name(register_array=register_array) 

206 vhdl = f"""\ 

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

208 array_index : {array_name}_range 

209 ) return {self._register_range_type_name}""" 

210 return vhdl 

211 

212 def _register_indexes(self) -> str: 

213 """ 

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

215 """ 

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

217 

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

219 if register_array is None: 

220 vhdl += ( 

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

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

223 ) 

224 else: 

225 vhdl += ( 

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

227 ) 

228 

229 vhdl += "\n" 

230 

231 return vhdl 

232 

233 def _register_map_head(self) -> str: 

234 """ 

235 Get constants mapping the register indexes to register modes. 

236 """ 

237 map_name = f"{self.name}_reg_map" 

238 

239 vhdl = f"""\ 

240 -- Declare 'reg_map' and 'regs_init' constants here but define them in body (deferred constants). 

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

242 -- Needed for ModelSim compilation to pass. 

243 

244 -- To be used as the 'regs' generic of 'axi_lite_reg_file.vhd'. 

245 constant {map_name} : reg_definition_vec_t({self._register_range_type_name}); 

246 

247 -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_reg_file.vhd'. 

248 subtype {self.name}_regs_t is reg_vec_t({self._register_range_type_name}); 

249 -- To be used as the 'default_values' generic of 'axi_lite_reg_file.vhd'. 

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

251 

252 -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_reg_file.vhd'. 

253 subtype {self.name}_reg_was_accessed_t is \ 

254std_ulogic_vector({self._register_range_type_name}); 

255 

256""" 

257 

258 return vhdl 

259 

260 def _field_declarations(self) -> str: 

261 """ 

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

263 

264 * Bit index range 

265 * VHDL type 

266 * width constant 

267 * conversion function declarations to/from type and SLV 

268 """ 

269 vhdl = "" 

270 

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

272 if not register.fields: 

273 continue 

274 

275 register_description = self.register_description( 

276 register=register, register_array=register_array 

277 ) 

278 

279 vhdl += f"""\ 

280 -- ----------------------------------------------------------------------------- 

281 -- Fields in the {register_description}. 

282""" 

283 

284 for field in register.fields: 

285 field_name = self.qualified_field_name( 

286 register=register, register_array=register_array, field=field 

287 ) 

288 field_is_bit = isinstance(field, Bit) 

289 

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

291 if field_is_bit: 

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

293 # gives a std_logic value. 

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

295 else: 

296 # For other fields its an actual range. 

297 vhdl += f"""\ 

298 subtype {field_name} is natural \ 

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

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

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

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

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

304""" 

305 

306 vhdl += f"""\ 

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

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

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

310""" 

311 

312 return vhdl 

313 

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

315 """ 

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

317 

318 Arguments: 

319 field: A field. 

320 field_name: The field's qualified name. 

321 """ 

322 if isinstance(field, BitVector): 

323 if isinstance(field.numerical_interpretation, Unsigned): 

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

325 

326 if isinstance(field.numerical_interpretation, Signed): 

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

328 

329 if isinstance(field.numerical_interpretation, UnsignedFixedPoint): 

330 return ( 

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

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

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

334 ) 

335 

336 if isinstance(field.numerical_interpretation, SignedFixedPoint): 

337 return ( 

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

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

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

341 ) 

342 

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

344 

345 if isinstance(field, Enumeration): 

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

347 # risk of name clashes. 

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

349 # easy to work with. 

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

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

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

353 return f"""\ 

354 type {field_name}_t is ( 

355 {elements} 

356 );\ 

357""" 

358 

359 if isinstance(field, Integer): 

360 return ( 

361 f" subtype {field_name}_t is integer " 

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

363 ) 

364 

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

366 

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

368 """ 

369 Get an init value constant for the field. 

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

371 

372 Arguments: 

373 field: A field. 

374 field_name: The field's qualified name. 

375 """ 

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

377 

378 if isinstance(field, Bit): 

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

380 

381 if isinstance(field, BitVector): 

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

383 

384 if isinstance(field, Enumeration): 

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

386 

387 if isinstance(field, Integer): 

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

389 

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

391 

392 def _field_conversion_function_declarations( 

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

394 ) -> str: 

395 """ 

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

397 to/from SLV. 

398 

399 Arguments: 

400 field: A field. 

401 field_name: The field's qualified name. 

402 """ 

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

404 return "" 

405 

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

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

408 

409 return f"""\ 

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

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

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

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

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

415 function to_{field_name}(data : reg_t) return {field_name}_t; 

416""" 

417 

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

419 

420 def _array_index_function_implementations(self) -> str: 

421 """ 

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

423 register array. 

424 """ 

425 vhdl = "" 

426 for register_array in self.iterate_register_arrays(): 

427 num_registers = len(register_array.registers) 

428 for register in register_array.registers: 

429 vhdl += f"""\ 

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

431 begin 

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

433 end function; 

434 

435""" 

436 

437 return vhdl 

438 

439 def _register_map_body(self) -> str: 

440 """ 

441 Get the body of the register map definition constants. 

442 """ 

443 map_name = f"{self.name}_reg_map" 

444 range_name = f"{self.name}_reg_range" 

445 

446 register_definitions = [] 

447 default_values = [] 

448 vhdl_array_index = 0 

449 for register_object in self.iterate_register_objects(): 

450 if isinstance(register_object, Register): 

451 idx = self.qualified_register_name(register_object) 

452 opening = f"{vhdl_array_index} => " 

453 

454 register_definitions.append( 

455 f"{opening}(idx => {idx}, reg_type => {register_object.mode.shorthand})" 

456 ) 

457 default_values.append(f'{opening}"{register_object.default_value:032b}"') 

458 

459 vhdl_array_index = vhdl_array_index + 1 

460 

461 else: 

462 for array_index in range(register_object.length): 

463 for register in register_object.registers: 

464 regiser_name = self.qualified_register_name(register, register_object) 

465 idx = f"{regiser_name}({array_index})" 

466 opening = f"{vhdl_array_index} => " 

467 

468 register_definitions.append( 

469 f"{opening}(idx => {idx}, reg_type => {register.mode.shorthand})" 

470 ) 

471 default_values.append(f'{opening}"{register.default_value:032b}"') 

472 

473 vhdl_array_index = vhdl_array_index + 1 

474 

475 array_element_separator = ",\n " 

476 vhdl = f"""\ 

477 constant {map_name} : reg_definition_vec_t({range_name}) := ( 

478 {array_element_separator.join(register_definitions)} 

479 ); 

480 

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

482 {array_element_separator.join(default_values)} 

483 ); 

484 

485""" 

486 

487 return vhdl 

488 

489 def _field_conversion_implementations(self) -> str: 

490 """ 

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

492 to/from SLV. 

493 """ 

494 vhdl = "" 

495 

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

497 for field in register.fields: 

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

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

500 # be implemented. 

501 continue 

502 

503 name = self.qualified_field_name( 

504 register=register, register_array=register_array, field=field 

505 ) 

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

507 

508 if isinstance(field, Enumeration): 

509 to_slv = f"""\ 

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

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

512 to_unsigned(data_int, {name}_width) 

513 ); 

514""" 

515 from_slv = f"""\ 

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

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

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

519""" 

520 elif isinstance(field, Integer): 

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

522 to_slv = f"""\ 

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

524""" 

525 from_slv = f"""\ 

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

527""" 

528 else: 

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

530 

531 vhdl += f"""\ 

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

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

534{to_slv}\ 

535 begin 

536 return result; 

537 end function; 

538 

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

540 function to_{name}(data : reg_t) return {name}_t is 

541{from_slv}\ 

542 begin 

543 return result; 

544 end function; 

545 

546""" 

547 

548 return vhdl