GHDL.py

Summary

Lines:

213 of 236 covered (90.3%)

Branches:

16 of 28 taken (57.1%), 10 lines partially

Coverage:

86.7%

Directory:

CLITool

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)