NVC.py

Summary

Lines:

132 of 150 covered (88.0%)

Branches:

10 of 16 taken (62.5%), 4 lines partially

Coverage:

85.5%

Directory:

CLITool

Missed lines:

87, 125, 228, 238-241, 245, 304, 316-322, 325-331

Source

pyEDAA/CLITool/NVC.pycoveredpartialuncoveredexcludednot executable
1# ==================================================================================================================== #
2#              _____ ____    _        _      ____ _     ___ _____           _                                          #
3#  _ __  _   _| ____|  _ \  / \      / \    / ___| |   |_ _|_   _|__   ___ | |                                         #
4# | '_ \| | | |  _| | | | |/ _ \    / _ \  | |   | |    | |  | |/ _ \ / _ \| |                                         #
5# | |_) | |_| | |___| |_| / ___ \  / ___ \ | |___| |___ | |  | | (_) | (_) | |                                         #
6# | .__/ \__, |_____|____/_/   \_\/_/   \_(_)____|_____|___| |_|\___/ \___/|_|                                         #
7# |_|    |___/                                                                                                         #
8# ==================================================================================================================== #
9# Authors:                                                                                                             #
10#   Patrick Lehmann                                                                                                    #
11#                                                                                                                      #
12# License:                                                                                                             #
13# ==================================================================================================================== #
14# Copyright 2026-2026 Electronic Design Automation Abstraction (EDA²)                                                  #
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#
31"""This module contains the CLI abstraction layer for `NVC <https://github.com/nickg/nvc>`__."""
32from re                    import search as re_search
33from typing                import Union, Iterable, Optional as Nullable
34
35from pyTooling.Decorators  import export
36from pyTooling.MetaClasses import ExtendedType
37from pyVHDLModel           import VHDLVersion
38
39from pyTooling.CLIAbstraction                 import CLIArgument, Executable
40from pyTooling.CLIAbstraction.Argument        import PathListArgument, StringArgument
41from pyTooling.CLIAbstraction.Command         import LongCommand
42from pyTooling.CLIAbstraction.Flag            import ShortFlag, LongFlag
43from pyTooling.CLIAbstraction.BooleanFlag     import LongBooleanFlag
44from pyTooling.CLIAbstraction.ValuedFlag      import ShortValuedFlag, LongValuedFlag
45from pyTooling.CLIAbstraction.ValuedTupleFlag import LongTupleFlag, ShortTupleFlag
46from pyTooling.CLIAbstraction.KeyValueFlag    import ShortKeyValueFlag, LongKeyValueFlag
47
48from pyEDAA.CLITool                           import CLIToolException
49
50
51@export
52class NVCVersion(metaclass=ExtendedType, slots=True):
53	"""
54
55  .. code-block::
56
57	   nvc 1.18.2 (1.18.2.r0.g8893318a) (Using LLVM 21.1.5)
58     Copyright (C) 2011-2025  Nick Gasson
59     This program comes with ABSOLUTELY NO WARRANTY. This is free software, and
60     you are welcome to redistribute it under certain conditions. See the GNU
61     General Public Licence for details.
62	"""
63	_major: int
64	_minor: int
65	_micro: int
66	# _dev:   bool
67	_commitsSinceLastTag: int
68	_gitHash: str
69	_backend: str
70
71	VERSION_LINE_PATTERN = (
72		r"nvc"
73		r"\s(?P<Major>\d+)\.(?P<Minor>\d+)\.(?P<Micro>\d+)(?:-(?P<Suffix>dev|rc\d+))?"
74		r"\s\((?:"
75			r"(?:"
76				r"(?P<major2>\d+)\.(?P<minor2>\d+)\.(?P<micro2>\d+)\.(?:r(?P<cslt>\d+))\.(?:g(?P<Hash>[0-9a-f]+))(?:\.(?P<Dirty>dirty))?"
77			r")|(?:"
78				r"(?P<Hash2>[0-9a-f]{7})"
79			r")"
80		r")\)"
81		r"\s\(Using (?P<Backend>\w+) (?P<BackendMajor>\d+)\.(?P<BackendMinor>\d+)\.(?P<BackendMicro>\d+)\)"
82	)
83
84	def __init__(self, versionLine: str) -> None:
85		match = re_search("^" + self.VERSION_LINE_PATTERN + "$", versionLine)
86		if match is None:
87			raise CLIToolException(f"Unknown NVC version string '{versionLine}'.")
88
89		self._major = int(match["Major"])
90		self._minor = int(match["Minor"])
91		self._micro = int(match["Micro"])
92		# if (suffix := match["Suffix"]) is not None:
93		# 	self._dev = suffix == "dev"
94		# else:
95		# 	self._dev = False
96		if (cslt := match["cslt"]) is not None:
97			self._commitsSinceLastTag = int(cslt)
98		else:
99			self._commitsSinceLastTag = 0
100		self._gitHash = match["Hash"] if match["Hash"] is not None else match["Hash2"]
101		self._backend = match["Backend"]
102
103	@property
104	def Major(self) -> int:
105		return self._major
106
107	@property
108	def Minor(self) -> int:
109		return self._minor
110
111	@property
112	def Micro(self) -> int:
113		return self._micro
114
115	@property
116	def CommitsSinceLastTag(self) -> int:
117		return self._commitsSinceLastTag
118
119	@property
120	def GitHash(self) -> str:
121		return self._gitHash
122
123	@property
124	def Dirty(self) -> bool:
125		return self._dirty
126
127	@property
128	def Backend(self) -> str:
129		return self._backend
130
131	def __str__(self) -> str:
132		# dev = f"-dev" if self._dev else ""
133		return f"{self._major}.{self._minor}.{self._micro}"  # {dev}"
134
135	def __repr__(self) -> str:
136		return f"{self.__str__()} (Backend: {self._backend}; Git: {self._gitHash})"
137
138
139@export
140class NVC(Executable):
141	_EXECUTABLE_NAMES = {
142		"Darwin":  "nvc",
143		"FreeBSD": "nvc",
144		"Linux":   "nvc",
145		"Windows": "nvc.exe"
146	}
147
148	@CLIArgument()
149	class CommandHelp(LongCommand, name="help"):
150		"""Display usage summary."""
151
152	@CLIArgument()
153	class CommandVersion(LongCommand, name="version"):
154		"""Display version and copyright information."""
155
156	@CLIArgument()
157	class FlagSimulationHeapSize(ShortTupleFlag, name="H"):
158		"""
159		Set the maximum size in bytes of the simulation heap. This area of memory is used for temporary allocations during
160		process execution and dynamic allocations by the VHDL ‘new’ operator. The size parameter takes an optional k, m, or
161		g suffix to indicate kilobytes, megabytes, and gigabytes respectively. The default size is 16 megabytes.
162		"""
163
164	@CLIArgument()
165	class FlagIEEEWarnings(LongValuedFlag, name="ieee-warnings"):
166		"""
167		Enable or disable warning messages from the standard IEEE packages. The off-at-0 option disables warnings in the
168		first time step only. The default is warnings enabled.
169		"""
170
171	@CLIArgument()
172	class FlagIgnoreTime(LongFlag, name="ignore-time"):
173		"""
174		Do not check the timestamps of source files when the corresponding design unit is loaded from a library.
175		"""
176
177	@CLIArgument()
178	class FlagLibrarySearchPath(ShortTupleFlag, name="L"):
179		"""
180		Add path to the list of directories to search for libraries. See the LIBRARIES section below for details.
181		"""
182
183	@CLIArgument()
184	class FlagMaxMemorySize(ShortTupleFlag, name="M"):
185		"""
186		Set the maximum amount of memory in bytes used for the internal representations of design units.
187
188		The default is 16 megabytes but this may be insufficient when elaborating a large design. The size parameter takes
189		an optional k, m, or g suffix to indicate kilobytes, megabytes, and gigabytes respectively. For example -M64m for 64
190		megabytes.
191		"""
192
193	@CLIArgument()
194	class FlagLibraryMapping(LongKeyValueFlag, name="map", pattern="--{0}={1}:{2}"):
195		"""
196		Specify exactly the location of the logical library name. Libraries mapped in this way will not use the normal
197		search path.
198		"""
199
200	@CLIArgument()
201	class FlagMessages(LongValuedFlag, name="messages"):
202		"""
203		Select the format used for printing error and informational messages. The default full message format is designed
204		for readability whereas the compact messages can be easily parsed by tools.
205		"""
206
207	@CLIArgument()
208	class FlagRandomSeed(LongValuedFlag, name="seed"):
209		"""
210		Seed for random number generation. Affects the ‘get_random’ function in the ‘nvc.random’ package, the ``--shuffle``
211		option, and PSL union expressions.
212		"""
213
214	@CLIArgument()
215	class FlagVHDLStandard(LongValuedFlag, name="std"):
216		"""
217		Select the VHDL standard revision to use.
218
219		VHDL standard revisions are commonly referred to by the year they were published. For example IEEE 1076-1993 is
220		known as VHDL-93. Specify either the full year such as 1993 or just the last two digits such as ``93``. The
221		accepted revisions are 1993, 2000, 2002, 2008, 2019. Note there is very limited supported VHDL-2019 at present and
222		VHDL-87 is not supported. The default standard revision is VHDL-2008.
223		"""
224		_value: VHDLVersion
225
226		def __init__(self, value: VHDLVersion):
227			if value is None:
228				raise ValueError("")  # FIXME: add message
229
230			self._value = value
231
232		@property
233		def Value(self) -> VHDLVersion:
234			return self._value
235
236		@Value.setter
237		def Value(self, value: VHDLVersion) -> None:
238			if value is None:
239				raise ValueError("")  # FIXME: add message
240
241			self._value = value
242
243		def AsArgument(self) -> Union[str, Iterable[str]]:
244			if self._NAME is None:
245				raise ValueError("")  # FIXME: add message
246
247			return self._PATTERN.format(self._NAME, str(self._value)[-2:])
248
249	@CLIArgument()
250	class FlagRandomSeed(LongValuedFlag, name="stderr"):
251		"""
252		Print error messages with the given severity or higher to ‘stderr’ instead of ‘stdout’. The default is to print all
253		messages to ‘stderr’. Valid levels are note, warning, error, failure, and none.
254		"""
255
256	@CLIArgument()
257	class FlagLibrary(LongValuedFlag, name="work"):
258		"""
259		Use name as the work library. The second variant explicitly specifies the location of the library.
260		"""
261
262	@CLIArgument()
263	class CommandTCLScript(LongTupleFlag, name="do"):
264		"""
265		Evaluate a batch TCL script, optionally with *unit* loaded.
266		"""
267
268	@CLIArgument()
269	class CommandAnalyze(ShortFlag, name="a"):
270		"""Analyze VHDL source file(s)."""
271
272	@CLIArgument()
273	class OptionPaths(PathListArgument):
274		"""Add list of VHDL files to analyze."""
275
276	@CLIArgument()
277	class FlagRelaxed(LongFlag, name="relaxed"):
278		"""Relax some LRM rules."""
279
280	@CLIArgument()
281	class CommandElaborate(ShortTupleFlag, name="e"):
282		"""Elaborate design."""
283
284	@CLIArgument()
285	class CommandRun(ShortTupleFlag, name="r"):
286		"""Simulate design."""
287
288
289
290
291
292
293
294	def _CopyParameters(self, tool: "NVC") -> None:
295		for key in self.__cliParameters__:
296			if self._NeedsParameterInitialization(key):
297				value = self.__cliParameters__[key].Value
298				tool.__cliParameters__[key] = key(value)
299			else:
300				tool.__cliParameters__[key] = key()
301
302	def _SetParameters(self, tool: "NVC", std: Nullable[VHDLVersion] = None):
303		if std is not None:
304			tool[self.FlagVHDLStandard] = str(std)
305
306	def GetNVCAsAnalyzer(self, std: Nullable[VHDLVersion] = None) -> "NVC":
307		tool = NVC(executablePath=self._executablePath)
308
309		tool[tool.CommandAnalyze] = True
310		self._CopyParameters(tool)
311		self._SetParameters(tool, std)
312
313		return tool
314
315	def GetNVCAsElaborator(self, std: Nullable[VHDLVersion] = None) -> "NVC":
316		tool = NVC(executablePath=self._executablePath)
317
318		tool[tool.CommandElaborate] = True
319		self._CopyParameters(tool)
320		self._SetParameters(tool, std)
321
322		return tool
323
324	def GetNVCAsSimulator(self, std: Nullable[VHDLVersion] = None) -> "NVC":
325		tool = NVC(executablePath=self._executablePath)
326
327		tool[tool.CommandRun] = True
328		self._CopyParameters(tool)
329		self._SetParameters(tool, std)
330
331		return tool
332
333	def Help(self) -> str:
334		tool = NVC(executablePath=self._executablePath)
335
336		tool[tool.CommandHelp] = True
337
338		tool.StartProcess()
339		return "\n".join(tool.GetLineReader())
340
341	def Version(self) -> NVCVersion:
342		tool = NVC(executablePath=self._executablePath)
343
344		tool[tool.CommandVersion] = True
345
346		tool.StartProcess()
347		iterator = iter(tool.GetLineReader())
348		firstLine = next(iterator)
349
350		return NVCVersion(firstLine)