+ 1# ==================================================================================================================== #
+ 2# _____ ____ _ _ ____ _ ___ _____ _ #
+ 3# _ __ _ _| ____| _ \ / \ / \ / ___| | |_ _|_ _|__ ___ | | #
+ 4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | | | | | |/ _ \ / _ \| | #
+ 5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |___| |___ | | | | (_) | (_) | | #
+ 6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)____|_____|___| |_|\___/ \___/|_| #
+ 7# |_| |___/ #
+ 8# ==================================================================================================================== #
+ 9# Authors: #
+ 10# Patrick Lehmann #
+ 11# #
+ 12# License: #
+ 13# ==================================================================================================================== #
+ 14# Copyright 2017-2023 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"""Unit tests for executable ``ghdl``."""
+ 32from sys import platform as sys_platform
+ 33from os import getenv as os_getenv
+ 34from pathlib import Path
+ 35from unittest import TestCase
+ 36
+ 37from pyEDAA.CLITool.GHDL import GHDL
+ 38from . import Helper
+ 39
+ 40
+ 41class CommonOptions(TestCase, Helper):
+ 42 _binaryDirectoryPath = Path(os_getenv("GHDL_PREFIX", default="/usr/local")) / "bin"
+ 43
+ 44 @classmethod
+ 45 def setUpClass(cls) -> None:
+ 46 print(f"\nPlatform: {sys_platform}")
+ 47 if sys_platform in ("linux", "darwin"): 47 ↛ exitline 47 didn't return from function 'setUpClass', because the condition on line 47 was never false
+ 48 ghdlBinaryPath: Path = cls._binaryDirectoryPath / "ghdl"
+ 49 print(f"Creating dummy file '{ghdlBinaryPath}': ", end="")
+ 50 ghdlBinaryPath.touch()
+ 51 print(f"DONE" if ghdlBinaryPath.exists() else f"FAILED")
+ 52
+ 53 def test_Help(self):
+ 54 tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
+ 55 tool[tool.CommandHelp] = True
+ 56
+ 57 executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
+ 58 self.assertEqual(f"[\"{executable}\", \"help\"]", repr(tool))
+ 59
+ 60 def test_Version(self):
+ 61 tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
+ 62 tool[tool.CommandVersion] = True
+ 63
+ 64 executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
+ 65 self.assertEqual(f"[\"{executable}\", \"version\"]", repr(tool))
+ 66
+ 67
+ 68class Analyze(TestCase, Helper):
+ 69 _binaryDirectoryPath = Path(os_getenv("GHDL_PREFIX", default="/usr/local")) / "bin"
+ 70
+ 71 @classmethod
+ 72 def setUpClass(cls) -> None:
+ 73 print(f"\nPlatform: {sys_platform}")
+ 74 if sys_platform in ("linux", "darwin"): 74 ↛ exitline 74 didn't return from function 'setUpClass', because the condition on line 74 was never false
+ 75 ghdlBinaryPath: Path = cls._binaryDirectoryPath / "ghdl"
+ 76 print(f"Creating dummy file '{ghdlBinaryPath}': ", end="")
+ 77 ghdlBinaryPath.touch()
+ 78 print(f"DONE" if ghdlBinaryPath.exists() else f"FAILED")
+ 79
+ 80 def test_AnalyzeFile(self):
+ 81 tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
+ 82 tool[tool.CommandAnalyze] = True
+ 83 tool[tool.FlagVHDLStandard] = "08"
+ 84 tool[tool.FlagSynopsys] = True
+ 85 tool[tool.FlagRelaxed] = True
+ 86 tool[tool.FlagExplicit] = True
+ 87 tool[tool.FlagMultiByteComments] = True
+ 88 tool[tool.FlagLibrary] = "lib_Test"
+ 89
+ 90 executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
+ 91 self.assertEqual(f"[\"{executable}\", \"analyze\", \"--std=08\", \"-fsynopsys\", \"-frelaxed\", \"-fexplicit\", \"--work=lib_Test\", \"--mb-comments\"]", repr(tool))
+ 92
+ 93 def test_DeriveAnalyzer(self):
+ 94 tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
+ 95 tool[tool.FlagVHDLStandard] = "08"
+ 96 tool[tool.FlagSynopsys] = True
+ 97 tool[tool.FlagRelaxed] = True
+ 98 tool[tool.FlagExplicit] = True
+ 99 tool[tool.FlagMultiByteComments] = True
+ 100
+ 101 derived = tool.GetGHDLAsAnalyzer()
+ 102 derived[derived.FlagLibrary] = "lib_Test"
+ 103
+ 104 executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
+ 105 self.assertEqual(f"[\"{executable}\", \"analyze\", \"--std=08\", \"-fsynopsys\", \"-frelaxed\", \"-fexplicit\", \"--work=lib_Test\", \"--mb-comments\"]", repr(derived))
+
+
+ 1# ==================================================================================================================== #
+ 2# _____ ____ _ _ ____ _ ___ _____ _ #
+ 3# _ __ _ _| ____| _ \ / \ / \ / ___| | |_ _|_ _|__ ___ | | #
+ 4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | | | | | |/ _ \ / _ \| | #
+ 5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |___| |___ | | | | (_) | (_) | | #
+ 6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)____|_____|___| |_|\___/ \___/|_| #
+ 7# |_| |___/ #
+ 8# ==================================================================================================================== #
+ 9# Authors: #
+ 10# Patrick Lehmann #
+ 11# #
+ 12# License: #
+ 13# ==================================================================================================================== #
+ 14# Copyright 2017-2023 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 typing import Union, Iterable
+ 34
+ 35from pyTooling.Decorators import export
+ 36from pyVHDLModel import VHDLVersion
+ 37
+ 38from pyTooling.CLIAbstraction import CLIArgument, Executable
+ 39from pyTooling.CLIAbstraction.Argument import PathListArgument, StringArgument
+ 40from pyTooling.CLIAbstraction.Command import CommandArgument
+ 41from pyTooling.CLIAbstraction.Flag import ShortFlag, LongFlag
+ 42from pyTooling.CLIAbstraction.BooleanFlag import LongBooleanFlag
+ 43from pyTooling.CLIAbstraction.ValuedFlag import ShortValuedFlag, LongValuedFlag
+ 44from pyTooling.CLIAbstraction.KeyValueFlag import ShortKeyValueFlag
+ 45
+ 46
+ 47@export
+ 48class GHDL(Executable):
+ 49 _executableNames = {
+ 50 "Darwin": "ghdl",
+ 51 "Linux": "ghdl",
+ 52 "Windows": "ghdl.exe"
+ 53 }
+ 54
+ 55 # XXX: overwrite __init__ and get backend variant
+ 56 # XXX: check for compatible backends
+ 57
+ 58 @CLIArgument()
+ 59 class CommandHelp(CommandArgument, name="help"):
+ 60 """Print help page(s)."""
+ 61
+ 62 @CLIArgument()
+ 63 class CommandVersion(CommandArgument, name="version"):
+ 64 """Print version information."""
+ 65
+ 66 @CLIArgument()
+ 67 class CommandSyntax(CommandArgument, name="syntax"):
+ 68 """Check syntax."""
+ 69
+ 70 @CLIArgument()
+ 71 class CommandElaborationOrder(CommandArgument, name="elab-order"):
+ 72 """Display (elaboration) ordered source files."""
+ 73
+ 74 @CLIArgument()
+ 75 class CommandAnalyze(CommandArgument, name="analyze"):
+ 76 """Analyze VHDL source file(s)."""
+ 77
+ 78 @CLIArgument()
+ 79 class CommandElaborate(CommandArgument, name="elaborate"):
+ 80 """Elaborate design."""
+ 81
+ 82 @CLIArgument()
+ 83 class CommandElaborationAndRun(CommandArgument, name="elab-run"):
+ 84 """Elaborate and simulate design."""
+ 85
+ 86 @CLIArgument()
+ 87 class CommandRun(CommandArgument, name="run"):
+ 88 """Simulate design."""
+ 89
+ 90 @CLIArgument()
+ 91 class CommandBind(CommandArgument, name="bind"):
+ 92 """Bind design unit."""
+ 93
+ 94 @CLIArgument()
+ 95 class CommandLink(CommandArgument, name="link"):
+ 96 """Link design unit."""
+ 97
+ 98 @CLIArgument()
+ 99 class CommandListLink(CommandArgument, name="list-link"):
+ 100 """List objects file to link a design unit."""
+ 101
+ 102 @CLIArgument()
+ 103 class CommandCompile(CommandArgument, name="compile"):
+ 104 """Generate whole sequence to elaborate design from files."""
+ 105
+ 106 @CLIArgument()
+ 107 class CommandGenerateDependencies(CommandArgument, name="gen-depends"):
+ 108 """Generate dependencies of design."""
+ 109
+ 110 @CLIArgument()
+ 111 class CommandSynthesize(CommandArgument, name="synth"):
+ 112 """Synthesis from design unit."""
+ 113
+ 114 @CLIArgument()
+ 115 class FlagVerbose(ShortFlag, name="v"):
+ 116 """Run in verbose mode (print more messages)."""
+ 117
+ 118 # Analyze and elaborate options
+ 119 @CLIArgument()
+ 120 class FlagVHDLStandard(LongValuedFlag, name="std"):
+ 121 """Set the used VHDL standard version."""
+ 122 _value: VHDLVersion
+ 123
+ 124 def __init__(self, value: VHDLVersion):
+ 125 if value is None: 125 ↛ 126line 125 didn't jump to line 126, because the condition on line 125 was never true
+ 126 raise ValueError(f"") # XXX: add message
+ 127
+ 128 self._value = value
+ 129
+ 130 @property
+ 131 def Value(self) -> VHDLVersion:
+ 132 return self._value
+ 133
+ 134 @Value.setter
+ 135 def Value(self, value: VHDLVersion) -> None:
+ 136 if value is None:
+ 137 raise ValueError(f"") # XXX: add message
+ 138
+ 139 self._value = value
+ 140
+ 141 def AsArgument(self) -> Union[str, Iterable[str]]:
+ 142 if self._name is None: 142 ↛ 143line 142 didn't jump to line 143, because the condition on line 142 was never true
+ 143 raise ValueError(f"") # XXX: add message
+ 144
+ 145 return self._pattern.format(self._name, str(self._value)[-2:])
+ 146
+ 147 @CLIArgument()
+ 148 class FlagIEEEFlavor(LongValuedFlag, name="ieee"):
+ 149 """Set the used VHDL flavor."""
+ 150
+ 151 @CLIArgument()
+ 152 class FlagSynopsys(ShortFlag, name="fsynopsys"):
+ 153 """Set used VHDL flavor to *Synopsys* and make Synopsys packages visible in library ``ìeee``."""
+ 154
+ 155 @CLIArgument()
+ 156 class FlagRelaxed(ShortFlag, name="frelaxed"):
+ 157 """Relax some LRM rules."""
+ 158
+ 159 @CLIArgument()
+ 160 class FlagExplicit(ShortFlag, name="fexplicit"): ...
+ 161
+ 162 @CLIArgument()
+ 163 class FlagLibrary(LongValuedFlag, name="work"):
+ 164 """Set working library."""
+ 165
+ 166 @CLIArgument()
+ 167 class FlagWorkingDirectory(LongValuedFlag, name="workdir"):
+ 168 """Set working directory."""
+ 169
+ 170 @CLIArgument()
+ 171 class FlagMultiByteComments(LongFlag, name="mb-comments"):
+ 172 """Allow multi-byte comments."""
+ 173
+ 174 @CLIArgument()
+ 175 class FlagSyntesisBindingRule(LongFlag, name="syn-binding"):
+ 176 """Enable synthesis binding rule."""
+ 177
+ 178 @CLIArgument()
+ 179 class FlagSearchPath(ShortValuedFlag, name="P", pattern="-{0}{1}"):
+ 180 """Add search path."""
+ 181
+ 182 @CLIArgument()
+ 183 class FlagTimeResolution(LongValuedFlag, name="time-resolution"):
+ 184 """Set base time resolution.
+ 185
+ 186 Allowed values are ``auto`` (default), ``fs``, ``ps``, ``ns``, ``us``, ``ms`` or ``sec``.
+ 187 """
+ 188
+ 189 @CLIArgument()
+ 190 class FlagVitalChecks(LongBooleanFlag, name="vital-checks", pattern="-{0}", falsePattern="--no-{0}"):
+ 191 """Check VITAL restrictions."""
+ 192
+ 193 @CLIArgument()
+ 194 class FlagWarnUnboundComponents(ShortFlag, name="binding", pattern="-W{0}"):
+ 195 """Warns for unbound components."""
+ 196
+ 197 @CLIArgument()
+ 198 class FlagWarnReservedWords(ShortFlag, name="reserved", pattern="-W{0}"):
+ 199 """Warns if VHDL'93 reserved words are used in VHDL'87."""
+ 200
+ 201 @CLIArgument()
+ 202 class FlagWarnRedefinedDesignUnits(ShortFlag, name="library", pattern="-W{0}"):
+ 203 """Warns for redefined design unit."""
+ 204
+ 205 @CLIArgument()
+ 206 class FlagWarnNonVitalGenericNames(ShortFlag, name="vital-generic", pattern="-W{0}"):
+ 207 """Warns of non-vital generic names."""
+ 208
+ 209 @CLIArgument()
+ 210 class FlagWarnElaborationChecks(ShortFlag, name="delayed-checks", pattern="-W{0}"):
+ 211 """Warns for checks performed at elaboration."""
+ 212
+ 213 @CLIArgument()
+ 214 class FlagWarnUnnecessaryPackageBody(ShortFlag, name="body", pattern="-W{0}"):
+ 215 """Warns for unnecessary package body."""
+ 216
+ 217 @CLIArgument()
+ 218 class FlagWarnOthersSpecifications(ShortFlag, name="specs", pattern="-W{0}"):
+ 219 """Warns if an all/others specification does not apply."""
+ 220
+ 221 @CLIArgument()
+ 222 class FlagSyntesisBindingRule(ShortFlag, name="unused", pattern="-W{0}"):
+ 223 """Warns for unused subprograms."""
+ 224
+ 225 @CLIArgument()
+ 226 class FlagSyntesisBindingRule(ShortFlag, name="error", pattern="-W{0}"):
+ 227 """Turns warnings into errors."""
+ 228
+ 229 @CLIArgument()
+ 230 class OptionPath(PathListArgument):
+ 231 """Add VHDL file to analyze."""
+ 232
+ 233 @CLIArgument()
+ 234 class OptionTopLevel(StringArgument):
+ 235 """Specify the toplevel design unit."""
+ 236
+ 237 @CLIArgument()
+ 238 class OptionArchitecture(StringArgument):
+ 239 """Specify the architecture name, if the toplevel design unit is an entity."""
+ 240
+ 241 @CLIArgument()
+ 242 class FlagGenerics(ShortKeyValueFlag, pattern="-{0}{1}={2}"):
+ 243 """Set a generic value."""
+ 244
+ 245 @CLIArgument()
+ 246 class FlagAsserts(ShortValuedFlag, name="asserts"):
+ 247 """Select how assertions are handled.
+ 248
+ 249 It can be ``enable`` (the default), ``disable`` which disables all assertions and ``disable-at-0`` which disables
+ 250 only at the start of simulation.
+ 251 """
+ 252
+ 253 @CLIArgument()
+ 254 class FlagIEEEAsserts(ShortValuedFlag, name="ieee-asserts"):
+ 255 """Select how assertions are handled.
+ 256
+ 257 It can be ``enable`` (the default), ``disable`` which disables all assertions and ``disable-at-0`` which disables
+ 258 only at the start of simulation.
+ 259 """
+ 260
+ 261 @CLIArgument()
+ 262 class FlagStopTime(ShortValuedFlag, name="stop-time"):
+ 263 """Stop the simulation after a given simulation time.
+ 264
+ 265 The time is expressed as a time value, without any spaces. The time is the simulation time, not the real execution time.
+ 266 """
+ 267
+ 268 @CLIArgument()
+ 269 class FlagMaxDeltaCycles(ShortValuedFlag, name="stop-delta"):
+ 270 """Stop the simulation after N delta cycles in the same current time."""
+ 271
+ 272 @CLIArgument()
+ 273 class FlagDisplayDeltaCycles(ShortValuedFlag, name="disp-time"):
+ 274 """Display the time and delta cycle number as simulation advances."""
+ 275
+ 276 @CLIArgument()
+ 277 class FlagUnbufferedIO(ShortValuedFlag, name="unbuffered"):
+ 278 """Disable buffering on STDOUT, STDERR and files opened in write or append mode (TEXTIO)."""
+ 279
+ 280 @CLIArgument()
+ 281 class FlagReadWaveformOptionsFile(ShortValuedFlag, name="read-wave-opt"):
+ 282 """Filter signals to be dumped to the waveform file according to the wavefile option file provided."""
+ 283
+ 284 @CLIArgument()
+ 285 class FlagWriteWaveformOptionsFile(ShortValuedFlag, name="write-wave-opt"):
+ 286 """If the wavefile option file doesn’t exist, creates it with all the signals of the design.
+ 287 Otherwise, it throws an error, because it won’t erase an existing file.
+ 288 """
+ 289
+ 290 @CLIArgument()
+ 291 class FlagGHWWaveformFile(ShortValuedFlag, name="wave"):
+ 292 """Write the waveforms into a GHDL Waveform (``*.ghw``) file.
+ 293
+ 294 Contrary to VCD files, any VHDL type can be dumped into a GHW file.
+ 295 """
+ 296
+ 297 def _CopyParameters(self, tool: "GHDL") -> None:
+ 298 for key in self.__cliParameters__:
+ 299 if self._NeedsParameterInitialization(key):
+ 300 value = self.__cliParameters__[key].Value
+ 301 tool.__cliParameters__[key] = key(value)
+ 302 else:
+ 303 tool.__cliParameters__[key] = key()
+ 304
+ 305 def _SetParameters(self, tool: "GHDL", std: VHDLVersion = None, ieee: str = None):
+ 306 if std is not None: 306 ↛ 307line 306 didn't jump to line 307, because the condition on line 306 was never true
+ 307 tool[self.FlagVHDLStandard] = str(std)
+ 308
+ 309 if ieee is not None: 309 ↛ 310line 309 didn't jump to line 310, because the condition on line 309 was never true
+ 310 tool[self.FlagVHDLStandard] = ieee
+ 311
+ 312 def GetGHDLAsAnalyzer(self, std: VHDLVersion = None, ieee: str = None):
+ 313 tool = GHDL(executablePath=self._executablePath)
+ 314
+ 315 tool[tool.CommandAnalyze] = True
+ 316 self._CopyParameters(tool)
+ 317 self._SetParameters(tool, std, ieee)
+ 318
+ 319 return tool
+ 320
+ 321 def GetGHDLAsElaborator(self, std: VHDLVersion = None, ieee: str = None):
+ 322 tool = GHDL(executablePath=self._executablePath)
+ 323
+ 324 tool[tool.CommandElaborate] = True
+ 325 self._CopyParameters(tool)
+ 326 self._SetParameters(tool, std, ieee)
+ 327
+ 328 return tool
+ 329
+ 330 def GetGHDLAsSimulator(self, std: VHDLVersion = None, ieee: str = None):
+ 331 tool = GHDL(executablePath=self._executablePath)
+ 332
+ 333 tool[tool.CommandRun] = True
+ 334 self._CopyParameters(tool)
+ 335 self._SetParameters(tool, std, ieee)
+ 336
+ 337 return tool
+
+