Coverage for pyEDAA/OSVVM/Project/Procedures.py: 90%

167 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-27 22:24 +0000

1# ==================================================================================================================== # 

2# _____ ____ _ _ ___ ______ ____ ____ __ # 

3# _ __ _ _| ____| _ \ / \ / \ / _ \/ ___\ \ / /\ \ / / \/ | # 

4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | \___ \\ \ / / \ \ / /| |\/| | # 

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| |___) |\ V / \ V / | | | | # 

6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/|____/ \_/ \_/ |_| |_| # 

7# |_| |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2025-2025 Patrick Lehmann - Boetzingen, Germany # 

15# # 

16# Licensed under the Apache License, Version 2.0 (the "License"); # 

17# you may not use this file except in compliance with the License. # 

18# You may obtain a copy of the License at # 

19# # 

20# http://www.apache.org/licenses/LICENSE-2.0 # 

21# # 

22# Unless required by applicable law or agreed to in writing, software # 

23# distributed under the License is distributed on an "AS IS" BASIS, # 

24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

25# See the License for the specific language governing permissions and # 

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31""" 

32This module implements OSVVM's TCL procedures (used in OSVVM's ``*.pro`` files) as Python functions. 

33 

34These functions are then registered at the :class:`TCL processor <pyEDAA.OSVVM.TCL.OsvvmProFileProcessor>`, so procedure 

35calls within TCL code get "redirected" to these Python functions. Each function has access to a global variable 

36:data:`~pyEDAA.OSVVM.Environment.osvvmContext` to preserve its state or modify the context. 

37""" 

38from pathlib import Path 

39from typing import Optional as Nullable 

40 

41from pyTooling.Decorators import export 

42from pyVHDLModel import VHDLVersion 

43 

44from pyEDAA.OSVVM import OSVVMException 

45from pyEDAA.OSVVM.Project import osvvmContext, VHDLSourceFile, GenericValue 

46from pyEDAA.OSVVM.Project import BuildName as OSVVM_BuildName, NoNullRangeWarning as OSVVM_NoNullRangeWarning 

47 

48 

49@export 

50def BuildName(name: str) -> int: 

51 try: 

52 buildName = OSVVM_BuildName(name) 

53 optionID = osvvmContext.AddOption(buildName) 

54 except Exception as ex: # pragma: no cover 

55 osvvmContext.LastException = ex 

56 raise ex 

57 

58 return optionID 

59 

60@export 

61def build(file: str, *options: int) -> None: 

62 """ 

63 This function implements the behavior of OSVVM's ``build`` procedure. 

64 

65 The current directory of the currently active context is preserved while the referenced ``*.pro`` file is processed. 

66 After processing that file, the context's current directory is restored. 

67 

68 The referenced file gets appended to a list of included files maintained by the context. 

69 

70 :underline:`pro-file discovery algorithm:` 

71 

72 1. If the path explicitly references a ``*.pro`` file, this file is used. 

73 2. If the path references a directory, it checks implicitly for a ``build.pro`` file, otherwise 

74 3. it checks implicitly for a ``<path>.pro`` file, named like the directories name. 

75 

76 :param file: Explicit path to a ``*.pro`` file or a directory containing an implicitly searched ``*.pro`` file. 

77 :param options: Optional list of option IDs. 

78 :raises TypeError: If parameter proFileOrBuildDirectory is not a Path. 

79 :raises OSVVMException: If parameter proFileOrBuildDirectory is an absolute path. 

80 :raises OSVVMException: If parameter proFileOrBuildDirectory is not a *.pro file or build directory. 

81 :raises OSVVMException: If a TclError was caught while processing a *.pro file. 

82 

83 .. seealso:: 

84 

85 * :func:`BuildName` 

86 * :func:`include` 

87 * :func:`ChangeWorkingDirectory` 

88 """ 

89 try: 

90 file = Path(file) 

91 buildName = None 

92 

93 # Preserve current directory 

94 currentDirectory = osvvmContext._currentDirectory 

95 

96 for optionID in options: 

97 try: 

98 option = osvvmContext._options[int(optionID)] 

99 except KeyError as e: # pragma: no cover 

100 ex = OSVVMException(f"Option {optionID} not found in option dictionary.") 

101 ex.__cause__ = e 

102 osvvmContext.LastException = ex 

103 raise ex 

104 

105 if isinstance(option, OSVVM_BuildName): 

106 buildName = option.Name 

107 else: # pragma: no cover 

108 ex = OSVVMException(f"Option {optionID} is not a BuildName.") 

109 ex.__cause__ = TypeError() 

110 osvvmContext.LastException = ex 

