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
« 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# --------------------------------------------------------------------------------------------------
10from __future__ import annotations
12from typing import TYPE_CHECKING, Any
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
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
34if TYPE_CHECKING:
35 from collections.abc import Iterable
36 from pathlib import Path
38 from systemrdl.component import Addrmap, Field
39 from systemrdl.node import RootNode
41 from hdl_registers.field.register_field import RegisterField
42 from hdl_registers.register import Register
43 from hdl_registers.register_list import RegisterList
46class SystemVerilogAxiLiteGenerator(RegisterCodeGenerator):
47 """
48 Generate a SystemVerilog register file with AXI-Lite interface.
49 See the :ref:`generator_systemverilog` article for usage details.
51 This generator will create
53 1. the register file module, and
54 2. a package file.
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 """
61 __version__ = "0.0.1"
63 SHORT_DESCRIPTION = "SystemVerilog AXI-Lite register file"
65 COMMENT_START = "//"
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"
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"
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)
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")
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)
117 cpu_interface = AXI4Lite_Cpuif_flattened if flatten_axi_lite else AXI4Lite_Cpuif
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 )
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)
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
139def to_systemrdl(register_list: RegisterList) -> RootNode:
140 """
141 Translate the register data from hdl-registers representation to SystemRDL representation.
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)
151 HdlRegistersImporter(compiler=compiler).import_register_list(register_list=register_list)
153 return compiler.elaborate()
156class HdlRegistersImporter(RDLImporter):
157 """
158 Importer class that translates the register data from hdl-registers representation
159 to SystemRDL representation.
161 .. warning::
162 This is an internal class.
163 Do not use it directly, API is subject to change.
164 """
166 # The register list to import.
167 _register_list: RegisterList
169 # A note about the source definition file, to be included in error messages.
170 _source_note: str
172 # The name of the register list, aka the name of the module.
173 _name: str
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
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)
197 top_component = self.create_addrmap_definition(type_name=register_list.name)
199 self._import(top_component=top_component)
201 self.register_root_component(definition=top_component)
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 )
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 )
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 )
224 self._add_register(top_component=top_component, register=register)
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 )
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 )
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 )
249 self.add_child(parent=top_component, child=register_instance)
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 )
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)
269 self._assign_field_properties(register=register, field=field, field_instance=field_instance)
271 self.add_child(parent=register_component, child=field_instance)
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
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
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
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 )
315 self.assign_property(
316 component=field_instance, prop_name="reset", value=field.default_value
317 )
318 return
320 raise ValueError(f"Unknown field: {field}")
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
330 if register.mode == REGISTER_MODES["w"]:
331 # SW writes a value that HW can use.
332 return AccessType.r, AccessType.w
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
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 )