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
« 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.
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
46from pyEDAA.OSVVM.Project import BuildName as OSVVM_BuildName, NoNullRangeWarning as OSVVM_NoNullRangeWarning
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
58 return optionID
60@export
61def build(file: str, *options: int) -> None:
62 """
63 This function implements the behavior of OSVVM's ``build`` procedure.
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.
68 The referenced file gets appended to a list of included files maintained by the context.
70 :underline:`pro-file discovery algorithm:`
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.
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.
83 .. seealso::
85 * :func:`BuildName`
86 * :func:`include`
87 * :func:`ChangeWorkingDirectory`
88 """
89 try:
90 file = Path(file)
91 buildName = None
93 # Preserve current directory
94 currentDirectory = osvvmContext._currentDirectory
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
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
113 # If no build name was specified, derive a name from *.pro file.
114 if buildName is None:
115 buildName = file.stem
117 osvvmContext.BeginBuild(buildName)
118 includeFile = osvvmContext.IncludeFile(file)
119 osvvmContext.EvaluateFile(includeFile)
120 osvvmContext.EndBuild()
122 # Restore current directory after recursively evaluating *.pro files.
123 osvvmContext._currentDirectory = currentDirectory
125 except Exception as ex: # pragma: no cover
126 osvvmContext.LastException = ex
127 raise ex
130@export
131def include(file: str) -> None:
132 """
133 This function implements the behavior of OSVVM's ``include`` procedure.
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.
138 The referenced file gets appended to a list of included files maintained by the context.
140 :underline:`pro-file discovery algorithm:`
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.
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.
152 .. seealso::
154 * :func:`build`
155 * :func:`ChangeWorkingDirectory`
156 """
157 try:
158 # Preserve current directory
159 currentDirectory = osvvmContext._currentDirectory
161 includeFile = osvvmContext.IncludeFile(Path(file))
162 osvvmContext.EvaluateFile(includeFile)
164 # Restore current directory after recursively evaluating *.pro files.
165 osvvmContext._currentDirectory = currentDirectory
167 except Exception as ex: # pragma: no cover
168 osvvmContext.LastException = ex
169 raise ex
172@export
173def library(libraryName: str, libraryPath: Nullable[str] = None) -> None:
174 """
175 This function implements the behavior of OSVVM's ``library`` procedure.
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.
180 .. hint:: All following ``analyze`` calls will use this library as the VHDL file's VHDL library.
182 .. caution:: Parameter `libraryPath` is not yet implemented.
184 :param libraryName: Name of the VHDL library.
185 :param libraryPath: Optional: Path where to create that VHDL library.
187 .. seealso::
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
198 osvvmContext.SetLibrary(libraryName)
200 except Exception as ex: # pragma: no cover
201 osvvmContext.LastException = ex
202 raise ex
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
214 return optionID
217@export
218def analyze(file: str, *options: int) -> None:
219 try:
220 file = Path(file)
221 fullPath = (osvvmContext._currentDirectory / file).resolve()
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
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
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
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
258 except Exception as ex: # pragma: no cover
259 osvvmContext.LastException = ex
260 raise ex
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
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
284 except Exception as ex: # pragma: no cover
285 osvvmContext.LastException = ex
286 raise ex
287 # osvvmContext._testcase = None
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
299 return optionID
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
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
320@export
321def RunTest(file: str, *options: int) -> None:
322 try:
323 file = Path(file)
324 testName = file.stem
326 # Analyze file
327 fullPath = (osvvmContext._currentDirectory / file).resolve()
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
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
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
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
363 except Exception as ex: # pragma: no cover
364 osvvmContext.LastException = ex
365 raise ex
366 # osvvmContext._testcase = None
369@export
370def LinkLibrary(libraryName: str, libraryPath: Nullable[str] = None):
371 print(f"[LinkLibrary] {libraryPath}")
374@export
375def LinkLibraryDirectory(libraryDirectory: str):
376 print(f"[LinkLibraryDirectory] {libraryDirectory}")
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
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
406 except Exception as ex: # pragma: no cover
407 osvvmContext.LastException = ex
408 raise ex
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
432@export
433def SetCoverageAnalyzeEnable(value: bool) -> None:
434 print(f"[SetCoverageAnalyzeEnable] {value}:{value.__class__.__name__}")
437@export
438def SetCoverageSimulateEnable(value: bool) -> None:
439 print(f"[SetCoverageSimulateEnable] {value}")
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
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
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
472@export
473def FindOsvvmSettingsDirectory(*args):
474 pass
477@export
478def CreateOsvvmScriptSettingsPkg(*args):
479 pass
482@export
483def noop(*args):
484 pass