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