Coverage for pyEDAA / CLITool / GHDL.py: 87%
247 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-23 22:25 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-23 22:25 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ ___ _____ _ #
3# _ __ _ _| ____| _ \ / \ / \ / ___| | |_ _|_ _|__ ___ | | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | | | | | |/ _ \ / _ \| | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |___| |___ | | | | (_) | (_) | | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)____|_____|___| |_|\___/ \___/|_| #
7# |_| |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2026 Patrick Lehmann - Boetzingen, Germany #
15# Copyright 2014-2016 Technische Universität Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture #
16# #
17# Licensed under the Apache License, Version 2.0 (the "License"); #
18# you may not use this file except in compliance with the License. #
19# You may obtain a copy of the License at #
20# #
21# http://www.apache.org/licenses/LICENSE-2.0 #
22# #
23# Unless required by applicable law or agreed to in writing, software #
24# distributed under the License is distributed on an "AS IS" BASIS, #
25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
26# See the License for the specific language governing permissions and #
27# limitations under the License. #
28# #
29# SPDX-License-Identifier: Apache-2.0 #
30# ==================================================================================================================== #
31#
32"""This module contains the CLI abstraction layer for `GHDL <https://github.com/ghdl/ghdl>`__."""
33from re import search as re_search
34from typing import Union, Iterable, Tuple, Optional as Nullable
36from pyTooling.Decorators import export
37from pyTooling.MetaClasses import ExtendedType
38from pyVHDLModel import VHDLVersion
40from pyTooling.CLIAbstraction import CLIArgument, Executable
41from pyTooling.CLIAbstraction.Argument import PathListArgument, StringArgument
42from pyTooling.CLIAbstraction.Command import CommandArgument
43from pyTooling.CLIAbstraction.Flag import ShortFlag, LongFlag
44from pyTooling.CLIAbstraction.BooleanFlag import LongBooleanFlag
45from pyTooling.CLIAbstraction.ValuedFlag import ShortValuedFlag, LongValuedFlag
46from pyTooling.CLIAbstraction.KeyValueFlag import ShortKeyValueFlag
48from pyEDAA.CLITool import CLIToolException
51@export
52class GHDLVersion(metaclass=ExtendedType, slots=True):
53 """
55 .. code-block::
57 GHDL 6.0.0-dev (4.1.0.r1065.gd3ea86f11.dirty) [Dunoon edition]
58 Compiled with GNAT Version: 15.2.0
59 static elaboration, mcode JIT code generator
60 Written by Tristan Gingold.
62 Copyright (C) 2003 - 2025 Tristan Gingold.
63 GHDL is free software, covered by the GNU General Public License. There is NO
64 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
65 """
66 _major: int
67 _minor: int
68 _micro: int
69 _dev: bool
70 _commitsSinceLastTag: int
71 _gitHash: str
72 _dirty: bool
73 _edition: str
74 _gnatCompiler: Tuple[int, int, int]
75 _backend: str
77 VERSION_LINE_PATTERN = (
78 r"GHDL"
79 r"\s(?P<Major>\d+)\.(?P<Minor>\d+)\.(?P<Micro>\d+)(?:-(?P<Suffix>dev|rc\d+))?"
80 r"\s\((?:"
81 r"(?:"
82 r"(?P<Packaging>tarball)"
83 r")|(?:"
84 r"[a-z]?(?P<major2>\d+)\.(?P<minor2>\d+)\.(?P<micro2>\d+)\.(?:r(?P<cslt>\d+))\.(?:g(?P<Hash>[0-9a-f]+))(?:\.(?P<Dirty>dirty))?"
85 r")|(?:"
86 r"Ubuntu\s(?P<UbuntuMajor>\d+)\.(?P<UbuntuMinor>\d+)\.(?P<UbuntuMicro>\d+)\+dfsg-(?P<dfsg>\d+)ubuntu(?P<UbuntuPackage>\d+)"
87 r")"
88 r")\)"
89 r"\s\[(?P<Edition>[\w\s]+)\]"
90 )
91 GNAT_LINE_PATTERN = (
92 r"\s*[\w\s]+:\s"
93 r"(?:"
94 r"(?:(?P<Major>\d+)\.(?P<Minor>\d+)\.(?P<Micro>\d+))"
95 r"|"
96 r"(?:Community\s(?P<Year>\d{4})\s\((?P<DateCode>\d{8}-\d{2})\))"
97 r")"
98 )
99 BACKEND_LINE_PATTERN = (
100 r"\s*"
101 r"(?:static elaboration, )?"
102 r"(?P<Backend>llvm|mcode|gcc)"
103 r"(?: (?P<LLVMMajor>\d+)\.(?P<LLVMMinor>\d+)\.(?P<LLVMMicro>\d+))?"
104 r"(?: JIT)?"
105 r" code generator"
106 )
108 def __init__(self, versionLine: str, gnatLine: str, backendLine: str) -> None:
109 match = re_search("^" + self.VERSION_LINE_PATTERN + "$", versionLine)
110 if match is None: 110 ↛ 111line 110 didn't jump to line 111 because the condition on line 110 was never true
111 raise CLIToolException(f"Unknown first GHDL version string '{versionLine}'.")
113 self._major = int(match["Major"])
114 self._minor = int(match["Minor"])
115 self._micro = int(match["Micro"])
116 if (suffix := match["Suffix"]) is not None: 116 ↛ 119line 116 didn't jump to line 119 because the condition on line 116 was always true
117 self._dev = suffix == "dev"
118 else:
119 self._dev = False
120 if (cslt := match["cslt"]) is not None: 120 ↛ 123line 120 didn't jump to line 123 because the condition on line 120 was always true
121 self._commitsSinceLastTag = int(cslt)
122 else:
123 self._commitsSinceLastTag = 0
124 self._gitHash = match["Hash"]
125 self._dirty = "Dirty" in match.groups()
126 self._edition = match["Edition"]
128 match = re_search("^" + self.GNAT_LINE_PATTERN + "$", gnatLine)
129 if match is None: 129 ↛ 130line 129 didn't jump to line 130 because the condition on line 129 was never true
130 raise CLIToolException(f"Unknown second GHDL version string '{gnatLine}'.")
132 if match["Year"] is None: 132 ↛ 135line 132 didn't jump to line 135 because the condition on line 132 was always true
133 self._gnatCompiler = (int(match["Major"]), int(match["Minor"]), int(match["Micro"]))
134 else:
135 self._gnatCompiler = (int(match["Year"]), 0, 0)
137 match = re_search("^" + self.BACKEND_LINE_PATTERN + "$", backendLine)
138 if match is None: 138 ↛ 139line 138 didn't jump to line 139 because the condition on line 138 was never true
139 raise CLIToolException(f"Unknown third GHDL version string '{backendLine}'.")
141 self._backend = match["Backend"]
143 @property
144 def Major(self) -> int:
145 return self._major
147 @property
148 def Minor(self) -> int:
149 return self._minor
151 @property
152 def Micro(self) -> int:
153 return self._micro
155 @property
156 def Dev(self) -> bool:
157 return self._dev
159 @property
160 def CommitsSinceLastTag(self) -> int:
161 return self._commitsSinceLastTag
163 @property
164 def GitHash(self) -> str:
165 return self._gitHash
167 @property
168 def Dirty(self) -> bool:
169 return self._dirty
171 @property
172 def Edition(self) -> str:
173 return self._edition
175 @property
176 def Backend(self) -> str:
177 return self._backend
179 def __str__(self) -> str:
180 dev = f"-dev" if self._dev else ""
181 return f"{self._major}.{self._minor}.{self._micro}{dev}"
183 def __repr__(self) -> str:
184 return f"{self.__str__()} (Backend: {self._backend}; Git: {self._gitHash})"
187@export
188class GHDL(Executable):
189 _executableNames = {
190 "Darwin": "ghdl",
191 "FreeBSD": "ghdl",
192 "Linux": "ghdl",
193 "Windows": "ghdl.exe"
194 }
196 # XXX: overwrite __init__ and get backend variant
197 # XXX: check for compatible backends
199 @CLIArgument()
200 class CommandHelp(CommandArgument, name="help"):
201 """Print help page(s)."""
203 @CLIArgument()
204 class CommandVersion(CommandArgument, name="version"):
205 """Print version information."""
207 @CLIArgument()
208 class CommandSyntax(CommandArgument, name="syntax"):
209 """Check syntax."""
211 @CLIArgument()
212 class CommandElaborationOrder(CommandArgument, name="elab-order"):
213 """Display (elaboration) ordered source files."""
215 @CLIArgument()
216 class CommandAnalyze(CommandArgument, name="analyze"):
217 """Analyze VHDL source file(s)."""
219 @CLIArgument()
220 class CommandElaborate(CommandArgument, name="elaborate"):
221 """Elaborate design."""
223 @CLIArgument()
224 class CommandElaborationAndRun(CommandArgument, name="elab-run"):
225 """Elaborate and simulate design."""
227 @CLIArgument()
228 class CommandRun(CommandArgument, name="run"):
229 """Simulate design."""
231 @CLIArgument()
232 class CommandBind(CommandArgument, name="bind"):
233 """Bind design unit."""
235 @CLIArgument()
236 class CommandLink(CommandArgument, name="link"):
237 """Link design unit."""
239 @CLIArgument()
240 class CommandListLink(CommandArgument, name="list-link"):
241 """List objects file to link a design unit."""
243 @CLIArgument()
244 class CommandCompile(CommandArgument, name="compile"):
245 """Generate whole sequence to elaborate design from files."""
247 @CLIArgument()
248 class CommandGenerateDependencies(CommandArgument, name="gen-depends"):
249 """Generate dependencies of design."""
251 @CLIArgument()
252 class CommandSynthesize(CommandArgument, name="synth"):
253 """Synthesis from design unit."""
255 @CLIArgument()
256 class FlagVerbose(ShortFlag, name="v"):
257 """Run in verbose mode (print more messages)."""
259 # Analyze and elaborate options
260 @CLIArgument()
261 class FlagVHDLStandard(LongValuedFlag, name="std"):
262 """Set the used VHDL standard version."""
263 _value: VHDLVersion
265 def __init__(self, value: VHDLVersion):
266 if value is None: 266 ↛ 267line 266 didn't jump to line 267 because the condition on line 266 was never true
267 raise ValueError(f"") # FIXME: add message
269 self._value = value
271 @property
272 def Value(self) -> VHDLVersion:
273 return self._value
275 @Value.setter
276 def Value(self, value: VHDLVersion) -> None:
277 if value is None:
278 raise ValueError(f"") # FIXME: add message
280 self._value = value
282 def AsArgument(self) -> Union[str, Iterable[str]]:
283 if self._name is None: 283 ↛ 284line 283 didn't jump to line 284 because the condition on line 283 was never true
284 raise ValueError(f"") # FIXME: add message
286 return self._pattern.format(self._name, str(self._value)[-2:])
288 @CLIArgument()
289 class FlagIEEEFlavor(LongValuedFlag, name="ieee"):
290 """Set the used VHDL flavor."""
292 @CLIArgument()
293 class FlagSynopsys(ShortFlag, name="fsynopsys"):
294 """Set used VHDL flavor to *Synopsys* and make Synopsys packages visible in library ``ìeee``."""
296 @CLIArgument()
297 class FlagRelaxed(ShortFlag, name="frelaxed"):
298 """Relax some LRM rules."""
300 @CLIArgument()
301 class FlagExplicit(ShortFlag, name="fexplicit"): ...
303 @CLIArgument()
304 class FlagLibrary(LongValuedFlag, name="work"):
305 """Set working library."""
307 @CLIArgument()
308 class FlagWorkingDirectory(LongValuedFlag, name="workdir"):
309 """Set working directory."""
311 @CLIArgument()
312 class FlagMultiByteComments(LongFlag, name="mb-comments"):
313 """Allow multi-byte comments."""
315 @CLIArgument()
316 class FlagSyntesisBindingRule(LongFlag, name="syn-binding"):
317 """Enable synthesis binding rule."""
319 @CLIArgument()
320 class FlagSearchPath(ShortValuedFlag, name="P", pattern="-{0}{1}"):
321 """Add search path."""
323 @CLIArgument()
324 class FlagTimeResolution(LongValuedFlag, name="time-resolution"):
325 """Set base time resolution.
327 Allowed values are ``auto`` (default), ``fs``, ``ps``, ``ns``, ``us``, ``ms`` or ``sec``.
328 """
330 @CLIArgument()
331 class FlagVitalChecks(LongBooleanFlag, name="vital-checks", pattern="-{0}", falsePattern="--no-{0}"):
332 """Check VITAL restrictions."""
334 @CLIArgument()
335 class FlagWarnUnboundComponents(ShortFlag, name="binding", pattern="-W{0}"):
336 """Warns for unbound components."""
338 @CLIArgument()
339 class FlagWarnReservedWords(ShortFlag, name="reserved", pattern="-W{0}"):
340 """Warns if VHDL'93 reserved words are used in VHDL'87."""
342 @CLIArgument()
343 class FlagWarnRedefinedDesignUnits(ShortFlag, name="library", pattern="-W{0}"):
344 """Warns for redefined design unit."""
346 @CLIArgument()
347 class FlagWarnNonVitalGenericNames(ShortFlag, name="vital-generic", pattern="-W{0}"):
348 """Warns of non-vital generic names."""
350 @CLIArgument()
351 class FlagWarnElaborationChecks(ShortFlag, name="delayed-checks", pattern="-W{0}"):
352 """Warns for checks performed at elaboration."""
354 @CLIArgument()
355 class FlagWarnUnnecessaryPackageBody(ShortFlag, name="body", pattern="-W{0}"):
356 """Warns for unnecessary package body."""
358 @CLIArgument()
359 class FlagWarnOthersSpecifications(ShortFlag, name="specs", pattern="-W{0}"):
360 """Warns if an all/others specification does not apply."""
362 @CLIArgument()
363 class FlagUnused(ShortFlag, name="unused", pattern="-W{0}"):
364 """Warns for unused subprograms."""
366 @CLIArgument()
367 class FlagError(ShortFlag, name="error", pattern="-W{0}"):
368 """Turns warnings into errors."""
370 @CLIArgument()
371 class OptionPaths(PathListArgument):
372 """Add list of VHDL files to analyze."""
374 @CLIArgument()
375 class OptionTopLevel(StringArgument):
376 """Specify the toplevel design unit."""
378 @CLIArgument()
379 class OptionArchitecture(StringArgument):
380 """Specify the architecture name, if the toplevel design unit is an entity."""
382 @CLIArgument()
383 class FlagGenerics(ShortKeyValueFlag, pattern="-{0}{1}={2}"):
384 """Set a generic value."""
386 @CLIArgument()
387 class FlagAsserts(ShortValuedFlag, name="asserts"):
388 """Select how assertions are handled.
390 It can be ``enable`` (the default), ``disable`` which disables all assertions and ``disable-at-0`` which disables
391 only at the start of simulation.
392 """
394 @CLIArgument()
395 class FlagIEEEAsserts(ShortValuedFlag, name="ieee-asserts"):
396 """Select how assertions are handled.
398 It can be ``enable`` (the default), ``disable`` which disables all assertions and ``disable-at-0`` which disables
399 only at the start of simulation.
400 """
402 @CLIArgument()
403 class FlagStopTime(ShortValuedFlag, name="stop-time"):
404 """Stop the simulation after a given simulation time.
406 The time is expressed as a time value, without any spaces. The time is the simulation time, not the real execution time.
407 """
409 @CLIArgument()
410 class FlagMaxDeltaCycles(ShortValuedFlag, name="stop-delta"):
411 """Stop the simulation after N delta cycles in the same current time."""
413 @CLIArgument()
414 class FlagDisplayDeltaCycles(ShortValuedFlag, name="disp-time"):
415 """Display the time and delta cycle number as simulation advances."""
417 @CLIArgument()
418 class FlagUnbufferedIO(ShortValuedFlag, name="unbuffered"):
419 """Disable buffering on STDOUT, STDERR and files opened in write or append mode (TEXTIO)."""
421 @CLIArgument()
422 class FlagReadWaveformOptionsFile(ShortValuedFlag, name="read-wave-opt"):
423 """Filter signals to be dumped to the waveform file according to the wavefile option file provided."""
425 @CLIArgument()
426 class FlagWriteWaveformOptionsFile(ShortValuedFlag, name="write-wave-opt"):
427 """If the wavefile option file doesn’t exist, creates it with all the signals of the design.
428 Otherwise, it throws an error, because it won’t erase an existing file.
429 """
431 @CLIArgument()
432 class FlagGHWWaveformFile(ShortValuedFlag, name="wave"):
433 """Write the waveforms into a GHDL Waveform (``*.ghw``) file.
435 Contrary to VCD files, any VHDL type can be dumped into a GHW file.
436 """
438 def _CopyParameters(self, tool: "GHDL") -> None:
439 for key in self.__cliParameters__:
440 if self._NeedsParameterInitialization(key):
441 value = self.__cliParameters__[key].Value
442 tool.__cliParameters__[key] = key(value)
443 else:
444 tool.__cliParameters__[key] = key()
446 def _SetParameters(self, tool: "GHDL", std: Nullable[VHDLVersion] = None, ieee: Nullable[str] = None):
447 if std is not None: 447 ↛ 448line 447 didn't jump to line 448 because the condition on line 447 was never true
448 tool[self.FlagVHDLStandard] = str(std)
450 if ieee is not None: 450 ↛ 451line 450 didn't jump to line 451 because the condition on line 450 was never true
451 tool[self.FlagVHDLStandard] = ieee
453 def GetGHDLAsAnalyzer(self, std: Nullable[VHDLVersion] = None, ieee: Nullable[str] = None) -> "GHDL":
454 tool = GHDL(executablePath=self._executablePath)
456 tool[tool.CommandAnalyze] = True
457 self._CopyParameters(tool)
458 self._SetParameters(tool, std, ieee)
460 return tool
462 def GetGHDLAsElaborator(self, std: Nullable[VHDLVersion] = None, ieee: Nullable[str] = None) -> "GHDL":
463 tool = GHDL(executablePath=self._executablePath)
465 tool[tool.CommandElaborate] = True
466 self._CopyParameters(tool)
467 self._SetParameters(tool, std, ieee)
469 return tool
471 def GetGHDLAsSimulator(self, std: Nullable[VHDLVersion] = None, ieee: Nullable[str] = None) -> "GHDL":
472 tool = GHDL(executablePath=self._executablePath)
474 tool[tool.CommandRun] = True
475 self._CopyParameters(tool)
476 self._SetParameters(tool, std, ieee)
478 return tool
480 def Help(self) -> str:
481 tool = GHDL(executablePath=self._executablePath)
483 tool[tool.CommandHelp] = True
485 tool.StartProcess()
486 return "\n".join(tool.GetLineReader())
488 def Version(self) -> GHDLVersion:
489 tool = GHDL(executablePath=self._executablePath)
491 tool[tool.CommandVersion] = True
493 tool.StartProcess()
494 iterator = iter(tool.GetLineReader())
495 firstLine = next(iterator)
496 secondLine = next(iterator)
497 thirdLine = next(iterator)
499 return GHDLVersion(firstLine, secondLine, thirdLine)