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
« 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.
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
41from pyTooling.Decorators import export
42from pyVHDLModel import VHDLVersion
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
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
59 return optionID
61@export
62def build(file: str, *options: int) -> None:
63 """
64 This function implements the behavior of OSVVM's ``build`` procedure.
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.
69 The referenced file gets appended to a list of included files maintained by the context.
71 :underline:`pro-file discovery algorithm:`
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.
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.
84 .. seealso::
86 * :func:`BuildName`
87 * :func:`include`
88 * :func:`ChangeWorkingDirectory`
89 """
90 try:
91 file = Path(file)
92 buildName = None
94 # Preserve current directory
95 currentDirectory = osvvmContext._currentDirectory
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
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
114 # If no build name was specified, derive a name from *.pro file.
115 if buildName is None:
116 buildName = file.stem
118 osvvmContext.BeginBuild(buildName)
119 includeFile = osvvmContext.IncludeFile(file)
120 osvvmContext.EvaluateFile(includeFile)
121 osvvmContext.EndBuild()
123 # Restore current directory after recursively evaluating *.pro files.
124 osvvmContext._currentDirectory = currentDirectory
126 except Exception as ex: # pragma: no cover
127 osvvmContext.LastException = ex
128 raise ex
131@export
132def include(file: str) -> None:
133 """
134 This function implements the behavior of OSVVM's ``include`` procedure.
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.
139 The referenced file gets appended to a list of included files maintained by the context.
141 :underline:`pro-file discovery algorithm:`
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.
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.
153 .. seealso::
155 * :func:`build`
156 * :func:`ChangeWorkingDirectory`
157 """
158 try:
159 # Preserve current directory
160 currentDirectory = osvvmContext._currentDirectory
162 includeFile = osvvmContext.IncludeFile(Path(file))
163 osvvmContext.EvaluateFile(includeFile)
165 # Restore current directory after recursively evaluating *.pro files.
166 osvvmContext._currentDirectory = currentDirectory
168 except Exception as ex: # pragma: no cover
169 osvvmContext.LastException = ex
170 raise ex
173@export
174def library(libraryName: str, libraryPath: Nullable[str] = None) -> None:
175 """
176 This function implements the behavior of OSVVM's ``library`` procedure.
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.
181 .. hint:: All following ``analyze`` calls will use this library as the VHDL file's VHDL library.
183 .. caution:: Parameter `libraryPath` is not yet implemented.
185 :param libraryName: Name of the VHDL library.
186 :param libraryPath: Optional: Path where to create that VHDL library.
188 .. seealso::
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
199 osvvmContext.SetLibrary(libraryName)
201 except Exception as ex: # pragma: no cover
202 osvvmContext.LastException = ex
203 raise ex
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
215 return optionID
218@export
219def analyze(file: str, *options: int) -> None:
220 try:
221 file = Path(file)
222 fullPath = (osvvmContext._currentDirectory / file).resolve()
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
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
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
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
267 except Exception as ex: # pragma: no cover
268 osvvmContext.LastException = ex
269 raise ex
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
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
293 except Exception as ex: # pragma: no cover
294 osvvmContext.LastException = ex
295 raise ex
296 # osvvmContext._testcase = None
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
308 return optionID
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
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
329@export
330def RunTest(file: str, *options: int) -> None:
331 try:
332 file = Path(file)
333 testName = file.stem
335 # Analyze file
336 fullPath = (osvvmContext._currentDirectory / file).resolve()
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
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
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
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
372 except Exception as ex: # pragma: no cover
373 osvvmContext.LastException = ex
374 raise ex
375 # osvvmContext._testcase = None
378@export
379def LinkLibrary(libraryName: str, libraryPath: Nullable[str] = None):
380 print(f"[LinkLibrary] {libraryPath}")
383@export
384def LinkLibraryDirectory(libraryDirectory: str):
385 print(f"[LinkLibraryDirectory] {libraryDirectory}")
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
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
415 except Exception as ex: # pragma: no cover
416 osvvmContext.LastException = ex
417 raise ex
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
441@export
442def SetCoverageAnalyzeEnable(value: bool) -> None:
443 print(f"[SetCoverageAnalyzeEnable] {value}:{value.__class__.__name__}")
446@export
447def SetCoverageSimulateEnable(value: bool) -> None:
448 print(f"[SetCoverageSimulateEnable] {value}")
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
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
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
481@export
482def FindOsvvmSettingsDirectory(*args):
483 pass
486@export
487def CreateOsvvmScriptSettingsPkg(*args):
488 pass
491@export
492def noop(*args):
493 pass
496@export
497def ConstraintFile(file: str, *options: int) -> int:
498 try:
499 file = Path(file)
500 fullPath = (osvvmContext._currentDirectory / file).resolve()
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
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
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
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
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
542 except Exception as ex: # pragma: no cover
543 osvvmContext.LastException = ex
544 raise ex
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
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
563 return optionID
564 except Exception as ex: # pragma: no cover
565 osvvmContext.LastException = ex
566 raise ex
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
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
585 return optionID
586 except Exception as ex: # pragma: no cover
587 osvvmContext.LastException = ex
588 raise ex