Coverage for hdl_registers/generator/vhdl/simulation/wait_until_package.py: 94%
72 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.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 """
43 __version__ = "1.0.0"
45 SHORT_DESCRIPTION = "VHDL simulation wait until package"
47 @property
48 def output_file(self) -> Path:
49 """
50 Result will be placed in this file.
51 """
52 return self.output_folder / f"{self.name}_register_wait_until_pkg.vhd"
54 def create(
55 self,
56 **kwargs: Any, # noqa: ANN401
57 ) -> Path:
58 """
59 See super class for API details.
61 Overloaded here because this package file shall only be created if the register list
62 actually has any registers.
63 """
64 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
66 def get_code(
67 self,
68 **kwargs: Any, # noqa: ANN401, ARG002
69 ) -> str:
70 """
71 Get a package with ``wait_until_X_equals`` methods for registers/fields.
72 """
73 package_name = self.output_file.stem
75 return f"""\
76library ieee;
77use ieee.fixed_pkg.all;
78use ieee.std_logic_1164.all;
79use ieee.numeric_std.all;
81library vunit_lib;
82use vunit_lib.bus_master_pkg.bus_master_t;
83use vunit_lib.bus_master_pkg.wait_until_read_equals;
84use vunit_lib.com_types_pkg.max_timeout;
85use vunit_lib.com_types_pkg.network_t;
86use vunit_lib.string_ops.hex_image;
88library common;
89use common.addr_pkg.addr_t;
90use common.addr_pkg.addr_width;
92library register_file;
93use register_file.register_file_pkg.register_t;
94use register_file.register_operations_pkg.register_bus_master;
96use work.{self.name}_regs_pkg.all;
97use work.{self.name}_register_record_pkg.all;
100package {package_name} is
102{self._declarations()}\
103end package;
105package body {package_name} is
107{self._implementations()}\
108end package body;
109"""
111 def _declarations(self) -> str:
112 """
113 Get procedure declarations for all procedures.
114 """
115 separator = self.get_separator_line(indent=2)
116 vhdl = ""
118 for register, register_array in self.iterate_software_accessible_registers(
119 direction=SoftwareAccessDirection.READ
120 ):
121 declarations = []
123 signature = self._register_wait_until_equals_signature(
124 register=register, register_array=register_array
125 )
126 declarations.append(f"{signature};\n")
128 for field in register.fields:
129 signature = self._field_wait_until_equals_signature(
130 register=register, register_array=register_array, field=field
131 )
132 declarations.append(f"{signature};\n")
134 vhdl += separator
135 vhdl += "\n".join(declarations)
136 vhdl += separator
137 vhdl += "\n"
139 return vhdl
141 def _register_wait_until_equals_signature(
142 self, register: Register, register_array: RegisterArray | None
143 ) -> str:
144 """
145 Get signature for a 'wait_until_reg_equals' procedure.
146 """
147 register_name = self.qualified_register_name(
148 register=register, register_array=register_array
149 )
150 register_description = self.register_description(
151 register=register, register_array=register_array
152 )
154 if register.fields:
155 value_type = f"{register_name}_t"
156 slv_comment = ""
157 else:
158 value_type = "register_t"
159 slv_comment = (
160 " -- Note that '-' can be used as a wildcard in 'value' since 'check_match' is \n"
161 " -- used to check for equality.\n"
162 )
164 return f"""\
165 -- Wait until the {register_description} equals the given 'value'.
166{slv_comment}\
167 procedure wait_until_{register_name}_equals(
168 signal net : inout network_t;
169{self.get_array_index_port(register_array=register_array)}\
170 value : in {value_type};
171 base_address : in addr_t := (others => '0');
172 bus_handle : in bus_master_t := register_bus_master;
173 timeout : delay_length := max_timeout;
174 message : string := ""
175 )\
176"""
178 def _field_wait_until_equals_signature(
179 self,
180 register: Register,
181 register_array: RegisterArray | None,
182 field: RegisterField,
183 ) -> str:
184 """
185 Get signature for a 'wait_until_field_equals' procedure.
186 """
187 field_name = self.qualified_field_name(
188 register=register, register_array=register_array, field=field
189 )
190 field_description = self.field_description(
191 register=register, register_array=register_array, field=field
192 )
194 value_type = self.field_type_name(
195 register=register, register_array=register_array, field=field
196 )
198 return f"""\
199 -- Wait until the {field_description} equals the given 'value'.
200 procedure wait_until_{field_name}_equals(
201 signal net : inout network_t;
202{self.get_array_index_port(register_array=register_array)}\
203 value : in {value_type};
204 base_address : in addr_t := (others => '0');
205 bus_handle : in bus_master_t := register_bus_master;
206 timeout : delay_length := max_timeout;
207 message : string := ""
208 )\
209"""
211 def _implementations(self) -> str:
212 """
213 Get implementations of all procedures.
214 """
215 separator = self.get_separator_line(indent=2)
216 vhdl = ""
218 for register, register_array in self.iterate_software_accessible_registers(
219 direction=SoftwareAccessDirection.READ
220 ):
221 implementations = [
222 self._register_wait_until_equals_implementation(
223 register=register, register_array=register_array
224 )
225 ]
227 implementations.extend(
228 self._field_wait_until_equals_implementation(
229 register=register, register_array=register_array, field=field
230 )
231 for field in register.fields
232 )
234 vhdl += separator
235 vhdl += "\n".join(implementations)
236 vhdl += separator
237 vhdl += "\n"
239 return vhdl
241 def _register_wait_until_equals_implementation(
242 self, register: Register, register_array: RegisterArray | None
243 ) -> str:
244 """
245 Get implementation for a 'wait_until_reg_equals' procedure.
246 """
247 signature = self._register_wait_until_equals_signature(
248 register=register, register_array=register_array
249 )
251 conversion = "to_slv(value)" if register.fields else "value"
253 return f"""\
254{signature} is
255 constant reg_value : register_t := {conversion};
257{self._get_common_constants(register=register, register_array=register_array, field=None)}\
258 begin
259 wait_until_read_equals(
260 net => net,
261 bus_handle => bus_handle,
262 addr => std_ulogic_vector(reg_address),
263 value => reg_value,
264 timeout => timeout,
265 msg => get_message
266 );
267 end procedure;
268"""
270 def _field_wait_until_equals_implementation(
271 self,
272 register: Register,
273 register_array: RegisterArray | None,
274 field: RegisterField,
275 ) -> str:
276 """
277 Get implementation for a 'wait_until_field_equals' procedure.
278 """
279 signature = self._field_wait_until_equals_signature(
280 register=register, register_array=register_array, field=field
281 )
282 field_name = self.qualified_field_name(
283 register=register, register_array=register_array, field=field
284 )
285 field_to_slv = self.field_to_slv(field=field, field_name=field_name, value="value")
287 return f"""\
288{signature} is
289 constant reg_value : register_t := (
290 {field_name} => {field_to_slv},
291 others => '-'
292 );
294{self._get_common_constants(register=register, register_array=register_array, field=field)}\
295 begin
296 wait_until_read_equals(
297 net => net,
298 bus_handle => bus_handle,
299 addr => std_ulogic_vector(reg_address),
300 value => reg_value,
301 timeout => timeout,
302 msg => get_message
303 );
304 end procedure;
305"""
307 def _get_common_constants(
308 self, register: Register, register_array: RegisterArray | None, field: RegisterField | None
309 ) -> str:
310 """
311 Get constants code that is common for all 'wait_until_*_equals' procedures.
312 """
313 field_description = f" the '{field.name}' field in" if field else ""
315 return f"""\
316{self.reg_index_constant(register=register, register_array=register_array)}\
317{self.reg_address_constant()}\
319{self.get_register_array_message(register_array=register_array)}\
320{self.get_base_address_message()}\
321 constant base_message : string := (
322 "Timeout while waiting for{field_description} the '{register.name}' register"
323 & register_array_message
324 & base_address_message
325 & " to equal the given value: "
326 & to_string(reg_value)
327 & "."
328 );
329{self.get_message()}\
330"""