Coverage for pyEDAA/OSVVM/Project/TCL.py: 75%
157 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#
31from pathlib import Path
32from textwrap import dedent
33from tkinter import Tk, Tcl, TclError
34from typing import Any, Dict, Callable, Optional as Nullable
36from pyTooling.Decorators import readonly, export
37from pyVHDLModel import VHDLVersion
39from pyEDAA.OSVVM import OSVVMException
40from pyEDAA.OSVVM.Project import Context, osvvmContext, Build, Project
41from pyEDAA.OSVVM.Project.Procedures import noop, NoNullRangeWarning
42from pyEDAA.OSVVM.Project.Procedures import FileExists, DirectoryExists, FindOsvvmSettingsDirectory
43from pyEDAA.OSVVM.Project.Procedures import build, BuildName, include, library, analyze, simulate, generic
44from pyEDAA.OSVVM.Project.Procedures import TestSuite, TestName, RunTest
45from pyEDAA.OSVVM.Project.Procedures import ChangeWorkingDirectory, CreateOsvvmScriptSettingsPkg
46from pyEDAA.OSVVM.Project.Procedures import SetVHDLVersion, GetVHDLVersion
47from pyEDAA.OSVVM.Project.Procedures import SetCoverageAnalyzeEnable, SetCoverageSimulateEnable
50@export
51class TclEnvironment:
52 _tcl: Tk
53 _procedures: Dict[str, Callable]
54 _context: Context
56 def __init__(self, context: Context) -> None:
57 self._context = context
58 context._processor = self
60 self._tcl = Tcl()
61 self._procedures = {}
63 @readonly
64 def TCL(self) -> Tk:
65 return self._tcl
67 @readonly
68 def Procedures(self) -> Dict[str, Callable]:
69 return self._procedures
71 @readonly
72 def Context(self) -> Context:
73 return self._context
75 def RegisterPythonFunctionAsTclProcedure(self, pythonFunction: Callable, tclProcedureName: Nullable[str] = None):
76 if tclProcedureName is None:
77 tclProcedureName = pythonFunction.__name__
79 self._tcl.createcommand(tclProcedureName, pythonFunction)
80 self._procedures[tclProcedureName] = pythonFunction
82 def EvaluateTclCode(self, tclCode: str) -> None:
83 try:
84 self._tcl.eval(tclCode)
85 except TclError as e:
86 e = getException(e, self._context)
87 ex = OSVVMException(f"Caught TclError while evaluating TCL code.")
88 ex.add_note(tclCode)
89 raise ex from e
91 def EvaluateProFile(self, path: Path) -> None:
92 try:
93 self._tcl.evalfile(str(path))
94 except TclError as e:
95 ex = getException(e, self._context)
96 raise OSVVMException(f"Caught TclError while processing '{path}'.") from ex
98 def __setitem__(self, tclVariableName: str, value: Any) -> None:
99 self._tcl.setvar(tclVariableName, value)
101 def __getitem__(self, tclVariableName: str) -> None:
102 return self._tcl.getvar(tclVariableName)
104 def __delitem__(self, tclVariableName: str) -> None:
105 self._tcl.unsetvar(tclVariableName)
108@export
109class OsvvmVariables:
110 _vhdlVersion: VHDLVersion
111 _toolVendor: str
112 _toolName: str
113 _toolVersion: str
115 def __init__(
116 self,
117 vhdlVersion: Nullable[VHDLVersion] = None,
118 toolVendor: Nullable[str] = None,
119 toolName: Nullable[str] = None,
120 toolVersion: Nullable[str] = None
121 ) -> None:
122 self._vhdlVersion = vhdlVersion if vhdlVersion is not None else VHDLVersion.VHDL2008
123 self._toolVendor = toolVendor if toolVendor is not None else "EDA²"
124 self._toolName = toolName if toolName is not None else "pyEDAA.ProjectModel"
125 self._toolVersion = toolVersion if toolVersion is not None else "0.1"
127 @readonly
128 def VHDlversion(self) -> VHDLVersion:
129 return self._vhdlVersion
131 @readonly
132 def ToolVendor(self) -> str:
133 return self._toolVendor
135 @readonly
136 def ToolName(self) -> str:
137 return self._toolName
139 @readonly
140 def ToolVersion(self) -> str:
141 return self._toolVersion
144@export
145class OsvvmProFileProcessor(TclEnvironment):
146 def __init__(
147 self,
148 context: Nullable[Context] = None,
149 osvvmVariables: Nullable[OsvvmVariables] = None
150 ) -> None:
151 if context is None: 151 ↛ 154line 151 didn't jump to line 154 because the condition on line 151 was always true
152 context = osvvmContext
154 super().__init__(context)
156 if osvvmVariables is None: 156 ↛ 159line 156 didn't jump to line 159 because the condition on line 156 was always true
157 osvvmVariables = OsvvmVariables()
159 self.LoadOsvvmDefaults(osvvmVariables)
160 self.OverwriteTclProcedures()
161 self.RegisterTclProcedures()
163 def LoadOsvvmDefaults(self, osvvmVariables: OsvvmVariables) -> None:
164 match osvvmVariables.VHDlversion:
165 case VHDLVersion.VHDL2002: 165 ↛ 166line 165 didn't jump to line 166 because the pattern on line 165 never matched
166 version = "2002"
167 case VHDLVersion.VHDL2008: 167 ↛ 169line 167 didn't jump to line 169 because the pattern on line 167 always matched
168 version = "2008"
169 case VHDLVersion.VHDL2019:
170 version = "2019"
171 case _:
172 version = "unsupported"
174 code = dedent(f"""\
175 namespace eval ::osvvm {{
176 variable VhdlVersion {version}
177 variable ToolVendor "{osvvmVariables.ToolVendor}"
178 variable ToolName "{osvvmVariables.ToolName}"
179 variable ToolNameVersion "{osvvmVariables.ToolVersion}"
180 variable ToolSupportsDeferredConstants 1
181 variable ToolSupportsGenericPackages 1
182 variable FunctionalCoverageIntegratedInSimulator "default"
183 variable Support2019FilePath 1
185 variable ClockResetVersion 0
186 }}
187 """)
189 try:
190 self._tcl.eval(code)
191 except TclError as ex:
192 raise OSVVMException(f"TCL error occurred, when initializing OSVVM variables.") from ex
194 def OverwriteTclProcedures(self) -> None:
195 self.RegisterPythonFunctionAsTclProcedure(noop, "puts")
197 def RegisterTclProcedures(self) -> None:
198 self.RegisterPythonFunctionAsTclProcedure(build)
199 self.RegisterPythonFunctionAsTclProcedure(include)
200 self.RegisterPythonFunctionAsTclProcedure(library)
201 self.RegisterPythonFunctionAsTclProcedure(analyze)
202 self.RegisterPythonFunctionAsTclProcedure(simulate)
203 self.RegisterPythonFunctionAsTclProcedure(generic)
205 self.RegisterPythonFunctionAsTclProcedure(BuildName)
206 self.RegisterPythonFunctionAsTclProcedure(NoNullRangeWarning)
208 self.RegisterPythonFunctionAsTclProcedure(TestSuite)
209 self.RegisterPythonFunctionAsTclProcedure(TestName)
210 self.RegisterPythonFunctionAsTclProcedure(RunTest)
212 self.RegisterPythonFunctionAsTclProcedure(SetVHDLVersion)
213 self.RegisterPythonFunctionAsTclProcedure(GetVHDLVersion)
214 self.RegisterPythonFunctionAsTclProcedure(SetCoverageAnalyzeEnable)
215 self.RegisterPythonFunctionAsTclProcedure(SetCoverageSimulateEnable)
217 self.RegisterPythonFunctionAsTclProcedure(FileExists)
218 self.RegisterPythonFunctionAsTclProcedure(DirectoryExists)
219 self.RegisterPythonFunctionAsTclProcedure(ChangeWorkingDirectory)
221 self.RegisterPythonFunctionAsTclProcedure(FindOsvvmSettingsDirectory)
222 self.RegisterPythonFunctionAsTclProcedure(CreateOsvvmScriptSettingsPkg)
224 self.RegisterPythonFunctionAsTclProcedure(noop, "OpenBuildHtml")
225 self.RegisterPythonFunctionAsTclProcedure(noop, "SetTranscriptType")
226 self.RegisterPythonFunctionAsTclProcedure(noop, "GetTranscriptType")
227 self.RegisterPythonFunctionAsTclProcedure(noop, "SetSimulatorResolution")
228 self.RegisterPythonFunctionAsTclProcedure(noop, "GetSimulatorResolution")
230 def LoadIncludeFile(self, path: Path) -> None:
231 includeFile = self._context.IncludeFile(path)
233 self.EvaluateProFile(includeFile)
235 def LoadBuildFile(self, buildFile: Path, buildName: Nullable[str] = None) -> Build:
236 if buildName is None:
237 buildName = buildFile.stem
239 self._context.BeginBuild(buildName)
240 includeFile = self._context.IncludeFile(buildFile)
241 self.EvaluateProFile(includeFile)
243 return self._context.EndBuild()
245 def LoadRegressionFile(self, regressionFile: Path, projectName: Nullable[str] = None) -> Project:
246 if projectName is None:
247 projectName = regressionFile.stem
249 self.EvaluateProFile(regressionFile)
251 return self._context.ToProject(projectName)
254@export
255def getException(ex: Exception, context: Context) -> Exception:
256 if str(ex) == "": 256 ↛ 260line 256 didn't jump to line 260 because the condition on line 256 was always true
257 if (lastException := context.LastException) is not None: 257 ↛ 260line 257 didn't jump to line 260 because the condition on line 257 was always true
258 return lastException
260 return ex