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

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# -------------------------------------------------------------------------------------------------- 

9 

10# Standard libraries 

11from pathlib import Path 

12from typing import TYPE_CHECKING, Any, Optional 

13 

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 

19 

20# Local folder libraries 

21from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon 

22 

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 

28 

29 

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. 

34 

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. 

37 

38 Uses VUnit Verification Component calls, via the procedures from 

39 :class:`.VhdlSimulationReadWritePackageGenerator`. 

40 

41 The generated VHDL file needs also the generated packages from 

42 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

43 """ 

44 

45 __version__ = "1.0.0" 

46 

47 SHORT_DESCRIPTION = "VHDL simulation check package" 

48 

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" 

55 

56 def create(self, **kwargs: Any) -> Path: 

57 """ 

58 See super class for API details. 

59 

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) 

64 

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 

70 

71 vhdl = f"""\ 

72{self.header} 

73library ieee; 

74use ieee.fixed_pkg.all; 

75use ieee.numeric_std.all; 

76use ieee.std_logic_1164.all; 

77 

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; 

84 

85library common; 

86use common.addr_pkg.addr_t; 

87 

88library reg_file; 

89use reg_file.reg_operations_pkg.regs_bus_master; 

90 

91use work.{self.name}_register_read_write_pkg.all; 

92use work.{self.name}_register_record_pkg.all; 

93use work.{self.name}_regs_pkg.all; 

94 

95 

96package {package_name} is 

97 

98{self._declarations()}\ 

99end package; 

100 

101package body {package_name} is 

102 

103{self._implementations()}\ 

104end package body; 

105""" 

106 

107 return vhdl 

108 

109 def _declarations(self) -> str: 

110 """ 

111 Get procedure declarations for all procedures. 

112 """ 

113 separator = self.get_separator_line(indent=2) 

114 vhdl = "" 

115 

116 for register, register_array in self.iterate_software_accessible_registers( 

117 direction=SoftwareAccessDirection.READ 

118 ): 

119 declarations = [] 

120 

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") 

128 

129 if declarations: 

130 vhdl += separator 

131 vhdl += "\n".join(declarations) 

132 vhdl += separator 

133 vhdl += "\n" 

134 

135 return vhdl 

136 

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 ) 

149 

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 ) 

156 

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""" 

169 

170 def _implementations(self) -> str: 

171 """ 

172 Get implementations of all procedures. 

173 """ 

174 separator = self.get_separator_line(indent=2) 

175 vhdl = "" 

176 

177 for register, register_array in self.iterate_software_accessible_registers( 

178 direction=SoftwareAccessDirection.READ 

179 ): 

180 implementations = [] 

181 

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 ) 

188 

189 if implementations: 

190 vhdl += separator 

191 vhdl += "\n".join(implementations) 

192 vhdl += separator 

193 vhdl += "\n" 

194 

195 return vhdl 

196 

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 ) 

209 

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 ) 

216 

217 value_type = self.field_type_name( 

218 register=register, register_array=register_array, field=field 

219 ) 

220 

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})" 

235 

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") & ")"' 

240 

241 raise ValueError(f"Unsupported field type: {field}") 

242 

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;""" 

257 

258 else: 

259 check = " check_equal(got=>got, expected=>expected, msg=>get_message);" 

260 

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()}\ 

272 

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}; 

284 

285{check} 

286 end procedure; 

287"""