Coverage for hdl_registers/generator/vhdl/simulation/wait_until_package.py: 96%
76 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, 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"""\
71{self.header}
72library ieee;
73use ieee.fixed_pkg.all;
74use ieee.std_logic_1164.all;
75use ieee.numeric_std.all;
77library vunit_lib;
78use vunit_lib.bus_master_pkg.bus_master_t;
79use vunit_lib.bus_master_pkg.wait_until_read_equals;
80use vunit_lib.com_types_pkg.max_timeout;
81use vunit_lib.com_types_pkg.network_t;
82use vunit_lib.string_ops.hex_image;
84library common;
85use common.addr_pkg.addr_t;
86use common.addr_pkg.addr_width;
88library reg_file;
89use reg_file.reg_file_pkg.reg_t;
90use reg_file.reg_operations_pkg.regs_bus_master;
92use work.{self.name}_regs_pkg.all;
93use work.{self.name}_register_record_pkg.all;
96package {package_name} is
98{self._declarations()}\
99end package;
101package body {package_name} is
103{self._implementations()}\
104end package body;
105"""
107 return vhdl
109 def _declarations(self) -> str:
110 """
111 Get procedure declarations for all procedures.
112 """
113 separator = self.get_separator_line(indent=2)
114 vhdl = ""
116 for register, register_array in self.iterate_software_accessible_registers(
117 direction=SoftwareAccessDirection.READ
118 ):
119 declarations = []
121 signature = self._register_wait_until_equals_signature(
122 register=register, register_array=register_array
123 )
124 declarations.append(f"{signature};\n")
126 for field in register.fields:
127 signature = self._field_wait_until_equals_signature(
128 register=register, register_array=register_array, field=field
129 )
130 declarations.append(f"{signature};\n")
132 vhdl += separator
133 vhdl += "\n".join(declarations)
134 vhdl += separator
135 vhdl += "\n"
137 return vhdl
139 def _register_wait_until_equals_signature(
140 self, register: "Register", register_array: Optional["RegisterArray"]
141 ) -> str:
142 """
143 Get signature for a 'wait_until_reg_equals' procedure.
144 """
145 register_name = self.qualified_register_name(
146 register=register, register_array=register_array
147 )
148 register_description = self.register_description(
149 register=register, register_array=register_array
150 )
152 if register.fields:
153 value_type = f"{register_name}_t"
154 slv_comment = ""
155 else:
156 value_type = "reg_t"
157 slv_comment = (
158 " -- Note that '-' can be used as a wildcard in 'value' since 'check_match' is \n"
159 " -- used to check for equality.\n"
160 )
162 return f"""\
163 -- Wait until the {register_description} equals the given 'value'.
164{slv_comment}\
165 procedure wait_until_{register_name}_equals(
166 signal net : inout network_t;
167{self.get_array_index_port(register_array=register_array)}\
168 value : in {value_type};
169 base_address : in addr_t := (others => '0');
170 bus_handle : in bus_master_t := regs_bus_master;
171 timeout : delay_length := max_timeout;
172 message : string := ""
173 )\
174"""
176 def _field_wait_until_equals_signature(
177 self,
178 register: "Register",
179 register_array: Optional["RegisterArray"],
180 field: "RegisterField",
181 ) -> str:
182 """
183 Get signature for a 'wait_until_field_equals' procedure.
184 """
185 field_name = self.qualified_field_name(
186 register=register, register_array=register_array, field=field
187 )
188 field_description = self.field_description(
189 register=register, register_array=register_array, field=field
190 )
192 value_type = self.field_type_name(
193 register=register, register_array=register_array, field=field
194 )
196 return f"""\
197 -- Wait until the {field_description} equals the given 'value'.
198 procedure wait_until_{field_name}_equals(
199 signal net : inout network_t;
200{self.get_array_index_port(register_array=register_array)}\
201 value : in {value_type};
202 base_address : in addr_t := (others => '0');
203 bus_handle : in bus_master_t := regs_bus_master;
204 timeout : delay_length := max_timeout;
205 message : string := ""
206 )\
207"""
209 def _implementations(self) -> str:
210 """
211 Get implementations of all procedures.
212 """
213 separator = self.get_separator_line(indent=2)
214 vhdl = ""
216 for register, register_array in self.iterate_software_accessible_registers(
217 direction=SoftwareAccessDirection.READ
218 ):
219 implementations = []
221 implementations.append(
222 self._register_wait_until_equals_implementation(
223 register=register, register_array=register_array
224 )
225 )
227 for field in register.fields:
228 implementations.append(
229 self._field_wait_until_equals_implementation(
230 register=register, register_array=register_array, field=field
231 )
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: Optional["RegisterArray"]
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 : reg_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: Optional["RegisterArray"],
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 : reg_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,
309 register: "Register",
310 register_array: Optional["RegisterArray"],
311 field: Optional["RegisterField"],
312 ) -> str:
313 """
314 Get constants code that is common for all 'wait_until_*_equals' procedures.
315 """
316 if field:
317 field_description = f" the '{field.name}' field in"
318 else:
319 field_description = ""
321 return f"""\
322{self.reg_index_constant(register=register, register_array=register_array)}\
323{self.reg_address_constant()}\
325{self.get_register_array_message(register_array=register_array)}\
326{self.get_base_address_message()}\
327 constant base_message : string := (
328 "Timeout while waiting for{field_description} the '{register.name}' register"
329 & register_array_message
330 & base_address_message
331 & " to equal the given value: "
332 & to_string(reg_value)
333 & "."
334 );
335{self.get_message()}\
336"""