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

76 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-28 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.register_mode import SoftwareAccessDirection 

16 

17# Local folder libraries 

18from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon 

19 

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 

25 

26 

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. 

31 

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

33 given natively-typed record value. 

34 

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

36 given natively-typed value. 

37 

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

39 

40 The generated VHDL file needs also the generated packages from 

41 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

42 """ 

43 

44 __version__ = "1.0.0" 

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(self, **kwargs: Any) -> Path: 

56 """ 

57 See super class for API details. 

58 

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) 

63 

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 

69 

70 vhdl = f"""\ 

71library ieee; 

72use ieee.fixed_pkg.all; 

73use ieee.std_logic_1164.all; 

74use ieee.numeric_std.all; 

75 

76library vunit_lib; 

77use vunit_lib.bus_master_pkg.bus_master_t; 

78use vunit_lib.bus_master_pkg.wait_until_read_equals; 

79use vunit_lib.com_types_pkg.max_timeout; 

80use vunit_lib.com_types_pkg.network_t; 

81use vunit_lib.string_ops.hex_image; 

82 

83library common; 

84use common.addr_pkg.addr_t; 

85use common.addr_pkg.addr_width; 

86 

87library register_file; 

88use register_file.register_file_pkg.register_t; 

89use register_file.register_operations_pkg.register_bus_master; 

90 

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

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

93 

94 

95package {package_name} is 

96 

97{self._declarations()}\ 

98end package; 

99 

100package body {package_name} is 

101 

102{self._implementations()}\ 

103end package body; 

104""" 

105 

106 return vhdl 

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: Optional["RegisterArray"] 

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 addr_t := (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: Optional["RegisterArray"], 

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 addr_t := (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 

220 implementations.append( 

221 self._register_wait_until_equals_implementation( 

222 register=register, register_array=register_array 

223 ) 

224 ) 

225 

226 for field in register.fields: 

227 implementations.append( 

228 self._field_wait_until_equals_implementation( 

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

230 ) 

231 ) 

232 

233 vhdl += separator 

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

235 vhdl += separator 

236 vhdl += "\n" 

237 

238 return vhdl 

239 

240 def _register_wait_until_equals_implementation( 

241 self, register: "Register", register_array: Optional["RegisterArray"] 

242 ) -> str: 

243 """ 

244 Get implementation for a 'wait_until_reg_equals' procedure. 

245 """ 

246 signature = self._register_wait_until_equals_signature( 

247 register=register, register_array=register_array 

248 ) 

249 

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

251 

252 return f"""\ 

253{signature} is 

254 constant reg_value : register_t := {conversion}; 

255 

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

257 begin 

258 wait_until_read_equals( 

259 net => net, 

260 bus_handle => bus_handle, 

261 addr => std_ulogic_vector(reg_address), 

262 value => reg_value, 

263 timeout => timeout, 

264 msg => get_message 

265 ); 

266 end procedure; 

267""" 

268 

269 def _field_wait_until_equals_implementation( 

270 self, 

271 register: "Register", 

272 register_array: Optional["RegisterArray"], 

273 field: "RegisterField", 

274 ) -> str: 

275 """ 

276 Get implementation for a 'wait_until_field_equals' procedure. 

277 """ 

278 signature = self._field_wait_until_equals_signature( 

279 register=register, register_array=register_array, field=field 

280 ) 

281 field_name = self.qualified_field_name( 

282 register=register, register_array=register_array, field=field 

283 ) 

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

285 

286 return f"""\ 

287{signature} is 

288 constant reg_value : register_t := ( 

289 {field_name} => {field_to_slv}, 

290 others => '-' 

291 ); 

292 

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

294 begin 

295 wait_until_read_equals( 

296 net => net, 

297 bus_handle => bus_handle, 

298 addr => std_ulogic_vector(reg_address), 

299 value => reg_value, 

300 timeout => timeout, 

301 msg => get_message 

302 ); 

303 end procedure; 

304""" 

305 

306 def _get_common_constants( 

307 self, 

308 register: "Register", 

309 register_array: Optional["RegisterArray"], 

310 field: Optional["RegisterField"], 

311 ) -> str: 

312 """ 

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

314 """ 

315 if field: 

316 field_description = f" the '{field.name}' field in" 

317 else: 

318 field_description = "" 

319 

320 return f"""\ 

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

322{self.reg_address_constant()}\ 

323 

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

325{self.get_base_address_message()}\ 

326 constant base_message : string := ( 

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

328 & register_array_message 

329 & base_address_message 

330 & " to equal the given value: " 

331 & to_string(reg_value) 

332 & "." 

333 ); 

334{self.get_message()}\ 

335"""