Coverage for pyEDAA/OSVVM/Project/TCL.py: 75%
161 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#
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
48from pyEDAA.OSVVM.Project.Procedures import ConstraintFile, ScopeToRef, ScopeToCell
51@export
52class TclEnvironment:
53 _tcl: Tk
54 _procedures: Dict[str, Callable]
55 _context: Context
57 def __init__(self, context: Context) -> None:
58 self._context = context
59 context._processor = self
61 self._tcl = Tcl()
62 self._procedures = {}
64 @readonly
65 def TCL(self) -> Tk:
66 return self._tcl
68 @readonly
69 def Procedures(self) -> Dict[str, Callable]:
70 return self._procedures
72 @readonly
73 def Context(self) -> Context:
74 return self._context
76 def RegisterPythonFunctionAsTclProcedure(self, pythonFunction: Callable, tclProcedureName: Nullable[str] = None):
77 if tclProcedureName is None:
78 tclProcedureName = pythonFunction.__name__
80 self._tcl.createcommand(tclProcedureName, pythonFunction)
81 self._procedures[tclProcedureName] = pythonFunction
83 def EvaluateTclCode(self, tclCode: str) -> None:
84 try:
85 self._tcl.eval(tclCode)
86 except TclError as e:
87 e = getException(e, self._context)
88 ex = OSVVMException(f"Caught TclError while evaluating TCL code.")
89 ex.add_note(tclCode)
90 raise ex from e
92 def EvaluateProFile(self, path: Path) -> None:
93 try:
94 self._tcl.evalfile(str(path))
95 except TclError as e:
96 ex = getException(e, self._context)
97 raise OSVVMException(f"Caught TclError while processing '{path}'.") from ex
99 def __setitem__(self, tclVariableName: str, value: Any) -> None:
100 self._tcl.setvar(tclVariableName, value)
102 def __getitem__(self, tclVariableName: str) -> None:
103 return self._tcl.getvar(tclVariableName)
105 def __delitem__(self, tclVariableName: str) -> None:
106 self._tcl.unsetvar(tclVariableName)
109@export
110class OsvvmVariables:
111 _vhdlVersion: VHDLVersion
112 _toolVendor: str
113 _toolName: str
114 _toolVersion: str
116 def __init__(
117 self,
118 vhdlVersion: Nullable[VHDLVersion] = None,
119 toolVendor: Nullable[str] = None,
120 toolName: Nullable[str] = None,
121 toolVersion: Nullable[str] = None
122 ) -> None:
123 self._vhdlVersion = vhdlVersion if vhdlVersion is not None else VHDLVersion.VHDL2008
124 self._toolVendor = toolVendor if toolVendor is not None else "EDA²"
125 self._toolName = toolName if toolName is not None else "pyEDAA.ProjectModel"
126 self._toolVersion = toolVersion if toolVersion is not None else "0.1"
128 @readonly
129 def VHDlversion(self) -> VHDLVersion:
130 return self._vhdlVersion
132 @readonly
133 def ToolVendor(self) -> str:
134 return self._toolVendor
136 @readonly
137 def ToolName(self) -> str:
138 return self._toolName
140 @readonly
141 def ToolVersion(self) -> str:
142 return self._toolVersion
145@export
146class OsvvmProFileProcessor(TclEnvironment):
147 def __init__(
148 self,
149 context: Nullable[Context] = None,
150 osvvmVariables: Nullable[OsvvmVariables] = None
151 ) -> None:
152 if context is None: 152 ↛ 155line 152 didn't jump to line 155 because the condition on line 152 was always true
153 context = osvvmContext
155 super().__init__(context)
157 if osvvmVariables is None: 157 ↛ 160line 157 didn't jump to line 160 because the condition on line 157 was always true
158 osvvmVariables = OsvvmVariables()
160 self.LoadOsvvmDefaults(osvvmVariables)
161 self.OverwriteTclProcedures()
162 self.RegisterTclProcedures()
164 def LoadOsvvmDefaults(self, osvvmVariables: OsvvmVariables) -> None:
165 match osvvmVariables.VHDlversion:
166 case VHDLVersion.VHDL2002: 166 ↛ 167line 166 didn't jump to line 167 because the pattern on line 166 never matched
167 version = "2002"
168 case VHDLVersion.VHDL2008: 168 ↛ 170line 168 didn't jump to line 170 because the pattern on line 168 always matched
169 version = "2008"
170 case VHDLVersion.VHDL2019:
171 version = "2019"
172 case _:
173 version = "unsupported"
175 code = dedent(f"""\
176 namespace eval ::osvvm {{
177 variable VhdlVersion {version}
178 variable ToolVendor "{osvvmVariables.ToolVendor}"
179 variable ToolName "{osvvmVariables.ToolName}"
180 variable ToolNameVersion "{osvvmVariables.ToolVersion}"
181 variable ToolSupportsDeferredConstants 1
182 variable ToolSupportsGenericPackages 1
183 variable FunctionalCoverageIntegratedInSimulator "default"
184 variable Support2019FilePath 1
186 variable ClockResetVersion 0
187 }}
188 """)
190 try:
191 self._tcl.eval(code)
192 except TclError as ex:
193 raise OSVVMException(f"TCL error occurred, when initializing OSVVM variables.") from ex
195 def OverwriteTclProcedures(self) -> None:
196 self.RegisterPythonFunctionAsTclProcedure(noop, "puts")
198 def RegisterTclProcedures(self) -> None:
199 self.RegisterPythonFunctionAsTclProcedure(build)
200 self.RegisterPythonFunctionAsTclProcedure(include)
201 self.RegisterPythonFunctionAsTclProcedure(library)
202 self.RegisterPythonFunctionAsTclProcedure(analyze)
203 self.RegisterPythonFunctionAsTclProcedure(simulate)
204 self.RegisterPythonFunctionAsTclProcedure(generic)
206 self.RegisterPythonFunctionAsTclProcedure(BuildName)
207 self.RegisterPythonFunctionAsTclProcedure(NoNullRangeWarning)
209 self.RegisterPythonFunctionAsTclProcedure(TestSuite)
210 self.RegisterPythonFunctionAsTclProcedure(TestName)
211 self.RegisterPythonFunctionAsTclProcedure(RunTest)
213 self.RegisterPythonFunctionAsTclProcedure(SetVHDLVersion)
214 self.RegisterPythonFunctionAsTclProcedure(GetVHDLVersion)
215 self.RegisterPythonFunctionAsTclProcedure(SetCoverageAnalyzeEnable)
216 self.RegisterPythonFunctionAsTclProcedure(SetCoverageSimulateEnable)
218 self.RegisterPythonFunctionAsTclProcedure(FileExists)
219 self.RegisterPythonFunctionAsTclProcedure(DirectoryExists)
220 self.RegisterPythonFunctionAsTclProcedure(ChangeWorkingDirectory)
222 self.RegisterPythonFunctionAsTclProcedure(FindOsvvmSettingsDirectory)
223 self.RegisterPythonFunctionAsTclProcedure(CreateOsvvmScriptSettingsPkg)
225 self.RegisterPythonFunctionAsTclProcedure(ConstraintFile)
226 self.RegisterPythonFunctionAsTclProcedure(ScopeToRef)
227 self.RegisterPythonFunctionAsTclProcedure(ScopeToCell)
229 self.RegisterPythonFunctionAsTclProcedure(noop, "OpenBuildHtml")
230 self.RegisterPythonFunctionAsTclProcedure(noop, "SetTranscriptType")
231 self.RegisterPythonFunctionAsTclProcedure(noop, "GetTranscriptType")
232 self.RegisterPythonFunctionAsTclProcedure(noop, "SetSimulatorResolution")
233 self.RegisterPythonFunctionAsTclProcedure(noop, "GetSimulatorResolution")
235 def LoadIncludeFile(self, path: Path) -> None:
236 # TODO: should a context be used with _context to restore _currentDirectory?
237 includeFile = self._context.IncludeFile(path)
238 self.EvaluateProFile(includeFile)
240 def LoadBuildFile(self, buildFile: Path, buildName: Nullable[str] = None) -> Build:
241 if buildName is None:
242 buildName = buildFile.stem
244 self._context.BeginBuild(buildName)
245 includeFile = self._context.IncludeFile(buildFile)
246 self.EvaluateProFile(includeFile)
248 # TODO: should a context be used with _context to restore _currentDirectory?
249 return self._context.EndBuild()
251 def LoadRegressionFile(self, regressionFile: Path, projectName: Nullable[str] = None) -> Project:
252 if projectName is None:
253 projectName = regressionFile.stem
255 self.EvaluateProFile(regressionFile)
257 return self._context.ToProject(projectName)
260@export
261def getException(ex: Exception, context: Context) -> Exception:
262 if str(ex) == "": 262 ↛ 266line 262 didn't jump to line 266 because the condition on line 262 was always true
263 if (lastException := context.LastException) is not None: 263 ↛ 266line 263 didn't jump to line 266 because the condition on line 263 was always true
264 return lastException
266 return ex