Coverage for hdl_registers/generator/vhdl/simulation/wait_until_package.py: 94%
72 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 hdl_registers.register_mode import SoftwareAccessDirection
16from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon
18if TYPE_CHECKING:
19 from pathlib import Path
21 from hdl_registers.field.register_field import RegisterField
22 from hdl_registers.register import Register
23 from hdl_registers.register_array import RegisterArray
26class VhdlSimulationWaitUntilPackageGenerator(VhdlSimulationGeneratorCommon):
27 """
28 Generate VHDL code with ``wait_until_X_equals`` procedures that simplify simulation.
29 See the :ref:`generator_vhdl` article for usage details.
31 * For each readable register, a procedure that waits until the register assumes a
32 given natively-typed record value.
34 * For each field in each readable register, a procedure that waits until the field assumes a
35 given natively-typed value.
37 Uses VUnit Verification Component calls to create bus read operations.
39 The generated VHDL file needs also the generated packages from
40 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
41 See :ref:`vhdl_dependencies` for further dependencies.
42 """
44 __version__ = "1.0.2"
46 SHORT_DESCRIPTION = "VHDL simulation wait until package"
48 @property
49 def output_file(self) -> Path:
50 """
51 Result will be placed in this file.
52 """
53 return self.output_folder / f"{self.name}_register_wait_until_pkg.vhd"
55 def create(
56 self,
57 **kwargs: Any, # noqa: ANN401
58 ) -> Path:
59 """
60 See super class for API details.
62 Overloaded here because this package file shall only be created if the register list
63 actually has any registers.
64 """
65 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
67 def get_code(
68 self,
69 **kwargs: Any, # noqa: ANN401, ARG002
70 ) -> str:
71 """
72 Get a package with ``wait_until_X_equals`` methods for registers/fields.
73 """
74 package_name = self.output_file.stem
76 return f"""\
77library ieee;
78use ieee.fixed_pkg.all;
79use ieee.std_logic_1164.all;
80use ieee.numeric_std.all;
82library vunit_lib;
83use vunit_lib.bus_master_pkg.bus_master_t;
84use vunit_lib.bus_master_pkg.wait_until_read_equals;
85use vunit_lib.com_types_pkg.max_timeout;
86use vunit_lib.com_types_pkg.network_t;
87use vunit_lib.string_ops.hex_image;
89library register_file;
90use register_file.register_file_pkg.register_t;
91use register_file.register_operations_pkg.register_bus_master;
93use work.{self.name}_regs_pkg.all;
94use work.{self.name}_register_record_pkg.all;
97package {package_name} is
99{self._declarations()}\
100end package;
102package body {package_name} is
104{self._implementations()}\
105end package body;
106"""
108 def _declarations(self) -> str:
109 """
110 Get procedure declarations for all procedures.
111 """
112 separator = self.get_separator_line(indent=2)
113 vhdl = ""
115 for register, register_array in self.iterate_software_accessible_registers(
116 direction=SoftwareAccessDirection.READ
117 ):
118 declarations = []
120 signature = self._register_wait_until_equals_signature(
121 register=register, register_array=register_array
122 )
123 declarations.append(f"{signature};\n")
125 for field in register.fields:
126 signature = self._field_wait_until_equals_signature(
127 register=register, register_array=register_array, field=field
128 )
129 declarations.append(f"{signature};\n")
131 vhdl += separator
132 vhdl += "\n".join(declarations)
133 vhdl += separator
134 vhdl += "\n"
136 return vhdl
138 def _register_wait_until_equals_signature(
139 self, register: Register, register_array: RegisterArray | None
140 ) -> str:
141 """
142 Get signature for a 'wait_until_reg_equals' procedure.
143 """
144 register_name = self.qualified_register_name(
145 register=register, register_array=register_array
146 )
147 register_description = self.register_description(
148 register=register, register_array=register_array
149 )
151 if register.fields:
152 value_type = f"{register_name}_t"
153 slv_comment = ""
154 else:
155 value_type = "register_t"
156 slv_comment = (
157 " -- Note that '-' can be used as a wildcard in 'value' since 'check_match' is \n"
158 " -- used to check for equality.\n"
159 )
161 return f"""\
162 -- Wait until the {register_description} equals the given 'value'.
163{slv_comment}\
164 procedure wait_until_{register_name}_equals(
165 signal net : inout network_t;
166{self.get_array_index_port(register_array=register_array)}\
167 value : in {value_type};
168 base_address : in unsigned(32 - 1 downto 0) := (others => '0');
169 bus_handle : in bus_master_t := register_bus_master;
170 timeout : delay_length := max_timeout;
171 message : string := ""
172 )\
173"""
175 def _field_wait_until_equals_signature(
176 self,
177 register: Register,
178 register_array: RegisterArray | None,
179 field: RegisterField,
180 ) -> str:
181 """
182 Get signature for a 'wait_until_field_equals' procedure.
183 """
184 field_name = self.qualified_field_name(
185 register=register, register_array=register_array, field=field
186 )
187 field_description = self.field_description(
188 register=register, register_array=register_array, field=field
189 )
191 value_type = self.field_type_name(
192 register=register, register_array=register_array, field=field
193 )
195 return f"""\
196 -- Wait until the {field_description} equals the given 'value'.
197 procedure wait_until_{field_name}_equals(
198 signal net : inout network_t;
199{self.get_array_index_port(register_array=register_array)}\
200 value : in {value_type};
201 base_address : in unsigned(32 - 1 downto 0) := (others => '0');
202 bus_handle : in bus_master_t := register_bus_master;
203 timeout : delay_length := max_timeout;
204 message : string := ""
205 )\
206"""
208 def _implementations(self) -> str:
209 """
210 Get implementations of all procedures.
211 """
212 separator = self.get_separator_line(indent=2)
213 vhdl = ""
215 for register, register_array in self.iterate_software_accessible_registers(
216 direction=SoftwareAccessDirection.READ
217 ):
218 implementations = [
219 self._register_wait_until_equals_implementation(
220 register=register, register_array=register_array
221 )
222 ]
224 implementations.extend(
225 self._field_wait_until_equals_implementation(
226 register=register, register_array=register_array, field=field
227 )
228 for field in register.fields
229 )
231 vhdl += separator
232 vhdl += "\n".join(implementations)
233 vhdl += separator
234 vhdl += "\n"
236 return vhdl
238 def _register_wait_until_equals_implementation(
239 self, register: Register, register_array: RegisterArray | None
240 ) -> str:
241 """
242 Get implementation for a 'wait_until_reg_equals' procedure.
243 """
244 signature = self._register_wait_until_equals_signature(
245 register=register, register_array=register_array
246 )
248 conversion = "to_slv(value)" if register.fields else "value"
250 return f"""\
251{signature} is
252 constant reg_value : register_t := {conversion};
254{self._get_common_constants(register=register, register_array=register_array, field=None)}\
255 begin
256 wait_until_read_equals(
257 net => net,
258 bus_handle => bus_handle,
259 addr => std_ulogic_vector(reg_address),
260 value => reg_value,
261 timeout => timeout,
262 msg => get_message
263 );
264 end procedure;
265"""
267 def _field_wait_until_equals_implementation(
268 self,
269 register: Register,
270 register_array: RegisterArray | None,
271 field: RegisterField,
272 ) -> str:
273 """
274 Get implementation for a 'wait_until_field_equals' procedure.
275 """
276 signature = self._field_wait_until_equals_signature(
277 register=register, register_array=register_array, field=field
278 )
279 field_name = self.qualified_field_name(
280 register=register, register_array=register_array, field=field
281 )
282 field_to_slv = self.field_to_slv(field=field, field_name=field_name, value="value")
284 return f"""\
285{signature} is
286 constant reg_value : register_t := (
287 {field_name} => {field_to_slv},
288 others => '-'
289 );
291{self._get_common_constants(register=register, register_array=register_array, field=field)}\
292 begin
293 wait_until_read_equals(
294 net => net,
295 bus_handle => bus_handle,
296 addr => std_ulogic_vector(reg_address),
297 value => reg_value,
298 timeout => timeout,
299 msg => get_message
300 );
301 end procedure;
302"""
304 def _get_common_constants(
305 self, register: Register, register_array: RegisterArray | None, field: RegisterField | None
306 ) -> str:
307 """
308 Get constants code that is common for all 'wait_until_*_equals' procedures.
309 """
310 field_description = f" the '{field.name}' field in" if field else ""
312 return f"""\
313{self.reg_index_constant(register=register, register_array=register_array)}\
314{self.reg_address_constant()}\
316{self.get_register_array_message(register_array=register_array)}\
317{self.get_base_address_message()}\
318 constant base_message : string := (
319 "Timeout while waiting for{field_description} the '{register.name}' register"
320 & register_array_message
321 & base_address_message
322 & " to equal the given value: "
323 & to_string(reg_value)
324 & "."
325 );
326{self.get_message()}\
327"""