Coverage for hdl_registers/generator/systemverilog/axi_lite/register_file.py: 92%

120 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 __future__ import annotations 

11 

12from typing import TYPE_CHECKING, Any 

13 

14from peakrdl_regblock import RegblockExporter 

15from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened 

16from peakrdl_regblock.udps import ALL_UDPS 

17from systemrdl import RDLCompiler 

18from systemrdl.importer import RDLImporter 

19from systemrdl.rdltypes import AccessType 

20from systemrdl.rdltypes.user_enum import UserEnum, UserEnumMemberContainer 

21from tsfpga.system_utils import prepend_file 

22 

23from hdl_registers.field.bit import Bit 

24from hdl_registers.field.bit_vector import BitVector 

25from hdl_registers.field.enumeration import Enumeration 

26from hdl_registers.field.integer import Integer 

27from hdl_registers.generator.register_code_generator import RegisterCodeGenerator 

28from hdl_registers.generator.register_code_generator_helpers import ( 

29 iterate_registers, 

30 qualified_field_name, 

31) 

32from hdl_registers.register_modes import REGISTER_MODES 

33 

34if TYPE_CHECKING: 

35 from collections.abc import Iterable 

36 from pathlib import Path 

37 

38 from systemrdl.component import Addrmap, Field 

39 from systemrdl.node import RootNode 

40 

41 from hdl_registers.field.register_field import RegisterField 

42 from hdl_registers.register import Register 

43 from hdl_registers.register_list import RegisterList 

44 

45 

46class SystemVerilogAxiLiteGenerator(RegisterCodeGenerator): 

47 """ 

48 Generate a SystemVerilog register file with AXI-Lite interface. 

49 See the :ref:`generator_systemverilog` article for usage details. 

50 

51 This generator will create 

52 

53 1. the register file module, and 

54 2. a package file. 

55 

56 The :meth:`.RegisterCodeGenerator.create` and :meth:`.RegisterCodeGenerator.create_if_needed` 

57 methods in this generator can be supplied with a ``flatten_axi_lite`` argument. 

58 See :ref:`here <systemverilog_flatten_axi_lite>` for details. 

59 """ 

60 

61 __version__ = "0.0.1" 

62 

63 SHORT_DESCRIPTION = "SystemVerilog AXI-Lite register file" 

64 

65 COMMENT_START = "//" 

66 

67 @property 

68 def output_file(self) -> Path: 

69 """ 

70 Result will be placed in this file. 

71 This specific generator will also create a package file with the same name, but with a 

72 ``_pkg`` suffix. 

73 """ 

74 return self.output_folder / f"{self.name}_register_file_axi_lite.sv" 

75 

76 @property 

77 def output_files(self) -> Iterable[Path]: 

78 """ 

79 All the files that this generator creates. 

80 """ 

81 output_file = self.output_file 

82 yield output_file 

83 yield output_file.parent / f"{output_file.stem}_pkg.sv" 

84 

85 @property 

86 def should_create(self) -> bool: 

87 """ 

88 Indicates if a (re-)create of artifacts is needed. 

89 Override the default behavior with a check for multiple files, instead of just one. 

90 """ 

91 return any(self._should_create(file_path=file_path) for file_path in self.output_files) 

92 

93 def get_code( 

94 self, 

95 **kwargs: Any, # noqa: ANN401 

96 ) -> str: 

97 """ 

98 Is required in the abstract base class, but not used in this generator. 

99 Do not call this method. 

100 """ 

101 raise NotImplementedError("Do not call this method directly") 

102 

103 def _create_artifact( 

104 self, 

105 output_file: Path, 

106 flatten_axi_lite: bool = False, 

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

108 ) -> Path: 

109 """ 

110 Override how the artifact file is created, since when using PeakRDL one call will generate 

111 two files. 

112 So we can not use :meth:`.get_code` and the default behavior of :meth:`.create_file`. 

113 """ 

114 # Import to SystemRDL representation. 

115 root_node = to_systemrdl(register_list=self.register_list) 

