Coverage for pyEDAA / CLITool / NVC.py: 86%

157 statements  

« 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 2025-2026 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# 

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: 86 ↛ 87line 86 didn't jump to line 87 because the condition on line 86 was never true

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 _executableNames = { 

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. The default is 

187 16 megabytes but this may be insufficient when elaborating a large design. The size parameter takes an optional k, 

188 m, or g suffix to indicate kilobytes, megabytes, and gigabytes respectively. For example -M64m for 64 megabytes. 

189 """ 

190 

191 @CLIArgument() 

192 class FlagLibraryMapping(LongKeyValueFlag, name="map", pattern="--{0}={1}:{2}"): 

193 """ 

194 Specify exactly the location of the logical library name. Libraries mapped in this way will not use the normal 

195 search path. 

196 """ 

197 

198 @CLIArgument() 

199 class FlagMessages(LongValuedFlag, name="messages"): 

200 """ 

201 Select the format used for printing error and informational messages. The default full message format is designed 

202 for readability whereas the compact messages can be easily parsed by tools. 

203 """ 

204 

205 @CLIArgument() 

206 class FlagRandomSeed(LongValuedFlag, name="seed"): 

207 """ 

208 Seed for random number generation. Affects the ‘get_random’ function in the ‘nvc.random’ package, the ``--shuffle`` 

209 option, and PSL union expressions. 

210 """ 

211 

212 @CLIArgument() 

213 class FlagVHDLStandard(LongValuedFlag, name="std"): 

214 """ 

215 Select the VHDL standard revision to use. VHDL standard revisions are commonly referred to by the year they were 

216 published. For example IEEE 1076-1993 is known as VHDL-93. Specify either the full year such as 1993 or just the 

217 last two digits such as ``93``. The accepted revisions are 1993, 2000, 2002, 2008, 2019. Note there is very limited 

218 supported VHDL-2019 at present and VHDL-87 is not supported. The default standard revision is VHDL-2008. 

219 """ 

220 _value: VHDLVersion 

221 

222 def __init__(self, value: VHDLVersion): 

223 if value is None: 223 ↛ 224line 223 didn't jump to line 224 because the condition on line 223 was never true

224 raise ValueError(f"") # FIXME: add message 

225 

226 self._value = value 

227 

228 @property 

229 def Value(self) -> VHDLVersion: 

230 return self._value 

231 

232 @Value.setter 

233 def Value(self, value: VHDLVersion) -> None: 

234 if value is None: 

235 raise ValueError(f"") # FIXME: add message 

236 

237 self._value = value 

238 

239 def AsArgument(self) -> Union[str, Iterable[str]]: 

240 if self._name is None: 240 ↛ 241line 240 didn't jump to line 241 because the condition on line 240 was never true

241 raise ValueError(f"") # FIXME: add message 

242 

243 return self._pattern.format(self._name, str(self._value)[-2:]) 

244 

245 @CLIArgument() 

246 class FlagRandomSeed(LongValuedFlag, name="stderr"): 

247 """ 

248 Print error messages with the given severity or higher to ‘stderr’ instead of ‘stdout’. The default is to print all 

249 messages to ‘stderr’. Valid levels are note, warning, error, failure, and none. 

250 """ 

251 

252 @CLIArgument() 

253 class FlagLibrary(LongValuedFlag, name="work"): 

254 """ 

255 Use name as the work library. The second variant explicitly specifies the location of the library. 

256 """ 

257 

258 @CLIArgument() 

259 class CommandTCLScript(LongTupleFlag, name="do"): 

260 """ 

261 Evaluate a batch TCL script, optionally with *unit* loaded. 

262 """ 

263 

264 @CLIArgument() 

265 class CommandAnalyze(ShortFlag, name="a"): 

266 """Analyze VHDL source file(s).""" 

267 

268 @CLIArgument() 

269 class OptionPaths(PathListArgument): 

270 """Add list of VHDL files to analyze.""" 

271 

272 @CLIArgument() 

273 class FlagRelaxed(LongFlag, name="relaxed"): 

274 """Relax some LRM rules.""" 

275 

276 @CLIArgument() 

277 class CommandElaborate(ShortTupleFlag, name="e"): 

278 """Elaborate design.""" 

279 

280 @CLIArgument() 

281 class CommandRun(ShortTupleFlag, name="r"): 

282 """Simulate design.""" 

283 

284 

285 

286 

287 

288 

289 

290 def _CopyParameters(self, tool: "NVC") -> None: 

291 for key in self.__cliParameters__: 

292 if self._NeedsParameterInitialization(key): 

293 value = self.__cliParameters__[key].Value 

294 tool.__cliParameters__[key] = key(value) 

295 else: 

296 tool.__cliParameters__[key] = key() 

297 

298 def _SetParameters(self, tool: "NVC", std: Nullable[VHDLVersion] = None): 

299 if std is not None: 299 ↛ 300line 299 didn't jump to line 300 because the condition on line 299 was never true

300 tool[self.FlagVHDLStandard] = str(std) 

301 

302 def GetNVCAsAnalyzer(self, std: Nullable[VHDLVersion] = None) -> "NVC": 

303 tool = NVC(executablePath=self._executablePath) 

304 

305 tool[tool.CommandAnalyze] = True 

306 self._CopyParameters(tool) 

307 self._SetParameters(tool, std) 

308 

309 return tool 

310 

311 def GetNVCAsElaborator(self, std: Nullable[VHDLVersion] = None) -> "NVC": 

312 tool = NVC(executablePath=self._executablePath) 

313 

314 tool[tool.CommandElaborate] = True 

315 self._CopyParameters(tool) 

316 self._SetParameters(tool, std) 

317 

318 return tool 

319 

320 def GetNVCAsSimulator(self, std: Nullable[VHDLVersion] = None) -> "NVC": 

321 tool = NVC(executablePath=self._executablePath) 

322 

323 tool[tool.CommandRun] = True 

324 self._CopyParameters(tool) 

325 self._SetParameters(tool, std) 

326 

327 return tool 

328 

329 def Help(self) -> str: 

330 tool = NVC(executablePath=self._executablePath) 

331 

332 tool[tool.CommandHelp] = True 

333 

334 tool.StartProcess() 

335 return "\n".join(tool.GetLineReader()) 

336 

337 def Version(self) -> NVCVersion: 

338 tool = NVC(executablePath=self._executablePath) 

339 

340 tool[tool.CommandVersion] = True 

341 

342 tool.StartProcess() 

343 iterator = iter(tool.GetLineReader()) 

344 firstLine = next(iterator) 

345 

346 return NVCVersion(firstLine)