Coverage for hdl_registers/generator/vhdl/simulation/check_package.py: 94%
70 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-07 20:51 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-07 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.field.bit_vector import BitVector
16from hdl_registers.field.enumeration import Enumeration
17from hdl_registers.field.numerical_interpretation import Fixed
18from hdl_registers.register_mode import SoftwareAccessDirection
20# Local folder libraries
21from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon
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 VhdlSimulationCheckPackageGenerator(VhdlSimulationGeneratorCommon):
31 """
32 Generate VHDL code with simulation procedures to check the values of registers and fields.
33 See the :ref:`generator_vhdl` article for usage details.
35 * For each field in each readable register, a procedure that checks that the register field's
36 current value is equal to a given expected value.
38 Uses VUnit Verification Component calls, via the procedures from
39 :class:`.VhdlSimulationReadWritePackageGenerator`.
41 The generated VHDL file needs also the generated packages from
42 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
43 """
45 __version__ = "1.0.0"
47 SHORT_DESCRIPTION = "VHDL simulation check package"
49 @property
50 def output_file(self) -> Path:
51 """
52 Result will be placed in this file.
53 """
54 return self.output_folder / f"{self.name}_register_check_pkg.vhd"
56 def create(self, **kwargs: Any) -> Path:
57 """
58 See super class for API details.
60 Overloaded here because this package file shall only be created if the register list
61 actually has any registers.
62 """
63 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
65 def get_code(self, **kwargs: Any) -> str:
66 """
67 Get a package with methods for checking register/field values.
68 """
69 package_name = self.output_file.stem
71 vhdl = f"""\
72{self.header}
73library ieee;
74use ieee.fixed_pkg.all;
75use ieee.numeric_std.all;
76use ieee.std_logic_1164.all;
78library vunit_lib;
79use vunit_lib.bus_master_pkg.bus_master_t;
80use vunit_lib.check_pkg.all;
81use vunit_lib.checker_pkg.all;
82use vunit_lib.com_types_pkg.network_t;
83use vunit_lib.string_ops.hex_image;
85library common;
86use common.addr_pkg.addr_t;
88library reg_file;
89use reg_file.reg_operations_pkg.regs_bus_master;
91use work.{self.name}_register_read_write_pkg.all;
92use work.{self.name}_register_record_pkg.all;
93use work.{self.name}_regs_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 for field in register.fields:
122 signature = self._field_check_signature(
123 register=register,
124 register_array=register_array,
125 field=field,
126 )
127 declarations.append(f"{signature};\n")
129 if declarations:
130 vhdl += separator
131 vhdl += "\n".join(declarations)
132 vhdl += separator
133 vhdl += "\n"
135 return vhdl
137 def _field_check_signature(
138 self,
139 register: "Register",
140 register_array: Optional["RegisterArray"],
141 field: "RegisterField",
142 ) -> str:
143 """
144 Get signature for a 'check_X_equal' procedure.
145 """
146 value_type = self.field_type_name(
147 register=register, register_array=register_array, field=field
148 )
150 field_name = self.qualified_field_name(
151 register=register, register_array=register_array, field=field
152 )
153 field_description = self.field_description(
154 register=register, field=field, register_array=register_array
155 )
157 return f"""\
158 -- Check that the current value of the {field_description}
159 -- equals the given 'expected' value.
160 procedure check_{field_name}_equal(
161 signal net : inout network_t;
162{self.get_array_index_port(register_array=register_array)}\
163 expected : in {value_type};
164 base_address : in addr_t := (others => '0');
165 bus_handle : in bus_master_t := regs_bus_master;
166 message : in string := ""
167 )\
168"""
170 def _implementations(self) -> str:
171 """
172 Get implementations of all procedures.
173 """
174 separator = self.get_separator_line(indent=2)
175 vhdl = ""
177 for register, register_array in self.iterate_software_accessible_registers(
178 direction=SoftwareAccessDirection.READ
179 ):
180 implementations = []
182 for field in register.fields:
183 implementations.append(
184 self._field_check_implementation(
185 register=register, register_array=register_array, field=field
186 )
187 )
189 if implementations:
190 vhdl += separator
191 vhdl += "\n".join(implementations)
192 vhdl += separator
193 vhdl += "\n"
195 return vhdl
197 def _field_check_implementation(
198 self,
199 register: "Register",
200 register_array: Optional["RegisterArray"],
201 field: "RegisterField",
202 ) -> str:
203 """
204 Get implementation for a 'check_X_equal' procedure.
205 """
206 signature = self._field_check_signature(
207 register=register, register_array=register_array, field=field
208 )
210 register_name = self.qualified_register_name(
211 register=register, register_array=register_array
212 )
213 field_name = self.qualified_field_name(
214 register=register, register_array=register_array, field=field
215 )
217 value_type = self.field_type_name(
218 register=register, register_array=register_array, field=field
219 )
221 if isinstance(field, Enumeration) or (
222 isinstance(field, BitVector) and isinstance(field.numerical_interpretation, Fixed)
223 ):
224 # These field types do not work with the standard VUnit check procedures.
225 # Enumeration because it is a custom type.
226 # ufixed and sfixed could for all intents and purposes be supported in VUnit,
227 # but they are not at the moment (4.7.0).
228 # Instead, do a custom check, with error reporting in the same way as in check_equal
229 # procedures in VUnit.
230 # The VUnit check also has a logging upon a passing check, but we skip that so we do
231 # not have to copy so much code.
232 def to_string(name: str) -> str:
233 if isinstance(field, Enumeration):
234 return f"to_string({name})"
236 if isinstance(field, BitVector) and isinstance(
237 field.numerical_interpretation, Fixed
238 ):
239 return f'to_string({name}) & " (" & to_string(to_real({name}), "%f") & ")"'
241 raise ValueError(f"Unsupported field type: {field}")
243 check = f"""\
244 if got /= expected then
245 failing_check(
246 checker => default_checker,
247 msg => p_std_msg(
248 check_result => "Equality check failed",
249 msg => get_message,
250 ctx => (
251 "Got " & {to_string("got")} & "."
252 & " Expected " & {to_string("expected")} & "."
253 )
254 )
255 );
256 end if;"""
258 else:
259 check = " check_equal(got=>got, expected=>expected, msg=>get_message);"
261 return f"""\
262{signature} is
263{self.get_register_array_message(register_array=register_array)}\
264{self.get_base_address_message()}\
265 constant base_message : string := (
266 "Checking the '{field.name}' field in the '{register.name}' register"
267 & register_array_message
268 & base_address_message
269 & "."
270 );
271{self.get_message()}\
273 variable got_reg : {register_name}_t := {register_name}_init;
274 variable got : {value_type} := {field_name}_init;
275 begin
276 read_{register_name}(
277 net => net,
278{self.get_array_index_association(register_array=register_array)}\
279 value => got_reg,
280 base_address => base_address,
281 bus_handle => bus_handle
282 );
283 got := got_reg.{field.name};
285{check}
286 end procedure;
287"""