116 

117 cpu_interface = AXI4Lite_Cpuif_flattened if flatten_axi_lite else AXI4Lite_Cpuif 

118 

119 # Export to SystemVerilog. 

120 exporter = RegblockExporter() 

121 exporter.export( 

122 node=root_node, 

123 output_dir=str(self.output_folder), 

124 cpuif_cls=cpu_interface, 

125 module_name=self.output_file.stem, 

126 ) 

127 

128 # Prepend the hdl-registers header so we can detect if the file needs a re-create in 

129 # the future. 

130 header = self.header + "\n" 

131 for created_file in self.output_files: 

132 prepend_file(file_path=created_file, text=header) 

133 

134 # Return just one of the two files, which isn't great, but we need to follow the API of the 

135 # base class. 

136 return output_file 

137 

138 

139def to_systemrdl(register_list: RegisterList) -> RootNode: 

140 """ 

141 Translate the register data from hdl-registers representation to SystemRDL representation. 

142 

143 .. warning:: 

144 This is an internal function. 

145 Do not use it directly, API is subject to change. 

146 """ 

147 compiler = RDLCompiler() 

148 for udp in ALL_UDPS: 

149 compiler.register_udp(definition_cls=udp) 

150 

151 HdlRegistersImporter(compiler=compiler).import_register_list(register_list=register_list) 

152 

153 return compiler.elaborate() 

154 

155 

156class HdlRegistersImporter(RDLImporter): 

157 """ 

158 Importer class that translates the register data from hdl-registers representation 

159 to SystemRDL representation. 

160 

161 .. warning:: 

162 This is an internal class. 

163 Do not use it directly, API is subject to change. 

164 """ 

165 

166 # The register list to import. 

167 _register_list: RegisterList 

168 

169 # A note about the source definition file, to be included in error messages. 

170 _source_note: str 

171 

172 # The name of the register list, aka the name of the module. 

173 _name: str 

174 

175 def import_register_list(self, register_list: RegisterList) -> None: 

176 """ 

177 Call this method to perform the import. 

178 """ 

179 self._register_list = register_list 

180 self._source_note = ( 

181 "" 

182 if self._register_list.source_definition_file is None 

183 else f" in {self._register_list.source_definition_file}" 

184 ) 

185 self._name = self._register_list.name 

186 

187 source_definition_file = ( 

188 "" 

189 if register_list.source_definition_file is None 

190 else str(register_list.source_definition_file) 

191 ) 

192 # To set the 'default_src_ref'. 

193 # Does not seem to ever appear in the generated code, 

194 # but its part of the API so let's do it. 

195 self.import_file(path=source_definition_file) 

196 

197 top_component = self.create_addrmap_definition(type_name=register_list.name) 

198 

199 self._import(top_component=top_component) 

200 

201 self.register_root_component(definition=top_component) 

202 

203 def _import(self, top_component: Addrmap) -> None: 

204 if self._register_list.constants: 

205 raise ValueError( 

206 f'Error while translating "{self._name}"{self._source_note}: ' 

207 "SystemVerilog generator does not support constants." 

208 ) 

209 

210 if not self._register_list.register_objects: 

211 raise ValueError( 

212 f'Error while translating "{self._name}"{self._source_note}: ' 

213 "SystemVerilog generator requires at least one register." 

214 ) 

215 

216 for register, register_array in iterate_registers(register_list=self._register_list): 

217 if register_array is not None: 

218 raise ValueError( 

219 f'Error while translating "{self._name}.{register_array.name}"' 

220 f"{self._source_note}: " 

221 "SystemVerilog generator does not support register arrays." 

222 ) 

223 

224 self._add_register(top_component=top_component, register=register) 

225 

226 def _add_register(self, top_component: Addrmap, register: Register) -> None: 

227 if not register.fields: 

228 raise ValueError( 

229 f'Error while translating "{self._name}.{register.name}"{self._source_note}: ' 

230 "SystemVerilog generator requires at least one field per register." 

231 ) 

