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

213 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-28 23:17 +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, ConstraintFile as OSVVM_ConstraintFile 

46from pyEDAA.OSVVM.Project import XDCConstraintFile, ScopeToRef as OSVVM_ScopeToRef, ScopeToCell as OSVVM_ScopeToCell 

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

48 

49 

50@export 

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

52 try: 

53 buildName = OSVVM_BuildName(name) 

54 optionID = osvvmContext.AddOption(buildName) 

55 except Exception as ex: # pragma: no cover 

56 osvvmContext.LastException = ex 

57 raise ex 

58 

59 return optionID 

60 

61@export 

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

63 """ 

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

65 

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

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

68 

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

70 

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

72 

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

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

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

76 

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

78 :param options: Optional list of option IDs. 

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

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

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

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

83 

84 .. seealso:: 

85 

86 * :func:`BuildName` 

87 * :func:`include` 

88 * :func:`ChangeWorkingDirectory` 

89 """ 

90 try: 

91 file = Path(file) 

92 buildName = None 

93 

94 # Preserve current directory 

95 currentDirectory = osvvmContext._currentDirectory 

96 

97 for optionID in options: 

98 try: 

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

100 except KeyError as e: # pragma: no cover 

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

102 ex.__cause__ = e 

103 osvvmContext.LastException = ex 

104 raise ex 

105 

106 if isinstance(option, OSVVM_BuildName): 

107 buildName = option.Name 

108 else: # pragma: no cover 

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

110 ex.__cause__ = TypeError() 

111 osvvmContext.LastException = ex 

112 raise ex 

113 

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

115 if buildName is None: 

116 buildName = file.stem 

117 

118 osvvmContext.BeginBuild(buildName) 

119 includeFile = osvvmContext.IncludeFile(file) 

120 osvvmContext.EvaluateFile(includeFile) 

121 osvvmContext.EndBuild() 

122 

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

124 osvvmContext._currentDirectory = currentDirectory 

125 

126 except Exception as ex: # pragma: no cover 

127 osvvmContext.LastException = ex 

128 raise ex 

129 

130 

131@export 

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

133 """ 

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

135 

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

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

138 

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

140 

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

142 

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

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

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

146 

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

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

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

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

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

152 

153 .. seealso:: 

154 

155 * :func:`build` 

156 * :func:`ChangeWorkingDirectory` 

157 """ 

158 try: 

159 # Preserve current directory 

160 currentDirectory = osvvmContext._currentDirectory 

161 

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

163 osvvmContext.EvaluateFile(includeFile) 

164 

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

166 osvvmContext._currentDirectory = currentDirectory 

167 

168 except Exception as ex: # pragma: no cover 

169 osvvmContext.LastException = ex 

170 raise ex 

171 

172 

173@export 

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

175 """ 

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

177 

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

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

180 

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

182 

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

184 

185 :param libraryName: Name of the VHDL library. 

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

187 

188 .. seealso:: 

189 

190 * :func:`LinkLibrary` 

191 * :func:`LinkLibraryDirectory` 

192 """ 

193 try: 

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

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

196 osvvmContext.LastException = ex 

197 raise ex 

198 

199 osvvmContext.SetLibrary(libraryName) 

200 

201 except Exception as ex: # pragma: no cover 

202 osvvmContext.LastException = ex 

203 raise ex 

204 

205 

206@export 

207def NoNullRangeWarning() -> int: 

208 try: 

209 option = OSVVM_NoNullRangeWarning() 

210 optionID = osvvmContext.AddOption(option) 

211 except Exception as ex: # pragma: no cover 

212 osvvmContext.LastException = ex 

213 raise ex 

214 

215 return optionID 

216 

217 

218@export 

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

220 try: 

221 file = Path(file) 

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

223 

224 noNullRangeWarning = None 

225 associatedConstraintFiles = [] 

226 for optionID in options: 

227 try: 

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

229 except KeyError as e: # pragma: no cover 

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

231 ex.__cause__ = e 

232 osvvmContext.LastException = ex 

233 raise ex 

234 

235 if isinstance(option, OSVVM_NoNullRangeWarning): 235 ↛ 236line 235 didn't jump to line 236 because the condition on line 235 was never true

236 noNullRangeWarning = True 

237 elif isinstance(option, OSVVM_ConstraintFile): 

238 associatedConstraintFiles.append(XDCConstraintFile( 

239 option.Path, 

240 option.ScopeToRef, 

241 option.ScopeToCell 

242 )) 

243 else: # pragma: no cover 

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

245 ex.__cause__ = TypeError() 

246 osvvmContext.LastException = ex 

247 raise ex 

248 

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

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

251 ex.__cause__ = FileNotFoundError(fullPath) 

252 osvvmContext.LastException = ex 

253 raise ex 

