Coverage for hdl_registers/generator/vhdl/simulation/wait_until_package.py: 94%

72 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-29 06:41 +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 

10from __future__ import annotations 

11 

12from typing import TYPE_CHECKING, Any 

13 

14from hdl_registers.register_mode import SoftwareAccessDirection 

15 

16from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon 

17 

18if TYPE_CHECKING: 

19 from pathlib import Path 

20 

21 from hdl_registers.field.register_field import RegisterField 

22 from hdl_registers.register import Register 

23 from hdl_registers.register_array import RegisterArray 

24 

25 

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. 

30 

31 * For each readable register, a procedure that waits until the register assumes a 

32 given natively-typed record value. 

33 

34 * For each field in each readable register, a procedure that waits until the field assumes a 

35 given natively-typed value. 

36 

37 Uses VUnit Verification Component calls to create bus read operations. 

38 

39 The generated VHDL file needs also the generated packages from 

40 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

41 See :ref:`vhdl_dependencies` for further dependencies. 

42 """ 

43 

44 __version__ = "1.0.2" 

45 

46 SHORT_DESCRIPTION = "VHDL simulation wait until package" 

47 

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" 

54 

55 def create( 

56 self, 

57 **kwargs: Any, # noqa: ANN401 

58 ) -> Path: 

59 """ 

60 See super class for API details. 

61 

62 Overloaded here because this package file shall only be created if the register list 

63 actually has any registers. 

64 """ 

65 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

66 

67 def get_code( 

68 self, 

69 **kwargs: Any, # noqa: ANN401, ARG002 

70 ) -> str: 

71 """ 

72 Get a package with ``wait_until_X_equals`` methods for registers/fields. 

73 """ 

74 package_name = self.output_file.stem 

75 

76 return f"""\ 

77library ieee; 

78use ieee.fixed_pkg.all; 

79use ieee.std_logic_1164.all; 

80use ieee.numeric_std.all; 

81 

82library vunit_lib; 

83use vunit_lib.bus_master_pkg.bus_master_t; 

84use vunit_lib.bus_master_pkg.wait_until_read_equals; 

85use vunit_lib.com_types_pkg.max_timeout; 

86use vunit_lib.com_types_pkg.network_t; 

87use vunit_lib.string_ops.hex_image; 

88 

89library register_file; 

90use register_file.register_file_pkg.register_t; 

91use register_file.register_operations_pkg.register_bus_master; 

92 

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

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

95 

96 

97package {package_name} is 

98 

99{self._declarations()}\ 

100end package; 

101 

102package body {package_name} is 

103 

104{self._implementations()}\ 

105end package body; 

106""" 

107 

108 def _declarations(self) -> str: 

109 """ 

110 Get procedure declarations for all procedures. 

111 """ 

112 separator = self.get_separator_line(indent=2) 

113 vhdl = "" 

114 

115 for register, register_array in self.iterate_software_accessible_registers( 

116 direction=SoftwareAccessDirection.READ 

117 ): 

118 declarations = [] 

119 

120 signature = self._register_wait_until_equals_signature( 

121 register=register, register_array=register_array 

122 ) 

123 declarations.append(f"{signature};\n") 

124 

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

130 

131 vhdl += separator 

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

133 vhdl += separator 

134 vhdl += "\n" 

135 

136 return vhdl 

137 

138 def _register_wait_until_equals_signature( 

139 self, register: Register, register_array: RegisterArray | None 

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 ) 

150 

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 ) 

160 

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 unsigned(32 - 1 downto 0) := (others => '0'); 

169 bus_handle : in bus_master_t := register_bus_master; 

170 timeout : delay_length := max_timeout; 

171 message : string := "" 

172 )\ 

173""" 

174 

175 def _field_wait_until_equals_signature( 

176 self, 

177 register: Register, 

178 register_array: RegisterArray | None, 

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 ) 

190 

191 value_type = self.field_type_name( 

192 register=register, register_array=register_array, field=field 

193 ) 