111 raise ex 

112 

113 # If no build name was specified, derive a name from *.pro file. 

114 if buildName is None: 

115 buildName = file.stem 

116 

117 osvvmContext.BeginBuild(buildName) 

118 includeFile = osvvmContext.IncludeFile(file) 

119 osvvmContext.EvaluateFile(includeFile) 

120 osvvmContext.EndBuild() 

121 

122 # Restore current directory after recursively evaluating *.pro files. 

123 osvvmContext._currentDirectory = currentDirectory 

124 

125 except Exception as ex: # pragma: no cover 

126 osvvmContext.LastException = ex 

127 raise ex 

128 

129 

130@export 

131def include(file: str) -> None: 

132 """ 

133 This function implements the behavior of OSVVM's ``include`` procedure. 

134 

135 The current directory of the currently active context is preserved while the referenced ``*.pro`` file is processed. 

136 After processing that file, the context's current directory is restored. 

137 

138 The referenced file gets appended to a list of included files maintained by the context. 

139 

140 :underline:`pro-file discovery algorithm:` 

141 

142 1. If the path explicitly references a ``*.pro`` file, this file is used. 

143 2. If the path references a directory, it checks implicitly for a ``build.pro`` file, otherwise 

144 3. it checks implicitly for a ``<path>.pro`` file, named like the directories name. 

145 

146 :param file: Explicit path to a ``*.pro`` file or a directory containing an implicitly searched ``*.pro`` file. 

147 :raises TypeError: If parameter proFileOrBuildDirectory is not a Path. 

148 :raises OSVVMException: If parameter proFileOrBuildDirectory is an absolute path. 

149 :raises OSVVMException: If parameter proFileOrBuildDirectory is not a *.pro file or build directory. 

150 :raises OSVVMException: If a TclError was caught while processing a *.pro file. 

151 

152 .. seealso:: 

153 

154 * :func:`build` 

155 * :func:`ChangeWorkingDirectory` 

156 """ 

157 try: 

158 # Preserve current directory 

159 currentDirectory = osvvmContext._currentDirectory 

160 

161 includeFile = osvvmContext.IncludeFile(Path(file)) 

162 osvvmContext.EvaluateFile(includeFile) 

163 

164 # Restore current directory after recursively evaluating *.pro files. 

165 osvvmContext._currentDirectory = currentDirectory 

166 

167 except Exception as ex: # pragma: no cover 

168 osvvmContext.LastException = ex 

169 raise ex 

170 

171 

172@export 

173def library(libraryName: str, libraryPath: Nullable[str] = None) -> None: 

174 """ 

175 This function implements the behavior of OSVVM's ``library`` procedure. 

176 

177 It sets the currently active VHDL library to the specified VHDL library. If no VHDL library with that name exist, a 

178 new VHDL library is created and set as active VHDL library. 

179 

180 .. hint:: All following ``analyze`` calls will use this library as the VHDL file's VHDL library. 

181 

182 .. caution:: Parameter `libraryPath` is not yet implemented. 

183 

184 :param libraryName: Name of the VHDL library. 

185 :param libraryPath: Optional: Path where to create that VHDL library. 

186 

187 .. seealso:: 

188 

189 * :func:`LinkLibrary` 

190 * :func:`LinkLibraryDirectory` 

191 """ 

192 try: 

193 if libraryPath is not None: 193 ↛ 194line 193 didn't jump to line 194 because the condition on line 193 was never true

194 ex = NotImplementedError(f"Optional parameter 'libraryPath' not yet supported.") 

195 osvvmContext.LastException = ex 

196 raise ex 

197 

198 osvvmContext.SetLibrary(libraryName) 

199 

200 except Exception as ex: # pragma: no cover 

201 osvvmContext.LastException = ex 

202 raise ex 

203 

204 

205@export 

206def NoNullRangeWarning() -> int: 

207 try: 

208 option = OSVVM_NoNullRangeWarning() 

209 optionID = osvvmContext.AddOption(option) 

210 except Exception as ex: # pragma: no cover 

211 osvvmContext.LastException = ex 

212 raise ex 

213 

214 return optionID 

215 

216 

217@export 

218def analyze(file: str, *options: int) -> None: 

219 try: 

220 file = Path(file) 

221 fullPath = (osvvmContext._currentDirectory / file).resolve() 

222 

223 noNullRangeWarning = None 

224 for optionID in options: 

225 try: 

226 option = osvvmContext._options[int(optionID)] 

227 except KeyError as e: # pragma: no cover 

228 ex = OSVVMException(f"Option {optionID} not found in option dictionary.") 

229 ex.__cause__ = e 

