Coverage for hdl_registers/generator/vhdl/simulation/wait_until_package.py: 96%
76 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +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, Optional
14# First party libraries
15from hdl_registers.register_mode import SoftwareAccessDirection
17# Local folder libraries
18from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon
20if TYPE_CHECKING:
21 # First party libraries
22 from hdl_registers.field.register_field import RegisterField
23 from hdl_registers.register import Register
24 from hdl_registers.register_array import RegisterArray
27class VhdlSimulationWaitUntilPackageGenerator(VhdlSimulationGeneratorCommon):
28 """
29 Generate VHDL code with ``wait_until_X_equals`` procedures that simplify simulation.
30 See the :ref:`generator_vhdl` article for usage details.
32 * For each readable register, a procedure that waits until the register assumes a
33 given natively-typed record value.
35 * For each field in each readable register, a procedure that waits until the field assumes a
36 given natively-typed value.
38 Uses VUnit Verification Component calls to create bus read operations.
40 The generated VHDL file needs also the generated packages from
41 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
42 """
44 __version__ = "1.0.0"
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(self, **kwargs: Any) -> Path:
56 """
57 See super class for API details.
59 Overloaded here because this package file shall only be created if the register list
60 actually has any registers.
61 """
62 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
64 def get_code(self, **kwargs: Any) -> str:
65 """
66 Get a package with ``wait_until_X_equals`` methods for registers/fields.
67 """
68 package_name = self.output_file.stem
70 vhdl = f"""\
71library ieee;
72use ieee.fixed_pkg.all;
73use ieee.std_logic_1164.all;
74use ieee.numeric_std.all;
76library vunit_lib;
77use vunit_lib.bus_master_pkg.bus_master_t;
78use vunit_lib.bus_master_pkg.wait_until_read_equals;
79use vunit_lib.com_types_pkg.max_timeout;
80use vunit_lib.com_types_pkg.network_t;
81use vunit_lib.string_ops.hex_image;
83library common;
84use common.addr_pkg.addr_t;
85use common.addr_pkg.addr_width;
87library register_file;
88use register_file.register_file_pkg.register_t;
89use register_file.register_operations_pkg.register_bus_master;
91use work.{self.name}_regs_pkg.all;
92use work.{self.name}_register_record_pkg.all;
95package {package_name} is
97{self._declarations()}\
98end package;
100package body {package_name} is
102{self._implementations()}\
103end package body;
104"""
106 return vhdl
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: Optional["RegisterArray"]
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 addr_t := (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: Optional["RegisterArray"],
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 addr_t := (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 = []
220 implementations.append(
221 self._register_wait_until_equals_implementation(
222 register=register, register_array=register_array
223 )
224 )
226 for field in register.fields:
227 implementations.append(
228 self._field_wait_until_equals_implementation(
229 register=register, register_array=register_array, field=field
230 )
231 )
233 vhdl += separator
234 vhdl += "\n".join(implementations)
235 vhdl += separator
236 vhdl += "\n"
238 return vhdl
240 def _register_wait_until_equals_implementation(
241 self, register: "Register", register_array: Optional["RegisterArray"]
242 ) -> str:
243 """
244 Get implementation for a 'wait_until_reg_equals' procedure.
245 """
246 signature = self._register_wait_until_equals_signature(
247 register=register, register_array=register_array
248 )
250 conversion = "to_slv(value)" if register.fields else "value"
252 return f"""\
253{signature} is
254 constant reg_value : register_t := {conversion};
256{self._get_common_constants(register=register, register_array=register_array, field=None)}\
257 begin
258 wait_until_read_equals(
259 net => net,
260 bus_handle => bus_handle,
261 addr => std_ulogic_vector(reg_address),
262 value => reg_value,
263 timeout => timeout,
264 msg => get_message
265 );
266 end procedure;
267"""
269 def _field_wait_until_equals_implementation(
270 self,
271 register: "Register",
272 register_array: Optional["RegisterArray"],
273 field: "RegisterField",
274 ) -> str:
275 """
276 Get implementation for a 'wait_until_field_equals' procedure.
277 """
278 signature = self._field_wait_until_equals_signature(
279 register=register, register_array=register_array, field=field
280 )
281 field_name = self.qualified_field_name(
282 register=register, register_array=register_array, field=field
283 )
284 field_to_slv = self.field_to_slv(field=field, field_name=field_name, value="value")
286 return f"""\
287{signature} is
288 constant reg_value : register_t := (
289 {field_name} => {field_to_slv},
290 others => '-'
291 );
293{self._get_common_constants(register=register, register_array=register_array, field=field)}\
294 begin
295 wait_until_read_equals(
296 net => net,
297 bus_handle => bus_handle,
298 addr => std_ulogic_vector(reg_address),
299 value => reg_value,
300 timeout => timeout,
301 msg => get_message
302 );
303 end procedure;
304"""
306 def _get_common_constants(
307 self,
308 register: "Register",
309 register_array: Optional["RegisterArray"],
310 field: Optional["RegisterField"],
311 ) -> str:
312 """
313 Get constants code that is common for all 'wait_until_*_equals' procedures.
314 """
315 if field:
316 field_description = f" the '{field.name}' field in"
317 else:
318 field_description = ""
320 return f"""\
321{self.reg_index_constant(register=register, register_array=register_array)}\
322{self.reg_address_constant()}\
324{self.get_register_array_message(register_array=register_array)}\
325{self.get_base_address_message()}\
326 constant base_message : string := (
327 "Timeout while waiting for{field_description} the '{register.name}' register"
328 & register_array_message
329 & base_address_message
330 & " to equal the given value: "
331 & to_string(reg_value)
332 & "."
333 );
334{self.get_message()}\
335"""