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