194 

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 unsigned(32 - 1 downto 0) := (others => '0'); 

202 bus_handle : in bus_master_t := register_bus_master; 

203 timeout : delay_length := max_timeout; 

204 message : string := "" 

205 )\ 

206""" 

207 

208 def _implementations(self) -> str: 

209 """ 

210 Get implementations of all procedures. 

211 """ 

212 separator = self.get_separator_line(indent=2) 

213 vhdl = "" 

214 

215 for register, register_array in self.iterate_software_accessible_registers( 

216 direction=SoftwareAccessDirection.READ 

217 ): 

218 implementations = [ 

219 self._register_wait_until_equals_implementation( 

220 register=register, register_array=register_array 

221 ) 

222 ] 

223 

224 implementations.extend( 

225 self._field_wait_until_equals_implementation( 

226 register=register, register_array=register_array, field=field 

227 ) 

228 for field in register.fields 

229 ) 

230 

231 vhdl += separator 

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

233 vhdl += separator 

234 vhdl += "\n" 

235 

236 return vhdl 

237 

238 def _register_wait_until_equals_implementation( 

239 self, register: Register, register_array: RegisterArray | None 

240 ) -> str: 

241 """ 

242 Get implementation for a 'wait_until_reg_equals' procedure. 

243 """ 

244 signature = self._register_wait_until_equals_signature( 

245 register=register, register_array=register_array 

246 ) 

247 

248 conversion = "to_slv(value)" if register.fields else "value" 

249 

250 return f"""\ 

251{signature} is 

252 constant reg_value : register_t := {conversion}; 

253 

254{self._get_common_constants(register=register, register_array=register_array, field=None)}\ 

255 begin 

256 wait_until_read_equals( 

257 net => net, 

258 bus_handle => bus_handle, 

259 addr => std_ulogic_vector(reg_address), 

260 value => reg_value, 

261 timeout => timeout, 

262 msg => get_message 

263 ); 

264 end procedure; 

265""" 

266 

267 def _field_wait_until_equals_implementation( 

268 self, 

269 register: Register, 

270 register_array: RegisterArray | None, 

271 field: RegisterField, 

272 ) -> str: 

273 """ 

274 Get implementation for a 'wait_until_field_equals' procedure. 

275 """ 

276 signature = self._field_wait_until_equals_signature( 

277 register=register, register_array=register_array, field=field 

278 ) 

279 field_name = self.qualified_field_name( 

280 register=register, register_array=register_array, field=field 

281 ) 

282 field_to_slv = self.field_to_slv(field=field, field_name=field_name, value="value") 

283 

284 return f"""\ 

285{signature} is 

286 constant reg_value : register_t := ( 

287 {field_name} => {field_to_slv}, 

288 others => '-' 

289 ); 

290 

291{self._get_common_constants(register=register, register_array=register_array, field=field)}\ 

292 begin 

293 wait_until_read_equals( 

294 net => net, 

295 bus_handle => bus_handle, 

296 addr => std_ulogic_vector(reg_address), 

297 value => reg_value, 

298 timeout => timeout, 

299 msg => get_message 

300 ); 

301 end procedure; 

302""" 

303 

304 def _get_common_constants( 

305 self, register: Register, register_array: RegisterArray | None, field: RegisterField | None 

306 ) -> str: 

307 """ 

308 Get constants code that is common for all 'wait_until_*_equals' procedures. 

309 """ 

310 field_description = f" the '{field.name}' field in" if field else "" 

311 

312 return f"""\ 

313{self.reg_index_constant(register=register, register_array=register_array)}\ 

314{self.reg_address_constant()}\ 

315 

316{self.get_register_array_message(register_array=register_array)}\ 

317{self.get_base_address_message()}\ 

318 constant base_message : string := ( 

319 "Timeout while waiting for{field_description} the '{register.name}' register" 

320 & register_array_message 

321 & base_address_message 

322 & " to equal the given value: " 

323 & to_string(reg_value) 

324 & "." 

325 ); 

326{self.get_message()}\ 

327"""