230 osvvmContext.LastException = ex 

231 raise ex 

232 

233 if isinstance(option, OSVVM_NoNullRangeWarning): 

234 noNullRangeWarning = True 

235 else: # pragma: no cover 

236 ex = OSVVMException(f"Option {optionID} is not a NoNullRangeWarning.") 

237 ex.__cause__ = TypeError() 

238 osvvmContext.LastException = ex 

239 raise ex 

240 

241 if not fullPath.exists(): # pragma: no cover 

242 ex = OSVVMException(f"Path '{fullPath}' can't be analyzed.") 

243 ex.__cause__ = FileNotFoundError(fullPath) 

244 osvvmContext.LastException = ex 

245 raise ex 

246 

247 if fullPath.suffix in (".vhd", ".vhdl"): 

248 vhdlFile = VHDLSourceFile( 

249 fullPath.relative_to(osvvmContext._workingDirectory, walk_up=True), 

250 noNullRangeWarning=noNullRangeWarning 

251 ) 

252 osvvmContext.AddVHDLFile(vhdlFile) 

253 else: # pragma: no cover 

254 ex = OSVVMException(f"Path '{fullPath}' is no VHDL file.") 

255 osvvmContext.LastException = ex 

256 raise ex 

257 

258 except Exception as ex: # pragma: no cover 

259 osvvmContext.LastException = ex 

260 raise ex 

261 

262 

263@export 

264def simulate(toplevelName: str, *options: int) -> None: 

265 try: 

266 testcase = osvvmContext.SetTestcaseToplevel(toplevelName) 

267 for optionID in options: 

268 try: 

269 option = osvvmContext._options[int(optionID)] 

270 except KeyError as e: # pragma: no cover 

271 ex = OSVVMException(f"Option {optionID} not found in option dictionary.") 

272 ex.__cause__ = e 

273 osvvmContext.LastException = ex 

274 raise ex 

275 

276 if isinstance(option, GenericValue): 

277 testcase.AddGeneric(option) 

278 else: # pragma: no cover 

279 ex = OSVVMException(f"Option {optionID} is not a GenericValue.") 

280 ex.__cause__ = TypeError() 

281 osvvmContext.LastException = ex 

282 raise ex 

283 

284 except Exception as ex: # pragma: no cover 

285 osvvmContext.LastException = ex 

286 raise ex 

287 # osvvmContext._testcase = None 

288 

289 

290@export 

291def generic(name: str, value: str) -> int: 

292 try: 

293 genericValue = GenericValue(name, value) 

294 optionID = osvvmContext.AddOption(genericValue) 

295 except Exception as ex: # pragma: no cover 

296 osvvmContext.LastException = ex 

297 raise ex 

298 

299 return optionID 

300 

301 

302@export 

303def TestSuite(name: str) -> None: 

304 try: 

305 osvvmContext.SetTestsuite(name) 

306 except Exception as ex: # pragma: no cover 

307 osvvmContext.LastException = ex 

308 raise ex 

309 

310 

311@export 

312def TestName(name: str) -> None: 

313 try: 

314 osvvmContext.AddTestcase(name) 

315 except Exception as ex: # pragma: no cover 

316 osvvmContext.LastException = ex 

317 raise ex 

318 

319 

320@export 

321def RunTest(file: str, *options: int) -> None: 

322 try: 

323 file = Path(file) 

324 testName = file.stem 

325 

326 # Analyze file 

327 fullPath = (osvvmContext._currentDirectory / file).resolve() 

328 

329 if not fullPath.exists(): # pragma: no cover 

330 ex = OSVVMException(f"Path '{fullPath}' can't be analyzed.") 

331 ex.__cause__ = FileNotFoundError(fullPath) 

332 osvvmContext.LastException = ex 

333 raise ex 

334 

335 if fullPath.suffix in (".vhd", ".vhdl"): 

336 vhdlFile = VHDLSourceFile(fullPath.relative_to(osvvmContext._workingDirectory, walk_up=True)) 

337 osvvmContext.AddVHDLFile(vhdlFile) 

338 else: # pragma: no cover 

339 ex = OSVVMException(f"Path '{fullPath}' is no VHDL file.") 

340 osvvmContext.LastException = ex 

341 raise ex 

342 

343 # Add testcase 

344 testcase = osvvmContext.AddTestcase(testName) 

345 testcase.SetToplevel(testName) 

346 for optionID in options: 

347 try: 

348 option = osvvmContext._options[int(optionID)] 

349 except KeyError as e: # pragma: no cover 

350 ex = OSVVMException(f"Option {optionID} not found in option dictionary.") 