232 

233 register_instance = self.instantiate_reg( 

234 comp_def=self.create_reg_definition(type_name=register.name), 

235 inst_name=register.name, 

236 addr_offset=register.address, 

237 ) 

238 

239 hw_access_type, sw_access_type = self._decode_register_mode(register=register) 

240 for field in register.fields: 

241 self._add_field( 

242 register_component=register_instance, 

243 hw_access_type=hw_access_type, 

244 sw_access_type=sw_access_type, 

245 register=register, 

246 field=field, 

247 ) 

248 

249 self.add_child(parent=top_component, child=register_instance) 

250 

251 def _add_field( 

252 self, 

253 register_component: Addrmap, 

254 hw_access_type: AccessType, 

255 sw_access_type: AccessType, 

256 register: Register, 

257 field: RegisterList.Register.Field, 

258 ) -> None: 

259 field_instance = self.instantiate_field( 

260 comp_def=self.create_field_definition(type_name=field.name), 

261 inst_name=field.name, 

262 bit_offset=field.base_index, 

263 bit_width=field.width, 

264 ) 

265 

266 self.assign_property(component=field_instance, prop_name="hw", value=hw_access_type) 

267 self.assign_property(component=field_instance, prop_name="sw", value=sw_access_type) 

268 

269 self._assign_field_properties(register=register, field=field, field_instance=field_instance) 

270 

271 self.add_child(parent=register_component, child=field_instance) 

272 

273 def _assign_field_properties( 

274 self, register: Register, field: RegisterField, field_instance: Field 

275 ) -> None: 

276 if isinstance(field, Bit): 

277 self.assign_property( 

278 component=field_instance, prop_name="reset", value=int(field.default_value) 

279 ) 

280 return 

281 

282 if isinstance(field, BitVector): 

283 self.assign_property( 

284 component=field_instance, prop_name="reset", value=int(field.default_value, base=2) 

285 ) 

286 return 

287 

288 if isinstance(field, Enumeration): 

289 members = [ 

290 UserEnumMemberContainer(name=element.name, value=element.value) 

291 for element in field.elements 

292 ] 

293 enum_type = UserEnum.define_new( 

294 qualified_field_name( 

295 register_list=self._register_list, register=register, field=field 

296 ), 

297 members, 

298 ) 

299 self.assign_property(component=field_instance, prop_name="encode", value=enum_type) 

300 self.assign_property( 

301 component=field_instance, prop_name="reset", value=field.default_value.value 

302 ) 

303 return 

304 

305 if isinstance(field, Integer): 

306 if field.is_signed: 

307 # Mainly because it's a hassle to convert a negative default value number to 

308 # positive representation here. 

309 raise ValueError( 

310 f'Error while translating "{self._name}.{register.name}.{field.name}"' 

311 f"{self._source_note}: " 

312 "SystemVerilog generator does not support signed integer fields." 

313 ) 

314 

315 self.assign_property( 

316 component=field_instance, prop_name="reset", value=field.default_value 

317 ) 

318 return 

319 

320 raise ValueError(f"Unknown field: {field}") 

321 

322 def _decode_register_mode(self, register: Register) -> tuple[AccessType, AccessType]: 

323 """ 

324 Return tuple: (hardware access, software access). 

325 """ 

326 if register.mode == REGISTER_MODES["r"]: 

327 # HW provides a value that SW can read. 

328 return AccessType.w, AccessType.r 

329 

330 if register.mode == REGISTER_MODES["w"]: 

331 # SW writes a value that HW can use. 

332 return AccessType.r, AccessType.w 

333 

334 if register.mode == REGISTER_MODES["r_w"]: 

335 # SW writes a value that HW can use. 

336 # SW can also read back the value. 

337 return AccessType.r, AccessType.rw 

338 

339 raise ValueError( 

340 f'Error while translating "{self._name}.{register.name}"{self._source_note}: ' 

341 f"SystemVerilog generator does not support register mode: {register.mode}" 

342 )