254 

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

256 vhdlFile = VHDLSourceFile( 

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

258 noNullRangeWarning=noNullRangeWarning, 

259 associatedFiles=associatedConstraintFiles 

260 ) 

261 osvvmContext.AddVHDLFile(vhdlFile) 

262 else: # pragma: no cover 

263 ex = OSVVMException(f"Path '{fullPath}' is no VHDL file (*.vhd, *.vhdl).") 

264 osvvmContext.LastException = ex 

265 raise ex 

266 

267 except Exception as ex: # pragma: no cover 

268 osvvmContext.LastException = ex 

269 raise ex 

270 

271 

272@export 

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

274 try: 

275 testcase = osvvmContext.SetTestcaseToplevel(toplevelName) 

276 for optionID in options: 

277 try: 

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

279 except KeyError as e: # pragma: no cover 

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

281 ex.__cause__ = e 

282 osvvmContext.LastException = ex 

283 raise ex 

284 

285 if isinstance(option, GenericValue): 

286 testcase.AddGeneric(option) 

287 else: # pragma: no cover 

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

289 ex.__cause__ = TypeError() 

290 osvvmContext.LastException = ex 

291 raise ex 

292 

293 except Exception as ex: # pragma: no cover 

294 osvvmContext.LastException = ex 

295 raise ex 

296 # osvvmContext._testcase = None 

297 

298 

299@export 

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

301 try: 

302 genericValue = GenericValue(name, value) 

303 optionID = osvvmContext.AddOption(genericValue) 

304 except Exception as ex: # pragma: no cover 

305 osvvmContext.LastException = ex 

306 raise ex 

307 

308 return optionID 

309 

310 

311@export 

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

313 try: 

314 osvvmContext.SetTestsuite(name) 

315 except Exception as ex: # pragma: no cover 

316 osvvmContext.LastException = ex 

317 raise ex 

318 

319 

320@export 

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

322 try: 

323 osvvmContext.AddTestcase(name) 

324 except Exception as ex: # pragma: no cover 

325 osvvmContext.LastException = ex 

326 raise ex 

327 

328 

329@export 

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

331 try: 

332 file = Path(file) 

333 testName = file.stem 

334 

335 # Analyze file 

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

337 

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

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

340 ex.__cause__ = FileNotFoundError(fullPath) 

341 osvvmContext.LastException = ex 

342 raise ex 

343 

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

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

346 osvvmContext.AddVHDLFile(vhdlFile) 

347 else: # pragma: no cover 

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

349 osvvmContext.LastException = ex 

350 raise ex 

351 

352 # Add testcase 

353 testcase = osvvmContext.AddTestcase(testName) 

354 testcase.SetToplevel(testName) 

355 for optionID in options: 

356 try: 

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

358 except KeyError as e: # pragma: no cover 

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

360 ex.__cause__ = e 

361 osvvmContext.LastException = ex 

362 raise ex 

363 

364 if isinstance(option, GenericValue): 

365 testcase.AddGeneric(option) 

366 else: # pragma: no cover 

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

368 ex.__cause__ = TypeError() 

369 osvvmContext.LastException = ex 

370 raise ex 

371 

372 except Exception as ex: # pragma: no cover 

373 osvvmContext.LastException = ex 

374 raise ex 

375 # osvvmContext._testcase = None 

376 

377 

378@export 

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

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

381 

382 

383@export 

384def LinkLibraryDirectory(libraryDirectory: str): 

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

386 

387 

388@export 

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

390 try: 

391 try: 

392 value = int(value) 

393 except ValueError as e: # pragma: no cover 

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

395 ex.__cause__ = e 

396 osvvmContext.LastException = ex 

397 raise ex 

398 

399 match value: 

400 case 1987: 

401 osvvmContext.VHDLVersion = VHDLVersion.VHDL87 

402 case 1993: 

403 osvvmContext.VHDLVersion = VHDLVersion.VHDL93 

404 case 2002: 

405 osvvmContext.VHDLVersion = VHDLVersion.VHDL2002 

406 case 2008: 

407 osvvmContext.VHDLVersion = VHDLVersion.VHDL2008 

408 case 2019: 

409 osvvmContext.VHDLVersion = VHDLVersion.VHDL2019 

410 case _: # pragma: no cover 

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

412 osvvmContext.LastException = ex 

413 raise ex 

414 

415 except Exception as ex: # pragma: no cover 

416 osvvmContext.LastException = ex 

417 raise ex 

418 

419@export 

420def GetVHDLVersion() -> int: 

421 try: 

422 if osvvmContext.VHDLVersion is VHDLVersion.VHDL87: 

423 return 1987 

424 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL93: 

425 return 1993 

426 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL2002: 

427 return 2002 

428 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL2008: 

429 return 2008 