351 ex.__cause__ = e 

352 osvvmContext.LastException = ex 

353 raise ex 

354 

355 if isinstance(option, GenericValue): 

356 testcase.AddGeneric(option) 

357 else: # pragma: no cover 

358 ex = OSVVMException(f"Option {optionID} is not a GenericValue.") 

359 ex.__cause__ = TypeError() 

360 osvvmContext.LastException = ex 

361 raise ex 

362 

363 except Exception as ex: # pragma: no cover 

364 osvvmContext.LastException = ex 

365 raise ex 

366 # osvvmContext._testcase = None 

367 

368 

369@export 

370def LinkLibrary(libraryName: str, libraryPath: Nullable[str] = None): 

371 print(f"[LinkLibrary] {libraryPath}") 

372 

373 

374@export 

375def LinkLibraryDirectory(libraryDirectory: str): 

376 print(f"[LinkLibraryDirectory] {libraryDirectory}") 

377 

378 

379@export 

380def SetVHDLVersion(value: str) -> None: 

381 try: 

382 try: 

383 value = int(value) 

384 except ValueError as e: # pragma: no cover 

385 ex = OSVVMException(f"Unsupported VHDL version '{value}'.") 

386 ex.__cause__ = e 

387 osvvmContext.LastException = ex 

388 raise ex 

389 

390 match value: 

391 case 1987: 

392 osvvmContext.VHDLVersion = VHDLVersion.VHDL87 

393 case 1993: 

394 osvvmContext.VHDLVersion = VHDLVersion.VHDL93 

395 case 2002: 

396 osvvmContext.VHDLVersion = VHDLVersion.VHDL2002 

397 case 2008: 

398 osvvmContext.VHDLVersion = VHDLVersion.VHDL2008 

399 case 2019: 

400 osvvmContext.VHDLVersion = VHDLVersion.VHDL2019 

401 case _: # pragma: no cover 

402 ex = OSVVMException(f"Unsupported VHDL version '{value}'.") 

403 osvvmContext.LastException = ex 

404 raise ex 

405 

406 except Exception as ex: # pragma: no cover 

407 osvvmContext.LastException = ex 

408 raise ex 

409 

410@export 

411def GetVHDLVersion() -> int: 

412 try: 

413 if osvvmContext.VHDLVersion is VHDLVersion.VHDL87: 

414 return 1987 

415 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL93: 

416 return 1993 

417 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL2002: 

418 return 2002 

419 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL2008: 

420 return 2008 

421 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL2019: 

422 return 2019 

423 else: # pragma: no cover 

424 ex = OSVVMException(f"Unsupported VHDL version '{osvvmContext.VHDLVersion}'.") 

425 osvvmContext.LastException = ex 

426 raise ex 

427 except Exception as ex: # pragma: no cover 

428 osvvmContext.LastException = ex 

429 raise ex 

430 

431 

432@export 

433def SetCoverageAnalyzeEnable(value: bool) -> None: 

434 print(f"[SetCoverageAnalyzeEnable] {value}:{value.__class__.__name__}") 

435 

436 

437@export 

438def SetCoverageSimulateEnable(value: bool) -> None: 

439 print(f"[SetCoverageSimulateEnable] {value}") 

440 

441 

442@export 

443def FileExists(file: str) -> bool: 

444 try: 

445 return (osvvmContext._currentDirectory / file).is_file() 

446 except Exception as ex: # pragma: no cover 

447 osvvmContext.LastException = ex 

448 raise ex 

449 

450@export 

451def DirectoryExists(directory: str) -> bool: 

452 try: 

453 return (osvvmContext._currentDirectory / directory).is_dir() 

454 except Exception as ex: # pragma: no cover 

455 osvvmContext.LastException = ex 

456 raise ex 

457 

458@export 

459def ChangeWorkingDirectory(directory: str) -> None: 

460 try: 

461 osvvmContext._currentDirectory = (newDirectory := osvvmContext._currentDirectory / directory) 

462 if not newDirectory.is_dir(): # pragma: no cover 

463 ex = OSVVMException(f"Directory '{newDirectory}' doesn't exist.") 

464 ex.__cause__ = NotADirectoryError(newDirectory) 

465 osvvmContext.LastException = ex 

466 raise ex 

467 except Exception as ex: # pragma: no cover 

468 osvvmContext.LastException = ex 

469 raise ex 

470 

471 

472@export 

473def FindOsvvmSettingsDirectory(*args): 

474 pass 

475 

476 

477@export 

478def CreateOsvvmScriptSettingsPkg(*args): 

479 pass 

480 

481 

482@export 

483def noop(*args): 

484 pass