Coverage for hdl_registers/generator/vhdl/vhdl_generator_common.py: 89%
94 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-15 20:50 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-15 20:50 +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 hdl_registers.field.bit import Bit
15from hdl_registers.field.bit_vector import BitVector
16from hdl_registers.field.enumeration import Enumeration
17from hdl_registers.field.integer import Integer
18from hdl_registers.field.numerical_interpretation import Fixed, Signed, Unsigned
19from hdl_registers.generator.register_code_generator import RegisterCodeGenerator
21if TYPE_CHECKING:
22 from collections.abc import Iterator
23 from pathlib import Path
25 from hdl_registers.field.register_field import RegisterField
26 from hdl_registers.register import Register
27 from hdl_registers.register_array import RegisterArray
28 from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection
31class VhdlGeneratorCommon(RegisterCodeGenerator):
32 """
33 Common methods for generation of VHDL code.
34 """
36 COMMENT_START = "--"
38 @staticmethod
39 def field_to_slv_function_name(field: RegisterField, field_name: str) -> str:
40 """
41 Name of the function that converts the field's native VHDL representation to SLV.
43 Arguments:
44 field: A field.
45 field_name: The field's qualified name.
46 """
47 if isinstance(field, Integer):
48 # All integer field values will be sub-type of VHDL integer.
49 # If many of these functions have the same name "to_slv", that will be a name clash.
50 # Hence we need to qualify the function name.
51 return f"to_{field_name}_slv"
53 if isinstance(field, Enumeration):
54 # For the enumeration field on the other hand, the type is unambiguous.
55 return "to_slv"
57 raise TypeError(f"Field {field} does not have a conversion function.")
59 def field_to_slv(self, field: RegisterField, field_name: str, value: str) -> str:
60 """
61 Get a VHDL snippet that converts a value of the given field to SLV.
62 Via e.g. a function call or a cast.
64 Arguments:
65 field: The field.
66 field_name: The field's qualified name.
67 value: The name of the variable/constant that holds the field's natively typed value.
68 """
69 if isinstance(field, Bit):
70 return value
72 if isinstance(field, BitVector):
73 if isinstance(field.numerical_interpretation, (Signed, Unsigned)):
74 # Plain unsigned/signed vector is a subtype of std_logic_vector.
75 # Hence we can just cast it.
76 return f"std_ulogic_vector({value})"
78 if isinstance(field.numerical_interpretation, Fixed):
79 # Casting function built into ieee.fixed_pkg.
80 return f"to_slv({value})"
82 raise ValueError(f"Unknown bit vector field: {field}")
84 if isinstance(field, (Enumeration, Integer)):
85 # Our own conversion functions.
86 to_slv = self.field_to_slv_function_name(field=field, field_name=field_name)
87 return f"{to_slv}({value})"
89 raise ValueError(f"Unknown field: {field}")
91 def field_type_name(
92 self,
93 register: Register,
94 field: RegisterField,
95 register_array: RegisterArray | None = None,
96 ) -> str:
97 """
98 Get the native VHDL type name that will represent the value of the supplied field.
99 """
100 if isinstance(field, Bit):
101 return "std_ulogic"
103 if isinstance(field, (BitVector, Enumeration, Integer)):
104 field_name = self.qualified_field_name(
105 register=register, register_array=register_array, field=field
106 )
107 return f"{field_name}_t"
109 raise ValueError(f"Unknown field: {field}")
111 def has_any_software_accessible_register(self, direction: SoftwareAccessDirection) -> bool:
112 """
113 Return True if the register list contains any register, plain or in array, that is
114 software-accessible in the given direction.
115 """
116 for register, _ in self.iterate_registers():
117 if register.mode.is_software_accessible(direction=direction):
118 return True
120 return False
122 def iterate_software_accessible_registers(
123 self, direction: SoftwareAccessDirection
124 ) -> Iterator[tuple[Register, RegisterArray | None]]:
125 """
126 Iterate all registers in the register list, plain or in array, that are software-accessible
127 in the given direction.
128 """
129 for register, register_array in self.iterate_registers():
130 if register.mode.is_software_accessible(direction=direction):
131 yield register, register_array
133 def iterate_software_accessible_plain_registers(
134 self, direction: SoftwareAccessDirection
135 ) -> Iterator[Register]:
136 """
137 Iterate all plain registers in the register list that are software-accessible in the
138 given direction.
139 """
140 for register in self.iterate_plain_registers():
141 if register.mode.is_software_accessible(direction=direction):
142 yield register
144 def iterate_software_accessible_array_registers(
145 self, register_array: RegisterArray, direction: SoftwareAccessDirection
146 ) -> Iterator[Register]:
147 """
148 Iterate all registers in the register array that are software-accessible in the
149 given direction.
150 """
151 for register in register_array.registers:
152 if register.mode.is_software_accessible(direction=direction):
153 yield register
155 def iterate_software_accessible_register_arrays(
156 self, direction: SoftwareAccessDirection
157 ) -> Iterator[RegisterArray]:
158 """
159 Iterate all register arrays in the register list that contain at least one register that
160 is software-accessible in the given direction.
161 """
162 for register_array in self.iterate_register_arrays():
163 accessible_registers = list(
164 self.iterate_software_accessible_array_registers(
165 register_array=register_array, direction=direction
166 )
167 )
168 if accessible_registers:
169 yield register_array
171 def has_any_hardware_accessible_register(self, direction: HardwareAccessDirection) -> bool:
172 """
173 Return True if the register list contains at least one register, plain or in array, with a
174 mode where hardware accesses the value in the given direction.
175 """
176 for register, _ in self.iterate_registers():
177 if register.mode.is_hardware_accessible(direction=direction):
178 return True
180 return False
182 def iterate_hardware_accessible_registers(
183 self, direction: HardwareAccessDirection
184 ) -> Iterator[tuple[Register, RegisterArray | None]]:
185 """
186 Iterate all registers in the register list, plain or in array, that are hardware-accessible
187 in the given direction.
188 """
189 for register, register_array in self.iterate_registers():
190 if register.mode.is_hardware_accessible(direction=direction):
191 yield register, register_array
193 def iterate_hardware_accessible_plain_registers(
194 self, direction: HardwareAccessDirection
195 ) -> Iterator[Register]:
196 """
197 Iterate all plain registers in the register list that are hardware-accessible in the
198 given direction.
199 """
200 for register in self.iterate_plain_registers():
201 if register.mode.is_hardware_accessible(direction=direction):
202 yield register
204 def iterate_hardware_accessible_array_registers(
205 self, register_array: RegisterArray, direction: HardwareAccessDirection
206 ) -> Iterator[Register]:
207 """
208 Iterate all registers in the register array that are hardware-accessible in the
209 given direction.
210 """
211 for register in register_array.registers:
212 if register.mode.is_hardware_accessible(direction=direction):
213 yield register
215 def iterate_hardware_accessible_register_arrays(
216 self, direction: HardwareAccessDirection
217 ) -> Iterator[RegisterArray]:
218 """
219 Iterate all register arrays in the register list that contain at least one register that
220 is hardware-accessible in the given direction.
221 """
222 for register_array in self.iterate_register_arrays():
223 accessible_registers = list(
224 self.iterate_hardware_accessible_array_registers(
225 register_array=register_array, direction=direction
226 )
227 )
228 if accessible_registers:
229 yield register_array
231 def _create_if_there_are_registers_otherwise_delete_file(
232 self,
233 **kwargs: Any, # noqa: ANN401
234 ) -> Path:
235 """
236 Create the code artifact only if the register list actually has any registers.
237 Convenient to call in generators where no registers would result in the generated file being
238 an empty shell.
240 If, for example, the user has a register list with only constants we do not want
241 to flood the file system with unnecessary files.
243 If the artifact file exists from a previous run, we delete it since we do not want stray
244 files laying around and we do not want to give the false impression that this file is being
245 actively generated.
246 """
247 if self.register_list.register_objects:
248 return super().create(**kwargs)
250 if self.output_file.exists():
251 # Will not work if it is a directory, but if it is then that is a major user error
252 # and we kinda want to back out.
253 self.output_file.unlink()
255 # Return the path to the output file, which at this point does not exist.
256 # But do it anyway just to be consistent with the other generators.
257 return self.output_file