Coverage for pyEDAA/OSVVM/Tcl.py: 84%
129 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-10 07:04 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-10 07:04 +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.Environment import Context, osvvmContext
41from pyEDAA.OSVVM.Procedures import noop
42from pyEDAA.OSVVM.Procedures import FileExists, DirectoryExists, FindOsvvmSettingsDirectory
43from pyEDAA.OSVVM.Procedures import build, include, library, analyze, simulate, generic
44from pyEDAA.OSVVM.Procedures import TestSuite, TestName, RunTest
45from pyEDAA.OSVVM.Procedures import ChangeWorkingDirectory, CreateOsvvmScriptSettingsPkg
46from pyEDAA.OSVVM.Procedures import SetVHDLVersion, GetVHDLVersion
47from pyEDAA.OSVVM.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 LoadProFile(self, path: Path) -> None:
83 includeFile = self._context.IncludeFile(path)
85 self.EvaluateProFile(includeFile)
87 def EvaluateProFile(self, path: Path) -> None:
88 try:
89 self._tcl.evalfile(str(path))
90 except TclError as ex:
91 raise getException(ex, self._context)
93 def __setitem__(self, tclVariableName: str, value: Any) -> None:
94 self._tcl.setvar(tclVariableName, value)
96 def __getitem__(self, tclVariableName: str) -> None:
97 return self._tcl.getvar(tclVariableName)
99 def __delitem__(self, tclVariableName: str) -> None:
100 self._tcl.unsetvar(tclVariableName)
103@export
104class OsvvmVariables:
105 _vhdlVersion: VHDLVersion
106 _toolVendor: str
107 _toolName: str
108 _toolVersion: str
110 def __init__(
111 self,
112 vhdlVersion: Nullable[VHDLVersion] = None,
113 toolVendor: Nullable[str] = None,
114 toolName: Nullable[str] = None,
115 toolVersion: Nullable[str] = None
116 ) -> None:
117 self._vhdlVersion = vhdlVersion if vhdlVersion is not None else VHDLVersion.VHDL2008
118 self._toolVendor = toolVendor if toolVendor is not None else "EDA²"
119 self._toolName = toolName if toolName is not None else "pyEDAA.ProjectModel"
120 self._toolVersion = toolVersion if toolVersion is not None else "0.1"
122 @readonly
123 def VHDlversion(self) -> VHDLVersion:
124 return self._vhdlVersion
126 @readonly
127 def ToolVendor(self) -> str:
128 return self._toolVendor
130 @readonly
131 def ToolName(self) -> str:
132 return self._toolName
134 @readonly
135 def ToolVersion(self) -> str:
136 return self._toolVersion
139@export
140class OsvvmProFileProcessor(TclEnvironment):
141 def __init__(
142 self,
143 context: Nullable[Context] = None,
144 osvvmVariables: Nullable[OsvvmVariables] = None
145 ) -> None:
146 if context is None: 146 ↛ 149line 146 didn't jump to line 149 because the condition on line 146 was always true
147 context = osvvmContext
149 super().__init__(context)
151 if osvvmVariables is None: 151 ↛ 154line 151 didn't jump to line 154 because the condition on line 151 was always true
152 osvvmVariables = OsvvmVariables()
154 self.LoadOsvvmDefaults(osvvmVariables)
155 self.OverwriteTclProcedures()
156 self.RegisterTclProcedures()
158 def LoadOsvvmDefaults(self, osvvmVariables: OsvvmVariables) -> None:
159 match osvvmVariables.VHDlversion:
160 case VHDLVersion.VHDL2002: 160 ↛ 161line 160 didn't jump to line 161 because the pattern on line 160 never matched
161 version = "2002"
162 case VHDLVersion.VHDL2008: 162 ↛ 164line 162 didn't jump to line 164 because the pattern on line 162 always matched
163 version = "2008"
164 case VHDLVersion.VHDL2019:
165 version = "2019"
166 case _:
167 version = "unsupported"
169 code = dedent(f"""\
170 namespace eval ::osvvm {
171 variable VhdlVersion {version}
172 variable ToolVendor "{osvvmVariables.ToolVendor}"
173 variable ToolName "{osvvmVariables.ToolName}"
174 variable ToolNameVersion "{osvvmVariables.ToolVersion}"
175 variable ToolSupportsDeferredConstants 1
176 variable ToolSupportsGenericPackages 1
177 variable FunctionalCoverageIntegratedInSimulator "default"
178 variable Support2019FilePath 1
180 variable ClockResetVersion 0
181 }
182 """)
184 try:
185 self._tcl.eval(code)
186 except TclError as ex:
187 raise OSVVMException(f"TCL error occurred, when initializing OSVVM variables.") from ex
189 def OverwriteTclProcedures(self) -> None:
190 self.RegisterPythonFunctionAsTclProcedure(noop, "puts")
192 def RegisterTclProcedures(self) -> None:
193 self.RegisterPythonFunctionAsTclProcedure(build)
194 self.RegisterPythonFunctionAsTclProcedure(include)
195 self.RegisterPythonFunctionAsTclProcedure(library)
196 self.RegisterPythonFunctionAsTclProcedure(analyze)
197 self.RegisterPythonFunctionAsTclProcedure(simulate)
198 self.RegisterPythonFunctionAsTclProcedure(generic)
200 self.RegisterPythonFunctionAsTclProcedure(TestSuite)
201 self.RegisterPythonFunctionAsTclProcedure(TestName)
202 self.RegisterPythonFunctionAsTclProcedure(RunTest)
204 self.RegisterPythonFunctionAsTclProcedure(SetVHDLVersion)
205 self.RegisterPythonFunctionAsTclProcedure(GetVHDLVersion)
206 self.RegisterPythonFunctionAsTclProcedure(SetCoverageAnalyzeEnable)
207 self.RegisterPythonFunctionAsTclProcedure(SetCoverageSimulateEnable)
209 self.RegisterPythonFunctionAsTclProcedure(FileExists)
210 self.RegisterPythonFunctionAsTclProcedure(DirectoryExists)
211 self.RegisterPythonFunctionAsTclProcedure(ChangeWorkingDirectory)
213 self.RegisterPythonFunctionAsTclProcedure(FindOsvvmSettingsDirectory)
214 self.RegisterPythonFunctionAsTclProcedure(CreateOsvvmScriptSettingsPkg)
217@export
218def getException(ex: Exception, context: Context) -> Exception:
219 if str(ex) == "": 219 ↛ 223line 219 didn't jump to line 223 because the condition on line 219 was always true
220 if (lastException := context.LastException) is not None: 220 ↛ 223line 220 didn't jump to line 223 because the condition on line 220 was always true
221 return lastException
223 return ex