430 elif osvvmContext.VHDLVersion is VHDLVersion.VHDL2019: 

431 return 2019 

432 else: # pragma: no cover 

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

434 osvvmContext.LastException = ex 

435 raise ex 

436 except Exception as ex: # pragma: no cover 

437 osvvmContext.LastException = ex 

438 raise ex 

439 

440 

441@export 

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

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

444 

445 

446@export 

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

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

449 

450 

451@export 

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

453 try: 

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

455 except Exception as ex: # pragma: no cover 

456 osvvmContext.LastException = ex 

457 raise ex 

458 

459@export 

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

461 try: 

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

463 except Exception as ex: # pragma: no cover 

464 osvvmContext.LastException = ex 

465 raise ex 

466 

467@export 

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

469 try: 

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

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

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

473 ex.__cause__ = NotADirectoryError(newDirectory) 

474 osvvmContext.LastException = ex 

475 raise ex 

476 except Exception as ex: # pragma: no cover 

477 osvvmContext.LastException = ex 

478 raise ex 

479 

480 

481@export 

482def FindOsvvmSettingsDirectory(*args): 

483 pass 

484 

485 

486@export 

487def CreateOsvvmScriptSettingsPkg(*args): 

488 pass 

489 

490 

491@export 

492def noop(*args): 

493 pass 

494 

495 

496@export 

497def ConstraintFile(file: str, *options: int) -> int: 

498 try: 

499 file = Path(file) 

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

501 

502 properties = {} 

503 for optionID in options: 

504 try: 

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

506 except KeyError as e: # pragma: no cover 

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

508 ex.__cause__ = e 

509 osvvmContext.LastException = ex 

510 raise ex 

511 

512 if isinstance(option, OSVVM_ScopeToRef): 

513 properties["scopeToRef"] = option.Reference 

514 elif isinstance(option, OSVVM_ScopeToCell): 

515 properties["scopeToCell"] = option.Cell 

516 else: # pragma: no cover 

517 ex = OSVVMException(f"Option {optionID} is not a ScopeToRef or ScopeToCell.") 

518 ex.__cause__ = TypeError() 

519 osvvmContext.LastException = ex 

520 raise ex 

521 

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

523 ex = OSVVMException(f"Constraint file '{fullPath}' can't be found.") 

524 ex.__cause__ = FileNotFoundError(fullPath) 

525 osvvmContext.LastException = ex 

526 raise ex 

527 

528 if fullPath.suffix in (".sdc", ".xdc"): 

529 try: 

530 constraint = OSVVM_ConstraintFile(Path(file), **properties) 

531 optionID = osvvmContext.AddOption(constraint) 

532 except Exception as ex: # pragma: no cover 

533 osvvmContext.LastException = ex 

534 raise ex 

535 

536 return optionID 

537 else: # pragma: no cover 

538 ex = OSVVMException(f"Path '{fullPath}' is no constraint file (*.sdc, *.xdc).") 

539 osvvmContext.LastException = ex 

540 raise ex 

541 

542 except Exception as ex: # pragma: no cover 

543 osvvmContext.LastException = ex 

544 raise ex 

545 

546 

547@export 

548def ScopeToRef(refName: str) -> int: 

549 try: 

550 if refName == "": 550 ↛ 551line 550 didn't jump to line 551 because the condition on line 550 was never true

551 ex = OSVVMException(f"Parameter 'refName' is empty.") 

552 ex.__cause__ = ValueError("Parameter 'refName' is a empty string.") 

553 osvvmContext.LastException = ex 

554 raise ex 

555 

556 try: 

557 ref = OSVVM_ScopeToRef(refName) 

558 optionID = osvvmContext.AddOption(ref) 

559 except Exception as ex: # pragma: no cover 

560 osvvmContext.LastException = ex 

561 raise ex 

562 

563 return optionID 

564 except Exception as ex: # pragma: no cover 

565 osvvmContext.LastException = ex 

566 raise ex 

567 

568 

569@export 

570def ScopeToCell(cellName: str) -> int: 

571 try: 

572 if cellName == "": 572 ↛ 573line 572 didn't jump to line 573 because the condition on line 572 was never true

573 ex = OSVVMException(f"Parameter 'cellName' is empty.") 

574 ex.__cause__ = ValueError("Parameter 'cellName' is a empty string.") 

575 osvvmContext.LastException = ex 

576 raise ex 

577 

578 try: 

579 ref = OSVVM_ScopeToCell(cellName) 

580 optionID = osvvmContext.AddOption(ref) 

581 except Exception as ex: # pragma: no cover 

582 osvvmContext.LastException = ex 

583 raise ex 

584 

585 return optionID 

586 except Exception as ex: # pragma: no cover 

587 osvvmContext.LastException = ex 

588 raise ex