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

72 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-15 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# -------------------------------------------------------------------------------------------------- 

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

42 

43 __version__ = "1.0.0" 

44 

45 SHORT_DESCRIPTION = "VHDL simulation wait until package" 

46 

47 @property 

48 def output_file(self) -> Path: 

49 """ 

50 Result will be placed in this file. 

51 """ 

52 return self.output_folder / f"{self.name}_register_wait_until_pkg.vhd" 

53 

54 def create( 

55 self, 

56 **kwargs: Any, # noqa: ANN401 

57 ) -> Path: 

58 """ 

59 See super class for API details. 

60 

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

62 actually has any registers. 

63 """ 

64 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

65 

66 def get_code( 

67 self, 

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

69 ) -> str: 

70 """ 

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

72 """ 

73 package_name = self.output_file.stem 

74 

75 return f"""\ 

76library ieee; 

77use ieee.fixed_pkg.all; 

78use ieee.std_logic_1164.all; 

79use ieee.numeric_std.all; 

80 

81library vunit_lib; 

82use vunit_lib.bus_master_pkg.bus_master_t; 

83use vunit_lib.bus_master_pkg.wait_until_read_equals; 

84use vunit_lib.com_types_pkg.max_timeout; 

85use vunit_lib.com_types_pkg.network_t; 

86use vunit_lib.string_ops.hex_image; 

87 

88library common; 

89use common.addr_pkg.addr_t; 

90use common.addr_pkg.addr_width; 

91 

92library register_file; 

93use register_file.register_file_pkg.register_t; 

94use register_file.register_operations_pkg.register_bus_master; 

95 

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

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

98 

99 

100package {package_name} is 

101 

102{self._declarations()}\ 

103end package; 

104 

105package body {package_name} is 

106 

107{self._implementations()}\ 

108end package body; 

109""" 

110 

111 def _declarations(self) -> str: 

112 """ 

113 Get procedure declarations for all procedures. 

114 """ 

115 separator = self.get_separator_line(indent=2) 

116 vhdl = "" 

117 

118 for register, register_array in self.iterate_software_accessible_registers( 

119 direction=SoftwareAccessDirection.READ 

120 ): 

121 declarations = [] 

122 

123 signature = self._register_wait_until_equals_signature( 

124 register=register, register_array=register_array 

125 ) 

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

127 

128 for field in register.fields: 

129 signature = self._field_wait_until_equals_signature( 

130 register=register, register_array=register_array, field=field 

131 ) 

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

133 

134 vhdl += separator 

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

136 vhdl += separator 

137 vhdl += "\n" 

138 

139 return vhdl 

140 

141 def _register_wait_until_equals_signature( 

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

143 ) -> str: 

144 """ 

145 Get signature for a 'wait_until_reg_equals' procedure. 

146 """ 

147 register_name = self.qualified_register_name( 

148 register=register, register_array=register_array 

149 ) 

150 register_description = self.register_description( 

151 register=register, register_array=register_array 

152 ) 

153 

154 if register.fields: 

155 value_type = f"{register_name}_t" 

156 slv_comment = "" 

157 else: 

158 value_type = "register_t" 

159 slv_comment = ( 

160 " -- Note that '-' can be used as a wildcard in 'value' since 'check_match' is \n" 

161 " -- used to check for equality.\n" 

162 ) 

163 

164 return f"""\ 

165 -- Wait until the {register_description} equals the given 'value'. 

166{slv_comment}\ 

167 procedure wait_until_{register_name}_equals( 

168 signal net : inout network_t; 

169{self.get_array_index_port(register_array=register_array)}\ 

170 value : in {value_type}; 

171 base_address : in addr_t := (others => '0'); 

172 bus_handle : in bus_master_t := register_bus_master; 

173 timeout : delay_length := max_timeout; 

174 message : string := "" 

175 )\ 

176""" 

177 

178 def _field_wait_until_equals_signature( 

179 self, 

180 register: Register, 

181 register_array: RegisterArray | None, 

182 field: RegisterField, 

183 ) -> str: 

184 """ 

185 Get signature for a 'wait_until_field_equals' procedure. 

186 """ 

187 field_name = self.qualified_field_name( 

188 register=register, register_array=register_array, field=field 

189 ) 

190 field_description = self.field_description( 

191 register=register, register_array=register_array, field=field 

192 ) 

193 

194 value_type = self.field_type_name( 

195 register=register, register_array=register_array, field=field 

196 ) 

197 

198 return f"""\ 

199 -- Wait until the {field_description} equals the given 'value'. 

200 procedure wait_until_{field_name}_equals( 

201 signal net : inout network_t; 

202{self.get_array_index_port(register_array=register_array)}\ 

203 value : in {value_type}; 

204 base_address : in addr_t := (others => '0'); 

205 bus_handle : in bus_master_t := register_bus_master; 

206 timeout : delay_length := max_timeout; 

207 message : string := "" 

208 )\ 

209""" 

210 

211 def _implementations(self) -> str: 

212 """ 

213 Get implementations of all procedures. 

214 """ 

215 separator = self.get_separator_line(indent=2) 

216 vhdl = "" 

217 

218 for register, register_array in self.iterate_software_accessible_registers( 

219 direction=SoftwareAccessDirection.READ 

220 ): 

221 implementations = [ 

222 self._register_wait_until_equals_implementation( 

223 register=register, register_array=register_array 

224 ) 

225 ] 

226 

227 implementations.extend( 

228 self._field_wait_until_equals_implementation( 

229 register=register, register_array=register_array, field=field 

230 ) 

231 for field in register.fields 

232 ) 

233 

234 vhdl += separator 

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

236 vhdl += separator 

237 vhdl += "\n" 

238 

239 return vhdl 

240 

241 def _register_wait_until_equals_implementation( 

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

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 ) 

250 

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

252 

253 return f"""\ 

254{signature} is 

255 constant reg_value : register_t := {conversion}; 

256 

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

269 

270 def _field_wait_until_equals_implementation( 

271 self, 

272 register: Register, 

273 register_array: RegisterArray | None, 

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

286 

287 return f"""\ 

288{signature} is 

289 constant reg_value : register_t := ( 

290 {field_name} => {field_to_slv}, 

291 others => '-' 

292 ); 

293 

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

306 

307 def _get_common_constants( 

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

309 ) -> str: 

310 """ 

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

312 """ 

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

314 

315 return f"""\ 

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

317{self.reg_address_constant()}\ 

318 

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

320{self.get_base_address_message()}\ 

321 constant base_message : string := ( 

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

323 & register_array_message 

324 & base_address_message 

325 & " to equal the given value: " 

326 & to_string(reg_value) 

327 & "." 

328 ); 

329{self.get_message()}\ 

330"""