cocotb 1.9.2__cp313-cp313-win_amd64.whl → 2.0.0b1__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cocotb might be problematic. Click here for more details.
- cocotb/{ANSI.py → _ANSI.py} +5 -25
- cocotb/__init__.py +76 -332
- cocotb/_base_triggers.py +513 -0
- cocotb/_bridge.py +187 -0
- cocotb/_decorators.py +515 -0
- cocotb/_deprecation.py +3 -3
- cocotb/_exceptions.py +7 -0
- cocotb/_extended_awaitables.py +419 -0
- cocotb/_gpi_triggers.py +382 -0
- cocotb/_init.py +295 -0
- cocotb/_outcomes.py +54 -0
- cocotb/_profiling.py +46 -0
- cocotb/_py_compat.py +100 -29
- cocotb/_scheduler.py +454 -0
- cocotb/_test.py +245 -0
- cocotb/_test_factory.py +309 -0
- cocotb/_test_functions.py +42 -0
- cocotb/_typing.py +7 -0
- cocotb/_utils.py +296 -0
- cocotb/_version.py +3 -7
- cocotb/_xunit_reporter.py +66 -0
- cocotb/clock.py +271 -108
- cocotb/handle.py +1342 -795
- cocotb/libs/cocotb.dll +0 -0
- cocotb/libs/cocotb.exp +0 -0
- cocotb/libs/cocotb.lib +0 -0
- cocotb/libs/cocotbfli_modelsim.dll +0 -0
- cocotb/libs/cocotbfli_modelsim.exp +0 -0
- cocotb/libs/cocotbfli_modelsim.lib +0 -0
- cocotb/libs/cocotbutils.dll +0 -0
- cocotb/libs/cocotbutils.exp +0 -0
- cocotb/libs/cocotbutils.lib +0 -0
- cocotb/libs/cocotbvhpi_aldec.dll +0 -0
- cocotb/libs/cocotbvhpi_aldec.exp +0 -0
- cocotb/libs/cocotbvhpi_aldec.lib +0 -0
- cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
- cocotb/libs/cocotbvpi_aldec.dll +0 -0
- cocotb/libs/cocotbvpi_aldec.exp +0 -0
- cocotb/libs/cocotbvpi_aldec.lib +0 -0
- cocotb/libs/cocotbvpi_ghdl.dll +0 -0
- cocotb/libs/cocotbvpi_ghdl.exp +0 -0
- cocotb/libs/cocotbvpi_ghdl.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.exp +0 -0
- cocotb/libs/cocotbvpi_icarus.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.vpl +0 -0
- cocotb/libs/cocotbvpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvpi_modelsim.lib +0 -0
- cocotb/libs/embed.dll +0 -0
- cocotb/libs/embed.exp +0 -0
- cocotb/libs/embed.lib +0 -0
- cocotb/libs/gpi.dll +0 -0
- cocotb/libs/gpi.exp +0 -0
- cocotb/libs/gpi.lib +0 -0
- cocotb/libs/gpilog.dll +0 -0
- cocotb/libs/gpilog.exp +0 -0
- cocotb/libs/gpilog.lib +0 -0
- cocotb/libs/pygpilog.dll +0 -0
- cocotb/libs/pygpilog.exp +0 -0
- cocotb/libs/pygpilog.lib +0 -0
- cocotb/{log.py → logging.py} +105 -110
- cocotb/queue.py +103 -57
- cocotb/regression.py +667 -712
- cocotb/result.py +17 -188
- cocotb/share/def/aldec.exp +0 -0
- cocotb/share/def/aldec.lib +0 -0
- cocotb/share/def/ghdl.exp +0 -0
- cocotb/share/def/ghdl.lib +0 -0
- cocotb/share/def/icarus.exp +0 -0
- cocotb/share/def/icarus.lib +0 -0
- cocotb/share/def/modelsim.def +1 -0
- cocotb/share/def/modelsim.exp +0 -0
- cocotb/share/def/modelsim.lib +0 -0
- cocotb/share/include/cocotb_utils.h +6 -29
- cocotb/share/include/embed.h +5 -28
- cocotb/share/include/gpi.h +137 -92
- cocotb/share/include/gpi_logging.h +221 -142
- cocotb/share/include/py_gpi_logging.h +7 -4
- cocotb/share/include/vpi_user_ext.h +4 -26
- cocotb/share/lib/verilator/verilator.cpp +59 -54
- cocotb/simulator.cp313-win_amd64.exp +0 -0
- cocotb/simulator.cp313-win_amd64.lib +0 -0
- cocotb/simulator.cp313-win_amd64.pyd +0 -0
- cocotb/simulator.pyi +107 -0
- cocotb/task.py +434 -212
- cocotb/triggers.py +55 -1092
- cocotb/types/__init__.py +25 -47
- cocotb/types/_abstract_array.py +151 -0
- cocotb/types/_array.py +264 -0
- cocotb/types/_logic.py +296 -0
- cocotb/types/_logic_array.py +834 -0
- cocotb/types/{range.py → _range.py} +36 -44
- cocotb/types/_resolve.py +76 -0
- cocotb/utils.py +119 -587
- cocotb-2.0.0b1.dist-info/METADATA +60 -0
- cocotb-2.0.0b1.dist-info/RECORD +143 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0b1.dist-info}/WHEEL +1 -1
- cocotb-2.0.0b1.dist-info/entry_points.txt +2 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0b1.dist-info}/top_level.txt +1 -0
- cocotb_tools/__init__.py +0 -0
- cocotb_tools/_coverage.py +33 -0
- cocotb_tools/_vendor/__init__.py +3 -0
- cocotb_tools/check_results.py +65 -0
- cocotb_tools/combine_results.py +152 -0
- cocotb_tools/config.py +241 -0
- {cocotb → cocotb_tools}/ipython_support.py +29 -22
- cocotb_tools/makefiles/Makefile.deprecations +27 -0
- {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +82 -54
- {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +8 -33
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
- cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
- cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
- cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
- cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
- cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
- cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
- cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
- cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
- cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
- cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
- cocotb_tools/py.typed +0 -0
- {cocotb → cocotb_tools}/runner.py +794 -361
- cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
- pygpi/entry.py +34 -17
- pygpi/py.typed +0 -0
- cocotb/binary.py +0 -858
- cocotb/config.py +0 -289
- cocotb/decorators.py +0 -332
- cocotb/memdebug.py +0 -35
- cocotb/outcomes.py +0 -56
- cocotb/scheduler.py +0 -1099
- cocotb/share/makefiles/Makefile.deprecations +0 -12
- cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
- cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
- cocotb/share/makefiles/simulators/Makefile.ius +0 -125
- cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
- cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
- cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
- cocotb/types/array.py +0 -309
- cocotb/types/logic.py +0 -292
- cocotb/types/logic_array.py +0 -298
- cocotb/wavedrom.py +0 -199
- cocotb/xunit_reporter.py +0 -80
- cocotb-1.9.2.dist-info/METADATA +0 -168
- cocotb-1.9.2.dist-info/RECORD +0 -121
- cocotb-1.9.2.dist-info/entry_points.txt +0 -2
- /cocotb/{_vendor/__init__.py → py.typed} +0 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0b1.dist-info/licenses}/LICENSE +0 -0
- {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
# TODO: support timescale on all simulators
|
|
10
10
|
# TODO: support custom dependencies
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import logging
|
|
13
|
+
import multiprocessing
|
|
13
14
|
import os
|
|
14
15
|
import re
|
|
15
16
|
import shlex
|
|
@@ -18,41 +19,86 @@ import subprocess
|
|
|
18
19
|
import sys
|
|
19
20
|
import tempfile
|
|
20
21
|
import warnings
|
|
22
|
+
from abc import ABC, abstractmethod
|
|
21
23
|
from contextlib import suppress
|
|
22
24
|
from pathlib import Path
|
|
23
|
-
from typing import
|
|
24
|
-
|
|
25
|
+
from typing import (
|
|
26
|
+
Dict,
|
|
27
|
+
Iterable,
|
|
28
|
+
List,
|
|
29
|
+
Mapping,
|
|
30
|
+
Optional,
|
|
31
|
+
Sequence,
|
|
32
|
+
TextIO,
|
|
33
|
+
Tuple,
|
|
34
|
+
Type,
|
|
35
|
+
Union,
|
|
36
|
+
)
|
|
25
37
|
|
|
26
38
|
import find_libpython
|
|
27
39
|
|
|
28
|
-
import
|
|
40
|
+
import cocotb_tools.config
|
|
41
|
+
from cocotb_tools.check_results import get_results
|
|
42
|
+
from cocotb_tools.sim_versions import NvcVersion
|
|
29
43
|
|
|
30
|
-
PathLike = Union["os.PathLike[str]", str]
|
|
31
|
-
|
|
32
|
-
Timescale = Tuple[str, str]
|
|
44
|
+
PathLike = Union["os.PathLike[str]", str] # TODO use TypeAlias in Python 3.10
|
|
45
|
+
"A path that can be passed to :class:`pathlib.Path` or :func:`open`"
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
"Python runners and associated APIs are an experimental feature and subject to change.",
|
|
36
|
-
UserWarning,
|
|
37
|
-
stacklevel=2,
|
|
38
|
-
)
|
|
47
|
+
_Command = List[str]
|
|
39
48
|
|
|
40
49
|
_magic_re = re.compile(r"([\\{}])")
|
|
41
50
|
_space_re = re.compile(r"([\s])", re.ASCII)
|
|
42
51
|
|
|
43
52
|
|
|
44
|
-
|
|
53
|
+
MAX_PARALLEL_BUILD_JOBS: int = 4
|
|
54
|
+
"""The maximum number of parallel build threads in calls to :meth:`.Runner.build`.
|
|
55
|
+
|
|
56
|
+
If the number of CPU cores is less than this value, it uses the CPU core count.
|
|
57
|
+
Set this variable to globally change the number of parallel build jobs.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _get_max_parallel_build_jobs() -> int:
|
|
62
|
+
return min(MAX_PARALLEL_BUILD_JOBS, multiprocessing.cpu_count())
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _as_tcl_value(value: str) -> str:
|
|
45
66
|
# add '\' before special characters and spaces
|
|
46
67
|
value = _magic_re.sub(r"\\\1", value)
|
|
47
68
|
value = value.replace("\n", r"\n")
|
|
48
69
|
value = _space_re.sub(r"\\\1", value)
|
|
49
|
-
if value[
|
|
70
|
+
if value[:1] == '"':
|
|
50
71
|
value = "\\" + value
|
|
51
72
|
|
|
52
73
|
return value
|
|
53
74
|
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
_sv_escapes = {
|
|
77
|
+
"\n": "\\n",
|
|
78
|
+
"\t": "\\t",
|
|
79
|
+
"\\": "\\\\",
|
|
80
|
+
'"': '\\"',
|
|
81
|
+
"\v": "\\v",
|
|
82
|
+
"\f": "\\f",
|
|
83
|
+
"\xff": "\\xFF",
|
|
84
|
+
}
|
|
85
|
+
for i in range(32):
|
|
86
|
+
if chr(i) not in _sv_escapes:
|
|
87
|
+
_sv_escapes[chr(i)] = f"\\x{i:02x}"
|
|
88
|
+
|
|
89
|
+
_sv_escape_translate_table = str.maketrans(_sv_escapes)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _as_sv_literal(value: str) -> str:
|
|
93
|
+
if isinstance(value, (int, float)):
|
|
94
|
+
return str(value)
|
|
95
|
+
elif isinstance(value, str):
|
|
96
|
+
return '"' + value.translate(_sv_escape_translate_table) + '"'
|
|
97
|
+
else:
|
|
98
|
+
raise TypeError("Can't serialize this type as an SV literal")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _shlex_join(split_command: Iterable[str]) -> str:
|
|
56
102
|
"""
|
|
57
103
|
Return a shell-escaped string from *split_command*
|
|
58
104
|
This is here more for compatibility purposes
|
|
@@ -61,19 +107,17 @@ def shlex_join(split_command):
|
|
|
61
107
|
|
|
62
108
|
|
|
63
109
|
class VHDL(str):
|
|
64
|
-
"""Tags source files and build arguments to :meth
|
|
110
|
+
"""Tags source files and build arguments to :meth:`Runner.build() <cocotb_tools.runner.Runner.build>` as VHDL-specific."""
|
|
65
111
|
|
|
66
112
|
|
|
67
113
|
class Verilog(str):
|
|
68
|
-
"""Tags source files and build arguments to :meth
|
|
114
|
+
"""Tags source files and build arguments to :meth:`Runner.build() <cocotb_tools.runner.Runner.build>` as Verilog-specific."""
|
|
69
115
|
|
|
70
116
|
|
|
71
|
-
class
|
|
72
|
-
|
|
117
|
+
class Runner(ABC):
|
|
73
118
|
supported_gpi_interfaces: Dict[str, List[str]] = {}
|
|
74
119
|
|
|
75
120
|
def __init__(self) -> None:
|
|
76
|
-
|
|
77
121
|
self._simulator_in_path()
|
|
78
122
|
|
|
79
123
|
self.env: Dict[str, str] = {}
|
|
@@ -82,7 +126,10 @@ class Simulator(abc.ABC):
|
|
|
82
126
|
self.build_dir: Path = get_abs_path("sim_build")
|
|
83
127
|
self.parameters: Mapping[str, object] = {}
|
|
84
128
|
|
|
85
|
-
|
|
129
|
+
self.log = logging.getLogger(type(self).__qualname__)
|
|
130
|
+
self.log.setLevel(logging.INFO)
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
86
133
|
def _simulator_in_path(self) -> None:
|
|
87
134
|
"""Raise exception if the simulator executable does not exist in :envvar:`PATH`.
|
|
88
135
|
|
|
@@ -90,8 +137,6 @@ class Simulator(abc.ABC):
|
|
|
90
137
|
SystemExit: Simulator executable does not exist in :envvar:`PATH`.
|
|
91
138
|
"""
|
|
92
139
|
|
|
93
|
-
raise NotImplementedError()
|
|
94
|
-
|
|
95
140
|
def _check_hdl_toplevel_lang(self, hdl_toplevel_lang: Optional[str]) -> str:
|
|
96
141
|
"""Return *hdl_toplevel_lang* if supported by simulator, raise exception otherwise.
|
|
97
142
|
|
|
@@ -120,12 +165,12 @@ class Simulator(abc.ABC):
|
|
|
120
165
|
else:
|
|
121
166
|
lang = hdl_toplevel_lang
|
|
122
167
|
|
|
123
|
-
if lang in self.supported_gpi_interfaces
|
|
168
|
+
if lang in self.supported_gpi_interfaces:
|
|
124
169
|
return lang
|
|
125
170
|
else:
|
|
126
171
|
raise ValueError(
|
|
127
172
|
f"{type(self).__qualname__}: hdl_toplevel_lang {hdl_toplevel_lang!r} is not "
|
|
128
|
-
f"in supported list: {', '.join(self.supported_gpi_interfaces
|
|
173
|
+
f"in supported list: {', '.join(self.supported_gpi_interfaces)}"
|
|
129
174
|
)
|
|
130
175
|
|
|
131
176
|
def _set_env(self) -> None:
|
|
@@ -142,23 +187,28 @@ class Simulator(abc.ABC):
|
|
|
142
187
|
)
|
|
143
188
|
self.env["LIBPYTHON_LOC"] = libpython_path
|
|
144
189
|
|
|
145
|
-
self.env["PATH"] += os.pathsep +
|
|
190
|
+
self.env["PATH"] += os.pathsep + str(cocotb_tools.config.libs_dir)
|
|
146
191
|
self.env["PYTHONPATH"] = os.pathsep.join(sys.path)
|
|
147
|
-
self.env["
|
|
148
|
-
self.env["
|
|
149
|
-
self.env["
|
|
192
|
+
self.env["PYGPI_PYTHON_BIN"] = sys.executable
|
|
193
|
+
self.env["COCOTB_TOPLEVEL"] = self.sim_hdl_toplevel
|
|
194
|
+
self.env["COCOTB_TEST_MODULES"] = self.test_module
|
|
195
|
+
self.env["TOPLEVEL_LANG"] = self.hdl_toplevel_lang
|
|
150
196
|
|
|
151
|
-
@
|
|
152
|
-
def _build_command(self) -> Sequence[
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def _build_command(self) -> Sequence[_Command]:
|
|
153
199
|
"""Return command to build the HDL sources."""
|
|
154
200
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@abc.abstractmethod
|
|
158
|
-
def _test_command(self) -> Sequence[Command]:
|
|
201
|
+
@abstractmethod
|
|
202
|
+
def _test_command(self) -> Sequence[_Command]:
|
|
159
203
|
"""Return command to run a test."""
|
|
160
204
|
|
|
161
|
-
|
|
205
|
+
def _use_external_viewer(self) -> bool:
|
|
206
|
+
"""Return if an external viewer should be called after simulation when ``gui=True``."""
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
def _waves_file(self) -> Optional[str]:
|
|
210
|
+
"""Return file name of the generated waveform file for use with external viewer."""
|
|
211
|
+
return None
|
|
162
212
|
|
|
163
213
|
def build(
|
|
164
214
|
self,
|
|
@@ -175,8 +225,8 @@ class Simulator(abc.ABC):
|
|
|
175
225
|
build_dir: PathLike = "sim_build",
|
|
176
226
|
clean: bool = False,
|
|
177
227
|
verbose: bool = False,
|
|
178
|
-
timescale: Optional[
|
|
179
|
-
waves:
|
|
228
|
+
timescale: Optional[Tuple[str, str]] = None,
|
|
229
|
+
waves: bool = False,
|
|
180
230
|
log_file: Optional[PathLike] = None,
|
|
181
231
|
) -> None:
|
|
182
232
|
"""Build the HDL sources.
|
|
@@ -199,7 +249,7 @@ class Simulator(abc.ABC):
|
|
|
199
249
|
+----------+------------------------------------+
|
|
200
250
|
|
|
201
251
|
|
|
202
|
-
.. code-block::
|
|
252
|
+
.. code-block:: python
|
|
203
253
|
|
|
204
254
|
runner.build(
|
|
205
255
|
sources=[
|
|
@@ -224,25 +274,41 @@ class Simulator(abc.ABC):
|
|
|
224
274
|
hdl_toplevel: The name of the HDL toplevel module.
|
|
225
275
|
always: Always run the build step.
|
|
226
276
|
build_dir: Directory to run the build step in.
|
|
227
|
-
clean: Delete build_dir before building
|
|
277
|
+
clean: Delete *build_dir* before building.
|
|
228
278
|
verbose: Enable verbose messages.
|
|
229
279
|
timescale: Tuple containing time unit and time precision for simulation.
|
|
230
|
-
waves: Record signal traces.
|
|
280
|
+
waves: Record signal traces. Overridden by the ``WAVES`` environment variable.
|
|
231
281
|
log_file: File to write the build log to.
|
|
282
|
+
|
|
283
|
+
.. deprecated:: 2.0
|
|
284
|
+
|
|
285
|
+
Uses of the *verilog_sources* and *vhdl_sources* parameters should be replaced with the language-agnostic *sources* argument.
|
|
232
286
|
"""
|
|
233
287
|
|
|
234
288
|
self.clean: bool = clean
|
|
235
289
|
self.build_dir = get_abs_path(build_dir)
|
|
236
290
|
if self.clean:
|
|
237
291
|
self.rm_build_folder(self.build_dir)
|
|
238
|
-
|
|
292
|
+
self.build_dir.mkdir(parents=True, exist_ok=True)
|
|
239
293
|
|
|
240
294
|
# note: to avoid mutating argument defaults, we ensure that no value
|
|
241
295
|
# is written without a copy. This is much more concise and leads to
|
|
242
296
|
# a better docstring than using `None` as a default in the parameters
|
|
243
297
|
# list.
|
|
244
298
|
self.hdl_library: str = hdl_library
|
|
299
|
+
if verilog_sources:
|
|
300
|
+
warnings.warn(
|
|
301
|
+
"Simulator.build *verilog_sources* parameter is deprecated. Use the language-agnostic *sources* parameter instead.",
|
|
302
|
+
DeprecationWarning,
|
|
303
|
+
stacklevel=2,
|
|
304
|
+
)
|
|
245
305
|
self.verilog_sources: List[Path] = get_abs_paths(verilog_sources)
|
|
306
|
+
if vhdl_sources:
|
|
307
|
+
warnings.warn(
|
|
308
|
+
"Simulator.build *vhdl_sources* parameter is deprecated. Use the language-agnostic *sources* parameter instead.",
|
|
309
|
+
DeprecationWarning,
|
|
310
|
+
stacklevel=2,
|
|
311
|
+
)
|
|
246
312
|
self.vhdl_sources: List[Path] = get_abs_paths(vhdl_sources)
|
|
247
313
|
self.sources: List[Path] = get_abs_paths(sources)
|
|
248
314
|
self.includes: List[Path] = get_abs_paths(includes)
|
|
@@ -252,15 +318,14 @@ class Simulator(abc.ABC):
|
|
|
252
318
|
self.always: bool = always
|
|
253
319
|
self.hdl_toplevel: Optional[str] = hdl_toplevel
|
|
254
320
|
self.verbose: bool = verbose
|
|
255
|
-
self.timescale: Optional[
|
|
321
|
+
self.timescale: Optional[Tuple[str, str]] = timescale
|
|
256
322
|
self.log_file: Optional[PathLike] = log_file
|
|
257
323
|
|
|
258
|
-
self.waves = bool(waves)
|
|
324
|
+
self.waves = bool(os.getenv("WAVES", waves))
|
|
259
325
|
|
|
260
|
-
|
|
261
|
-
self.env[e] = os.environ[e]
|
|
326
|
+
self.env.update(os.environ)
|
|
262
327
|
|
|
263
|
-
cmds: Sequence[
|
|
328
|
+
cmds: Sequence[_Command] = self._build_command()
|
|
264
329
|
self._execute(cmds, cwd=self.build_dir)
|
|
265
330
|
|
|
266
331
|
def test(
|
|
@@ -272,19 +337,21 @@ class Simulator(abc.ABC):
|
|
|
272
337
|
gpi_interfaces: Optional[List[str]] = None,
|
|
273
338
|
testcase: Optional[Union[str, Sequence[str]]] = None,
|
|
274
339
|
seed: Optional[Union[str, int]] = None,
|
|
340
|
+
elab_args: Sequence[str] = [],
|
|
275
341
|
test_args: Sequence[str] = [],
|
|
276
342
|
plusargs: Sequence[str] = [],
|
|
277
343
|
extra_env: Mapping[str, str] = {},
|
|
278
|
-
waves:
|
|
279
|
-
gui:
|
|
280
|
-
parameters: Mapping[str, object] = None,
|
|
344
|
+
waves: bool = False,
|
|
345
|
+
gui: bool = False,
|
|
346
|
+
parameters: Optional[Mapping[str, object]] = None,
|
|
281
347
|
build_dir: Optional[PathLike] = None,
|
|
282
348
|
test_dir: Optional[PathLike] = None,
|
|
283
349
|
results_xml: Optional[str] = None,
|
|
284
|
-
pre_cmd: List[str] =
|
|
350
|
+
pre_cmd: Optional[List[str]] = None,
|
|
285
351
|
verbose: bool = False,
|
|
286
|
-
timescale: Optional[
|
|
352
|
+
timescale: Optional[Tuple[str, str]] = None,
|
|
287
353
|
log_file: Optional[PathLike] = None,
|
|
354
|
+
test_filter: Optional[str] = None,
|
|
288
355
|
) -> Path:
|
|
289
356
|
"""Run the tests.
|
|
290
357
|
|
|
@@ -299,11 +366,12 @@ class Simulator(abc.ABC):
|
|
|
299
366
|
If not set, run all testcases found in *test_module*.
|
|
300
367
|
Can be a comma-separated list.
|
|
301
368
|
seed: A specific random seed to use.
|
|
302
|
-
|
|
369
|
+
elab_args: A list of elaboration arguments for the simulator.
|
|
370
|
+
test_args: A list of extra arguments for the simulator.
|
|
303
371
|
plusargs: 'plusargs' to set for the simulator.
|
|
304
372
|
extra_env: Extra environment variables to set.
|
|
305
|
-
waves: Record signal traces.
|
|
306
|
-
gui: Run with simulator GUI.
|
|
373
|
+
waves: Record signal traces. Overridden by the ``WAVES`` environment variable.
|
|
374
|
+
gui: Run with simulator GUI. Overridden by the ``GUI`` environment variable.
|
|
307
375
|
parameters: Verilog parameters or VHDL generics.
|
|
308
376
|
build_dir: Directory the build step has been run in.
|
|
309
377
|
test_dir: Directory to run the tests in.
|
|
@@ -313,8 +381,11 @@ class Simulator(abc.ABC):
|
|
|
313
381
|
This argument should not be set when run with ``pytest``.
|
|
314
382
|
verbose: Enable verbose messages.
|
|
315
383
|
pre_cmd: Commands to run before simulation begins.
|
|
384
|
+
Typically Tcl commands for simulators that support them.
|
|
316
385
|
timescale: Tuple containing time unit and time precision for simulation.
|
|
317
386
|
log_file: File to write the test log to.
|
|
387
|
+
test_filter: Regular expression which matches test names.
|
|
388
|
+
Only matched tests are run if this argument if given.
|
|
318
389
|
|
|
319
390
|
Returns:
|
|
320
391
|
The absolute location of the results XML file which can be
|
|
@@ -333,7 +404,7 @@ class Simulator(abc.ABC):
|
|
|
333
404
|
self.test_dir = self.build_dir
|
|
334
405
|
else:
|
|
335
406
|
self.test_dir = get_abs_path(test_dir)
|
|
336
|
-
|
|
407
|
+
self.test_dir.mkdir(parents=True, exist_ok=True)
|
|
337
408
|
|
|
338
409
|
if isinstance(test_module, str):
|
|
339
410
|
self.test_module = test_module
|
|
@@ -356,84 +427,134 @@ class Simulator(abc.ABC):
|
|
|
356
427
|
|
|
357
428
|
self.pre_cmd = pre_cmd
|
|
358
429
|
|
|
430
|
+
self.elab_args = list(elab_args)
|
|
359
431
|
self.test_args = list(test_args)
|
|
360
432
|
self.plusargs = list(plusargs)
|
|
361
433
|
self.env = dict(extra_env)
|
|
362
434
|
|
|
363
435
|
if testcase is not None:
|
|
364
436
|
if isinstance(testcase, str):
|
|
365
|
-
self.env["
|
|
437
|
+
self.env["COCOTB_TESTCASE"] = testcase
|
|
366
438
|
else:
|
|
367
|
-
self.env["
|
|
439
|
+
self.env["COCOTB_TESTCASE"] = ",".join(testcase)
|
|
440
|
+
|
|
441
|
+
if test_filter is not None:
|
|
442
|
+
self.env["COCOTB_TEST_FILTER"] = test_filter
|
|
368
443
|
|
|
369
444
|
if seed is not None:
|
|
370
|
-
self.env["
|
|
445
|
+
self.env["COCOTB_RANDOM_SEED"] = str(seed)
|
|
371
446
|
|
|
372
447
|
self.log_file = log_file
|
|
373
|
-
self.waves = bool(waves)
|
|
374
|
-
self.gui = bool(gui)
|
|
375
|
-
self.timescale
|
|
448
|
+
self.waves = bool(os.getenv("WAVES", waves))
|
|
449
|
+
self.gui = bool(os.getenv("GUI", gui))
|
|
450
|
+
self.timescale = timescale
|
|
376
451
|
|
|
377
452
|
if verbose is not None:
|
|
378
453
|
self.verbose = verbose
|
|
379
454
|
|
|
380
|
-
#
|
|
455
|
+
# Pytest test name is used by the next couple sections.
|
|
381
456
|
pytest_current_test = os.getenv("PYTEST_CURRENT_TEST", None)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if results_xml is not None:
|
|
385
|
-
# PYTEST_CURRENT_TEST only allowed when results_xml is not set
|
|
386
|
-
assert not pytest_current_test
|
|
387
|
-
results_xml_path = Path(results_xml)
|
|
388
|
-
if results_xml_path.is_absolute():
|
|
389
|
-
results_xml_file = results_xml_path
|
|
390
|
-
else:
|
|
391
|
-
results_xml_file = test_dir_path / results_xml_path
|
|
392
|
-
elif pytest_current_test is not None:
|
|
457
|
+
|
|
458
|
+
if pytest_current_test is not None:
|
|
393
459
|
self.current_test_name = pytest_current_test.split(":")[-1].split(" ")[0]
|
|
394
|
-
results_xml_file = test_dir_path / f"{self.current_test_name}.{results_xml}"
|
|
395
460
|
else:
|
|
396
|
-
|
|
461
|
+
self.current_test_name = "test"
|
|
462
|
+
|
|
463
|
+
results_xml_path: Union[None, Path] = (
|
|
464
|
+
Path(results_xml) if results_xml is not None else None
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# result.xml filename precedence:
|
|
468
|
+
# 1. absolute path
|
|
469
|
+
# 2. pytest test name
|
|
470
|
+
# 3. relative path
|
|
471
|
+
# 4. default name
|
|
472
|
+
if results_xml_path is not None and results_xml_path.is_absolute():
|
|
473
|
+
results_xml_file = results_xml_path
|
|
474
|
+
elif pytest_current_test is not None:
|
|
475
|
+
if results_xml_path is not None:
|
|
476
|
+
raise NotImplementedError(
|
|
477
|
+
"Relative result_xml paths aren't supported when using pytest"
|
|
478
|
+
)
|
|
479
|
+
results_xml_file = self.test_dir / f"{self.current_test_name}.result.xml"
|
|
480
|
+
elif results_xml_path is not None:
|
|
481
|
+
results_xml_file = self.test_dir / results_xml_path
|
|
482
|
+
else:
|
|
483
|
+
results_xml_file = self.test_dir / "results.xml"
|
|
397
484
|
|
|
398
485
|
with suppress(OSError):
|
|
399
|
-
|
|
486
|
+
results_xml_file.unlink()
|
|
400
487
|
|
|
401
488
|
# transport the settings to cocotb via environment variables
|
|
402
489
|
self._set_env()
|
|
403
490
|
self.env["COCOTB_RESULTS_FILE"] = str(results_xml_file)
|
|
404
491
|
|
|
405
|
-
cmds: Sequence[
|
|
406
|
-
|
|
492
|
+
cmds: Sequence[_Command] = self._test_command()
|
|
493
|
+
simulator_exit_code: int = 0
|
|
494
|
+
try:
|
|
495
|
+
self._execute(cmds, cwd=self.test_dir)
|
|
496
|
+
except subprocess.CalledProcessError as e:
|
|
497
|
+
# It is possible for the simulator to fail but still leave results.
|
|
498
|
+
self.log.error("Simulation failed: %d", e.returncode)
|
|
499
|
+
simulator_exit_code = e.returncode
|
|
407
500
|
|
|
408
501
|
# Only when running under pytest, check the results file here,
|
|
409
502
|
# potentially raising an exception with failing testcases,
|
|
410
503
|
# otherwise return the results file for later analysis.
|
|
411
504
|
if pytest_current_test:
|
|
412
|
-
|
|
505
|
+
try:
|
|
506
|
+
(num_tests, num_failed) = get_results(results_xml_file)
|
|
507
|
+
except RuntimeError as e:
|
|
508
|
+
self.log.error("%s", e.args[0])
|
|
509
|
+
sys.exit(simulator_exit_code)
|
|
510
|
+
else:
|
|
511
|
+
if num_failed:
|
|
512
|
+
self.log.error(
|
|
513
|
+
"ERROR: Failed %d of %d tests.", num_failed, num_tests
|
|
514
|
+
)
|
|
515
|
+
sys.exit(1 if simulator_exit_code == 0 else simulator_exit_code)
|
|
413
516
|
|
|
414
|
-
|
|
517
|
+
if simulator_exit_code != 0:
|
|
518
|
+
sys.exit(simulator_exit_code)
|
|
519
|
+
|
|
520
|
+
if pytest_current_test and self._use_external_viewer() and self.gui:
|
|
521
|
+
viewer = os.getenv("COCOTB_WAVEFORM_VIEWER")
|
|
522
|
+
if viewer is not None:
|
|
523
|
+
viewer_path = shutil.which(viewer)
|
|
524
|
+
if viewer_path is None:
|
|
525
|
+
raise ValueError(f"Cannot find {viewer} in the system path")
|
|
526
|
+
else:
|
|
527
|
+
viewer_path = shutil.which("surfer")
|
|
528
|
+
if viewer_path is None:
|
|
529
|
+
viewer_path = shutil.which("gtkwave")
|
|
530
|
+
if viewer_path is None:
|
|
531
|
+
raise SystemError(
|
|
532
|
+
"Cannot find any viewer (surfer or gtkwave) in the system path"
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
subprocess.run(
|
|
536
|
+
[f"{viewer_path} {self._waves_file()}"],
|
|
537
|
+
cwd=self.test_dir,
|
|
538
|
+
check=True,
|
|
539
|
+
shell=True,
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
self.log.info("Results file: %s", results_xml_file)
|
|
415
543
|
return results_xml_file
|
|
416
544
|
|
|
417
|
-
@
|
|
418
|
-
def _get_include_options(self, includes: Sequence[PathLike]) ->
|
|
545
|
+
@abstractmethod
|
|
546
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
419
547
|
"""Return simulator-specific formatted option strings with *includes* directories."""
|
|
420
548
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
@staticmethod
|
|
424
|
-
def _get_define_options(self, defines: Mapping[str, object]) -> Command:
|
|
549
|
+
@abstractmethod
|
|
550
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
425
551
|
"""Return simulator-specific formatted option strings with *defines* macros."""
|
|
426
552
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
@abc.abstractmethod
|
|
430
|
-
def _get_parameter_options(self, parameters: Mapping[str, object]) -> Command:
|
|
553
|
+
@abstractmethod
|
|
554
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
431
555
|
"""Return simulator-specific formatted option strings with *parameters*/generics."""
|
|
432
556
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
def _execute(self, cmds: Sequence[Command], cwd: PathLike) -> None:
|
|
436
|
-
|
|
557
|
+
def _execute(self, cmds: Sequence[_Command], cwd: PathLike) -> None:
|
|
437
558
|
__tracebackhide__ = True # Hide the traceback when using PyTest.
|
|
438
559
|
|
|
439
560
|
if self.log_file is None:
|
|
@@ -443,77 +564,27 @@ class Simulator(abc.ABC):
|
|
|
443
564
|
self._execute_cmds(cmds, cwd, f)
|
|
444
565
|
|
|
445
566
|
def _execute_cmds(
|
|
446
|
-
self, cmds: Sequence[
|
|
567
|
+
self, cmds: Sequence[_Command], cwd: PathLike, stdout: Optional[TextIO] = None
|
|
447
568
|
) -> None:
|
|
448
569
|
__tracebackhide__ = True # Hide the traceback when using PyTest.
|
|
449
570
|
|
|
450
571
|
for cmd in cmds:
|
|
451
|
-
|
|
572
|
+
self.log.info("Running command %s in directory %s", _shlex_join(cmd), cwd)
|
|
452
573
|
|
|
453
574
|
# TODO: create a thread to handle stderr and log as error?
|
|
454
575
|
# TODO: log forwarding
|
|
455
576
|
|
|
456
577
|
stderr = None if stdout is None else subprocess.STDOUT
|
|
457
|
-
|
|
458
|
-
cmd, cwd=cwd, env=self.env, stdout=stdout, stderr=stderr
|
|
578
|
+
subprocess.run(
|
|
579
|
+
cmd, cwd=cwd, env=self.env, check=True, stdout=stdout, stderr=stderr
|
|
459
580
|
)
|
|
460
581
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
def rm_build_folder(self, build_dir: Path):
|
|
467
|
-
if os.path.isdir(build_dir):
|
|
468
|
-
print("Removing:", build_dir)
|
|
582
|
+
def rm_build_folder(self, build_dir: Path) -> None:
|
|
583
|
+
if build_dir.is_dir():
|
|
584
|
+
self.log.info("Removing: %s", build_dir)
|
|
469
585
|
shutil.rmtree(build_dir, ignore_errors=True)
|
|
470
586
|
|
|
471
587
|
|
|
472
|
-
def get_results(results_xml_file: Path) -> Tuple[int, int]:
|
|
473
|
-
"""Return number of tests and fails in *results_xml_file*.
|
|
474
|
-
|
|
475
|
-
Returns:
|
|
476
|
-
Tuple of number of tests and number of fails.
|
|
477
|
-
|
|
478
|
-
Raises:
|
|
479
|
-
SystemExit: *results_xml_file* is non-existent.
|
|
480
|
-
"""
|
|
481
|
-
|
|
482
|
-
__tracebackhide__ = True # Hide the traceback when using PyTest.
|
|
483
|
-
|
|
484
|
-
if not results_xml_file.is_file():
|
|
485
|
-
raise SystemExit(
|
|
486
|
-
f"ERROR: Simulation terminated abnormally. Results file {results_xml_file} not found."
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
num_tests = 0
|
|
490
|
-
num_failed = 0
|
|
491
|
-
|
|
492
|
-
tree = ET.parse(results_xml_file)
|
|
493
|
-
for ts in tree.iter("testsuite"):
|
|
494
|
-
for tc in ts.iter("testcase"):
|
|
495
|
-
num_tests += 1
|
|
496
|
-
for _ in tc.iter("failure"):
|
|
497
|
-
num_failed += 1
|
|
498
|
-
|
|
499
|
-
return (num_tests, num_failed)
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
def check_results_file(results_xml_file: Path) -> None:
|
|
503
|
-
"""Raise exception if *results_xml_file* does not exist or contains failed tests.
|
|
504
|
-
|
|
505
|
-
Raises:
|
|
506
|
-
SystemExit: *results_xml_file* is non-existent or contains fails.
|
|
507
|
-
"""
|
|
508
|
-
|
|
509
|
-
__tracebackhide__ = True # Hide the traceback when using PyTest.
|
|
510
|
-
|
|
511
|
-
(num_tests, num_failed) = get_results(results_xml_file)
|
|
512
|
-
|
|
513
|
-
if num_failed:
|
|
514
|
-
raise SystemExit(f"ERROR: Failed {num_failed} of {num_tests} tests.")
|
|
515
|
-
|
|
516
|
-
|
|
517
588
|
def outdated(output: Path, dependencies: Sequence[Path]) -> bool:
|
|
518
589
|
"""Return ``True`` if any source files in *dependencies* are newer than the *output* directory.
|
|
519
590
|
|
|
@@ -529,8 +600,7 @@ def outdated(output: Path, dependencies: Sequence[Path]) -> bool:
|
|
|
529
600
|
dep_mtime = 0.0
|
|
530
601
|
for dependency in dependencies:
|
|
531
602
|
mtime = dependency.stat().st_mtime
|
|
532
|
-
|
|
533
|
-
dep_mtime = mtime
|
|
603
|
+
dep_mtime = max(mtime, dep_mtime)
|
|
534
604
|
|
|
535
605
|
return dep_mtime > output_mtime
|
|
536
606
|
|
|
@@ -559,6 +629,10 @@ _verilog_extensions_s = ", ".join(f"`{c}`" for c in _verilog_extensions)
|
|
|
559
629
|
|
|
560
630
|
|
|
561
631
|
class UnknownFileExtension(ValueError):
|
|
632
|
+
"""Raised when a source file type cannot be determined from the file extension.
|
|
633
|
+
|
|
634
|
+
See :meth:`Runner.build() <cocotb_tools.runner.Runner.build>` for details on supported standard file extensions and the use of :class:`VHDL` and :class:`Verilog` for specifying file type."""
|
|
635
|
+
|
|
562
636
|
def __init__(self, source: PathLike) -> None:
|
|
563
637
|
super().__init__(
|
|
564
638
|
f"Can't determine if {source} is a VHDL or Verilog file. "
|
|
@@ -571,44 +645,52 @@ def is_vhdl_source(source: PathLike) -> bool:
|
|
|
571
645
|
if isinstance(source, VHDL):
|
|
572
646
|
return True
|
|
573
647
|
source_as_path = Path(source)
|
|
574
|
-
|
|
575
|
-
return True
|
|
576
|
-
return False
|
|
648
|
+
return source_as_path.suffix in _vhdl_extensions
|
|
577
649
|
|
|
578
650
|
|
|
579
651
|
def is_verilog_source(source: PathLike) -> bool:
|
|
580
652
|
if isinstance(source, Verilog):
|
|
581
653
|
return True
|
|
582
654
|
source_as_path = Path(source)
|
|
583
|
-
|
|
584
|
-
return True
|
|
585
|
-
return False
|
|
655
|
+
return source_as_path.suffix in _verilog_extensions
|
|
586
656
|
|
|
587
657
|
|
|
588
|
-
class Icarus(
|
|
658
|
+
class Icarus(Runner):
|
|
659
|
+
"""Implementation of :class:`Runner` for Icarus.
|
|
660
|
+
|
|
661
|
+
* ``hdl_toplevel`` argument to :meth:`.build` is *required*.
|
|
662
|
+
* ``waves=True`` *must* be given to :meth:`.build` if either ``waves`` or ``gui`` are to be used during :meth:`.test`.
|
|
663
|
+
* ``timescale`` argument to :meth:`.build` must be given to support dumping the command file.
|
|
664
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
665
|
+
"""
|
|
666
|
+
|
|
589
667
|
supported_gpi_interfaces = {"verilog": ["vpi"]}
|
|
590
668
|
|
|
591
|
-
|
|
592
|
-
def _simulator_in_path() -> None:
|
|
669
|
+
def _simulator_in_path(self) -> None:
|
|
593
670
|
if shutil.which("iverilog") is None:
|
|
594
671
|
raise SystemExit("ERROR: iverilog executable not found!")
|
|
595
672
|
|
|
596
|
-
|
|
597
|
-
def _get_include_options(includes: Sequence[PathLike]) -> Command:
|
|
673
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
598
674
|
return [f"-I{include}" for include in includes]
|
|
599
675
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return [f"-D{name}={value}" for name, value in defines.items()]
|
|
676
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
677
|
+
return [f"-D{name}={_as_sv_literal(value)}" for name, value in defines.items()]
|
|
603
678
|
|
|
604
|
-
def _get_parameter_options(self, parameters: Mapping[str, object]) ->
|
|
679
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
605
680
|
assert self.hdl_toplevel is not None
|
|
606
681
|
return [
|
|
607
682
|
f"-P{self.hdl_toplevel}.{name}={value}"
|
|
608
683
|
for name, value in parameters.items()
|
|
609
684
|
]
|
|
610
685
|
|
|
686
|
+
def _use_external_viewer(self) -> bool:
|
|
687
|
+
return True
|
|
688
|
+
|
|
689
|
+
def _waves_file(self) -> Optional[str]:
|
|
690
|
+
return f"{self.hdl_toplevel}.fst"
|
|
691
|
+
|
|
611
692
|
def _create_cmd_file(self) -> None:
|
|
693
|
+
assert self.timescale is not None
|
|
612
694
|
with open(self.cmds_file, "w") as f:
|
|
613
695
|
f.write("+timescale+{}/{}\n".format(*self.timescale))
|
|
614
696
|
|
|
@@ -617,7 +699,14 @@ class Icarus(Simulator):
|
|
|
617
699
|
with open(self.iverilog_dump_file, "w") as f:
|
|
618
700
|
f.write("module cocotb_iverilog_dump();\n")
|
|
619
701
|
f.write("initial begin\n")
|
|
620
|
-
f.write(
|
|
702
|
+
f.write(" string dumpfile_path;")
|
|
703
|
+
f.write(
|
|
704
|
+
' if ($value$plusargs("dumpfile_path=%s", dumpfile_path)) begin\n'
|
|
705
|
+
)
|
|
706
|
+
f.write(" $dumpfile(dumpfile_path);\n")
|
|
707
|
+
f.write(" end else begin\n")
|
|
708
|
+
f.write(f' $dumpfile("{dumpfile_path}");\n')
|
|
709
|
+
f.write(" end\n")
|
|
621
710
|
f.write(f" $dumpvars(0, {self.hdl_toplevel});\n")
|
|
622
711
|
f.write("end\n")
|
|
623
712
|
f.write("endmodule\n")
|
|
@@ -634,28 +723,33 @@ class Icarus(Simulator):
|
|
|
634
723
|
def cmds_file(self) -> Path:
|
|
635
724
|
return self.build_dir / "cmds.f"
|
|
636
725
|
|
|
637
|
-
def _test_command(self) -> List[
|
|
726
|
+
def _test_command(self) -> List[_Command]:
|
|
638
727
|
plusargs = self.plusargs
|
|
639
|
-
if self.waves:
|
|
728
|
+
if self.waves or self.gui:
|
|
640
729
|
plusargs += ["-fst"]
|
|
730
|
+
else:
|
|
731
|
+
# Disable waveform output
|
|
732
|
+
plusargs += ["-none"]
|
|
641
733
|
|
|
642
|
-
if self.pre_cmd:
|
|
643
|
-
|
|
734
|
+
if self.pre_cmd is not None:
|
|
735
|
+
raise ValueError("WARNING: pre_cmd is not implemented for Icarus Verilog.")
|
|
644
736
|
|
|
645
737
|
return [
|
|
646
738
|
[
|
|
647
739
|
"vvp",
|
|
648
740
|
"-M",
|
|
649
|
-
|
|
741
|
+
str(cocotb_tools.config.libs_dir),
|
|
650
742
|
"-m",
|
|
651
|
-
|
|
743
|
+
cocotb_tools.config.lib_name("vpi", "icarus"),
|
|
744
|
+
*self.test_args,
|
|
745
|
+
str(self.sim_file),
|
|
746
|
+
*plusargs,
|
|
652
747
|
]
|
|
653
|
-
+ self.test_args
|
|
654
|
-
+ [str(self.sim_file)]
|
|
655
|
-
+ plusargs
|
|
656
748
|
]
|
|
657
749
|
|
|
658
|
-
def _build_command(self) -> List[
|
|
750
|
+
def _build_command(self) -> List[_Command]:
|
|
751
|
+
assert self.hdl_toplevel is not None
|
|
752
|
+
|
|
659
753
|
for source in self.sources:
|
|
660
754
|
if not is_verilog_source(source):
|
|
661
755
|
raise ValueError(
|
|
@@ -663,8 +757,8 @@ class Icarus(Simulator):
|
|
|
663
757
|
)
|
|
664
758
|
for arg in self.build_args:
|
|
665
759
|
if type(arg) not in (str, Verilog):
|
|
666
|
-
|
|
667
|
-
f"
|
|
760
|
+
raise ValueError(
|
|
761
|
+
f"{type(self).__qualname__} only supports Verilog. build_args {arg!r} cannot be applied."
|
|
668
762
|
)
|
|
669
763
|
|
|
670
764
|
build_args = list(self.build_args)
|
|
@@ -676,7 +770,7 @@ class Icarus(Simulator):
|
|
|
676
770
|
self._create_cmd_file()
|
|
677
771
|
build_args += ["-f", str(self.cmds_file)]
|
|
678
772
|
|
|
679
|
-
cmds = []
|
|
773
|
+
cmds: list[_Command] = []
|
|
680
774
|
sources = [
|
|
681
775
|
source for source in self.sources if is_verilog_source(source)
|
|
682
776
|
] + self.verilog_sources
|
|
@@ -686,8 +780,6 @@ class Icarus(Simulator):
|
|
|
686
780
|
"iverilog",
|
|
687
781
|
"-o",
|
|
688
782
|
str(self.sim_file),
|
|
689
|
-
"-D",
|
|
690
|
-
"COCOTB_SIM=1",
|
|
691
783
|
"-s",
|
|
692
784
|
self.hdl_toplevel,
|
|
693
785
|
"-g2012",
|
|
@@ -705,39 +797,38 @@ class Icarus(Simulator):
|
|
|
705
797
|
]
|
|
706
798
|
|
|
707
799
|
else:
|
|
708
|
-
|
|
800
|
+
self.log.warning("Skipping compilation of %s", self.sim_file)
|
|
709
801
|
|
|
710
802
|
return cmds
|
|
711
803
|
|
|
712
804
|
|
|
713
|
-
class Questa(
|
|
805
|
+
class Questa(Runner):
|
|
806
|
+
"""Implementation of :class:`Runner` for Questa.
|
|
807
|
+
|
|
808
|
+
* Does not support the ``timescale`` argument to :meth:`.build` or :meth:`.test`.
|
|
809
|
+
"""
|
|
810
|
+
|
|
714
811
|
supported_gpi_interfaces = {"verilog": ["vpi"], "vhdl": ["fli", "vhpi"]}
|
|
715
812
|
|
|
716
|
-
|
|
717
|
-
def _simulator_in_path() -> None:
|
|
813
|
+
def _simulator_in_path(self) -> None:
|
|
718
814
|
if shutil.which("vsim") is None:
|
|
719
815
|
raise SystemExit("ERROR: vsim executable not found!")
|
|
720
816
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
return [f"+incdir+{as_tcl_value(str(include))}" for include in includes]
|
|
817
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
818
|
+
return [f"+incdir+{_as_tcl_value(str(include))}" for include in includes]
|
|
724
819
|
|
|
725
|
-
|
|
726
|
-
def _get_define_options(defines: Mapping[str, object]) -> Command:
|
|
820
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
727
821
|
return [
|
|
728
|
-
f"+define+{
|
|
729
|
-
for name, value in defines.items()
|
|
822
|
+
f"+define+{name}={_as_sv_literal(value)}" for name, value in defines.items()
|
|
730
823
|
]
|
|
731
824
|
|
|
732
|
-
|
|
733
|
-
def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
|
|
825
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
734
826
|
return [f"-g{name}={value}" for name, value in parameters.items()]
|
|
735
827
|
|
|
736
|
-
def _build_command(self) -> List[
|
|
737
|
-
|
|
828
|
+
def _build_command(self) -> List[_Command]:
|
|
738
829
|
cmds = []
|
|
739
830
|
|
|
740
|
-
cmds.append(["vlib",
|
|
831
|
+
cmds.append(["vlib", _as_tcl_value(self.hdl_library)])
|
|
741
832
|
for source in self.sources:
|
|
742
833
|
if is_vhdl_source(source):
|
|
743
834
|
cmds.append(self._build_vhdl_command(source))
|
|
@@ -752,32 +843,33 @@ class Questa(Simulator):
|
|
|
752
843
|
|
|
753
844
|
return cmds
|
|
754
845
|
|
|
755
|
-
def _build_vhdl_command(self, source: PathLike) ->
|
|
846
|
+
def _build_vhdl_command(self, source: PathLike) -> _Command:
|
|
756
847
|
return (
|
|
757
848
|
["vcom"]
|
|
758
|
-
+ ["-work",
|
|
759
|
-
+ [
|
|
760
|
-
+ [
|
|
849
|
+
+ ["-work", _as_tcl_value(self.hdl_library)]
|
|
850
|
+
+ [_as_tcl_value(v) for v in self.build_args if type(v) in (str, VHDL)]
|
|
851
|
+
+ [_as_tcl_value(str(source))]
|
|
761
852
|
)
|
|
762
853
|
|
|
763
|
-
def _build_verilog_command(self, source: PathLike) ->
|
|
854
|
+
def _build_verilog_command(self, source: PathLike) -> _Command:
|
|
764
855
|
return (
|
|
765
856
|
["vlog"]
|
|
766
857
|
+ ([] if self.always else ["-incr"])
|
|
767
|
-
+ ["-work",
|
|
858
|
+
+ ["-work", _as_tcl_value(self.hdl_library)]
|
|
768
859
|
+ ["-sv"]
|
|
769
860
|
+ self._get_define_options(self.defines)
|
|
770
861
|
+ self._get_include_options(self.includes)
|
|
771
|
-
+ [
|
|
772
|
-
+ [
|
|
862
|
+
+ [_as_tcl_value(v) for v in self.build_args if type(v) in (str, Verilog)]
|
|
863
|
+
+ [_as_tcl_value(str(source))]
|
|
773
864
|
)
|
|
774
865
|
|
|
775
|
-
def _test_command(self) -> List[
|
|
776
|
-
|
|
866
|
+
def _test_command(self) -> List[_Command]:
|
|
777
867
|
cmds = []
|
|
778
868
|
|
|
779
|
-
if self.pre_cmd:
|
|
780
|
-
|
|
869
|
+
if self.pre_cmd is not None:
|
|
870
|
+
pre_cmd = ["-do", *self.pre_cmd]
|
|
871
|
+
else:
|
|
872
|
+
pre_cmd = []
|
|
781
873
|
|
|
782
874
|
do_script = ""
|
|
783
875
|
if self.waves:
|
|
@@ -791,19 +883,25 @@ class Questa(Simulator):
|
|
|
791
883
|
lib_opts = [
|
|
792
884
|
"-foreign",
|
|
793
885
|
"cocotb_init "
|
|
794
|
-
+
|
|
886
|
+
+ _as_tcl_value(
|
|
887
|
+
cocotb_tools.config.lib_name_path("fli", "questa").as_posix()
|
|
888
|
+
),
|
|
795
889
|
]
|
|
796
890
|
elif gpi_if_entry == "vhpi":
|
|
797
891
|
lib_opts = ["-voptargs=-access=rw+/."]
|
|
798
892
|
lib_opts += [
|
|
799
893
|
"-foreign",
|
|
800
894
|
"vhpi_startup_routines_bootstrap "
|
|
801
|
-
+
|
|
895
|
+
+ _as_tcl_value(
|
|
896
|
+
cocotb_tools.config.lib_name_path("vhpi", "questa").as_posix()
|
|
897
|
+
),
|
|
802
898
|
]
|
|
803
899
|
else:
|
|
804
900
|
lib_opts = [
|
|
805
901
|
"-pli",
|
|
806
|
-
|
|
902
|
+
_as_tcl_value(
|
|
903
|
+
cocotb_tools.config.lib_name_path("vpi", "questa").as_posix()
|
|
904
|
+
),
|
|
807
905
|
]
|
|
808
906
|
|
|
809
907
|
cmds.append(
|
|
@@ -811,34 +909,42 @@ class Questa(Simulator):
|
|
|
811
909
|
+ ["-gui" if self.gui else "-c"]
|
|
812
910
|
+ ["-onfinish", "stop" if self.gui else "exit"]
|
|
813
911
|
+ lib_opts
|
|
814
|
-
+ [
|
|
815
|
-
+ [
|
|
816
|
-
+ [
|
|
817
|
-
+ [
|
|
818
|
-
+
|
|
912
|
+
+ [_as_tcl_value(v) for v in self.test_args]
|
|
913
|
+
+ [_as_tcl_value(v) for v in self._get_parameter_options(self.parameters)]
|
|
914
|
+
+ [_as_tcl_value(f"{self.hdl_toplevel_library}.{self.sim_hdl_toplevel}")]
|
|
915
|
+
+ [_as_tcl_value(v) for v in self.plusargs]
|
|
916
|
+
+ pre_cmd
|
|
819
917
|
+ ["-do", do_script]
|
|
820
918
|
)
|
|
821
919
|
|
|
822
920
|
gpi_extra_list = []
|
|
823
921
|
for gpi_if in self.gpi_interfaces[1:]:
|
|
824
|
-
gpi_if_lib_path =
|
|
825
|
-
if
|
|
922
|
+
gpi_if_lib_path = cocotb_tools.config.lib_name_path(gpi_if, "questa")
|
|
923
|
+
if gpi_if_lib_path.is_file():
|
|
826
924
|
gpi_extra_list.append(
|
|
827
|
-
|
|
828
|
-
+ f":cocotb{gpi_if}_entry_point"
|
|
925
|
+
gpi_if_lib_path.as_posix() + f":cocotb{gpi_if}_entry_point"
|
|
829
926
|
)
|
|
830
927
|
else:
|
|
831
|
-
|
|
928
|
+
raise RuntimeError(f"{gpi_if_lib_path} library not found.")
|
|
832
929
|
self.env["GPI_EXTRA"] = ",".join(gpi_extra_list)
|
|
833
930
|
|
|
834
931
|
return cmds
|
|
835
932
|
|
|
836
933
|
|
|
837
|
-
class Ghdl(
|
|
934
|
+
class Ghdl(Runner):
|
|
935
|
+
"""Implementation of :class:`Runner` for GHDL.
|
|
936
|
+
|
|
937
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
938
|
+
"""
|
|
939
|
+
|
|
838
940
|
supported_gpi_interfaces = {"vhdl": ["vpi"]}
|
|
839
941
|
|
|
840
|
-
|
|
841
|
-
|
|
942
|
+
def _set_env(self) -> None:
|
|
943
|
+
super()._set_env()
|
|
944
|
+
if "COCOTB_TRUST_INERTIAL_WRITES" not in self.env:
|
|
945
|
+
self.env["COCOTB_TRUST_INERTIAL_WRITES"] = "1"
|
|
946
|
+
|
|
947
|
+
def _simulator_in_path(self) -> None:
|
|
842
948
|
if shutil.which("ghdl") is None:
|
|
843
949
|
raise SystemExit("ERROR: ghdl executable not found!")
|
|
844
950
|
|
|
@@ -853,11 +959,22 @@ class Ghdl(Simulator):
|
|
|
853
959
|
)
|
|
854
960
|
return "mcode" in result.stdout
|
|
855
961
|
|
|
856
|
-
|
|
857
|
-
|
|
962
|
+
def _use_external_viewer(self) -> bool:
|
|
963
|
+
return True
|
|
964
|
+
|
|
965
|
+
def _waves_file(self) -> Optional[str]:
|
|
966
|
+
return f"{self.hdl_toplevel}.ghw"
|
|
967
|
+
|
|
968
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
969
|
+
raise RuntimeError
|
|
970
|
+
|
|
971
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
972
|
+
raise RuntimeError
|
|
973
|
+
|
|
974
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
858
975
|
return [f"-g{name}={value}" for name, value in parameters.items()]
|
|
859
976
|
|
|
860
|
-
def _build_command(self) -> List[
|
|
977
|
+
def _build_command(self) -> List[_Command]:
|
|
861
978
|
for source in self.sources:
|
|
862
979
|
if not is_vhdl_source(source):
|
|
863
980
|
raise ValueError(
|
|
@@ -865,8 +982,8 @@ class Ghdl(Simulator):
|
|
|
865
982
|
)
|
|
866
983
|
for arg in self.build_args:
|
|
867
984
|
if type(arg) not in (str, VHDL):
|
|
868
|
-
|
|
869
|
-
f"
|
|
985
|
+
raise ValueError(
|
|
986
|
+
f"{type(self).__qualname__} only supports VHDL. build_args {arg!r} will not be applied."
|
|
870
987
|
)
|
|
871
988
|
|
|
872
989
|
cmds = [
|
|
@@ -879,17 +996,20 @@ class Ghdl(Simulator):
|
|
|
879
996
|
|
|
880
997
|
if self.hdl_toplevel is not None:
|
|
881
998
|
cmds += [
|
|
882
|
-
[
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
999
|
+
[
|
|
1000
|
+
"ghdl",
|
|
1001
|
+
"-m",
|
|
1002
|
+
f"--work={self.hdl_library}",
|
|
1003
|
+
*self.build_args,
|
|
1004
|
+
self.hdl_toplevel,
|
|
1005
|
+
]
|
|
886
1006
|
]
|
|
887
1007
|
|
|
888
1008
|
return cmds
|
|
889
1009
|
|
|
890
|
-
def _test_command(self) -> List[
|
|
891
|
-
if self.pre_cmd:
|
|
892
|
-
|
|
1010
|
+
def _test_command(self) -> List[_Command]:
|
|
1011
|
+
if self.pre_cmd is not None:
|
|
1012
|
+
raise RuntimeError("pre_cmd is not implemented for GHDL.")
|
|
893
1013
|
|
|
894
1014
|
ghdl_run_args = self.test_args
|
|
895
1015
|
|
|
@@ -925,27 +1045,64 @@ class Ghdl(Simulator):
|
|
|
925
1045
|
+ [f"--work={self.hdl_toplevel_library}"]
|
|
926
1046
|
+ ghdl_run_args
|
|
927
1047
|
+ [self.sim_hdl_toplevel]
|
|
928
|
-
+ ["--vpi=" +
|
|
1048
|
+
+ ["--vpi=" + cocotb_tools.config.lib_name_path("vpi", "ghdl").as_posix()]
|
|
929
1049
|
+ self.plusargs
|
|
930
1050
|
+ self._get_parameter_options(self.parameters)
|
|
1051
|
+
+ ([f"--wave={self._waves_file()}"] if self.waves or self.gui else [])
|
|
931
1052
|
]
|
|
932
1053
|
|
|
933
1054
|
return cmds
|
|
934
1055
|
|
|
935
1056
|
|
|
936
|
-
class Nvc(
|
|
1057
|
+
class Nvc(Runner):
|
|
1058
|
+
"""Implementation of :class:`Runner` for NVC.
|
|
1059
|
+
|
|
1060
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
1061
|
+
* Does not support the ``timescale`` argument to :meth:`.build` or :meth:`.test`.
|
|
1062
|
+
"""
|
|
1063
|
+
|
|
937
1064
|
supported_gpi_interfaces = {"vhdl": ["vhpi"]}
|
|
938
1065
|
|
|
939
|
-
|
|
940
|
-
|
|
1066
|
+
def __init__(self) -> None:
|
|
1067
|
+
super().__init__()
|
|
1068
|
+
|
|
1069
|
+
version_str = subprocess.run(
|
|
1070
|
+
["nvc", "--version"],
|
|
1071
|
+
check=True,
|
|
1072
|
+
text=True,
|
|
1073
|
+
stdout=subprocess.PIPE,
|
|
1074
|
+
).stdout
|
|
1075
|
+
version = NvcVersion.from_commandline(version_str)
|
|
1076
|
+
if version > NvcVersion("1.16"):
|
|
1077
|
+
self._preserve_case = ["--preserve-case"]
|
|
1078
|
+
else:
|
|
1079
|
+
self._preserve_case = []
|
|
1080
|
+
|
|
1081
|
+
def _set_env(self) -> None:
|
|
1082
|
+
super()._set_env()
|
|
1083
|
+
if "COCOTB_TRUST_INERTIAL_WRITES" not in self.env:
|
|
1084
|
+
self.env["COCOTB_TRUST_INERTIAL_WRITES"] = "1"
|
|
1085
|
+
|
|
1086
|
+
def _simulator_in_path(self) -> None:
|
|
941
1087
|
if shutil.which("nvc") is None:
|
|
942
1088
|
raise SystemExit("ERROR: nvc executable not found!")
|
|
943
1089
|
|
|
944
|
-
|
|
945
|
-
|
|
1090
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
1091
|
+
raise RuntimeError
|
|
1092
|
+
|
|
1093
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
1094
|
+
raise RuntimeError
|
|
1095
|
+
|
|
1096
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
946
1097
|
return [f"-g{name}={value}" for name, value in parameters.items()]
|
|
947
1098
|
|
|
948
|
-
def
|
|
1099
|
+
def _use_external_viewer(self) -> bool:
|
|
1100
|
+
return True
|
|
1101
|
+
|
|
1102
|
+
def _waves_file(self) -> Optional[str]:
|
|
1103
|
+
return f"{self.hdl_toplevel}.fst"
|
|
1104
|
+
|
|
1105
|
+
def _build_command(self) -> List[_Command]:
|
|
949
1106
|
for source in self.sources:
|
|
950
1107
|
if not is_vhdl_source(source):
|
|
951
1108
|
raise ValueError(
|
|
@@ -953,65 +1110,97 @@ class Nvc(Simulator):
|
|
|
953
1110
|
)
|
|
954
1111
|
for arg in self.build_args:
|
|
955
1112
|
if type(arg) not in (str, VHDL):
|
|
956
|
-
|
|
957
|
-
f"
|
|
1113
|
+
raise ValueError(
|
|
1114
|
+
f"{type(self).__qualname__} only supports VHDL. build_args {arg!r} will not be applied."
|
|
958
1115
|
)
|
|
959
1116
|
|
|
960
1117
|
cmds = [
|
|
961
|
-
[
|
|
1118
|
+
[
|
|
1119
|
+
"nvc",
|
|
1120
|
+
f"--work={self.hdl_library}",
|
|
1121
|
+
"-L",
|
|
1122
|
+
str(get_abs_path(self.build_dir)),
|
|
1123
|
+
]
|
|
962
1124
|
+ [arg for arg in self.build_args if type(arg) in (str, VHDL)]
|
|
963
1125
|
+ ["-a"]
|
|
964
1126
|
+ [str(source) for source in self.sources if is_vhdl_source(source)]
|
|
965
1127
|
+ [str(source) for source in self.vhdl_sources]
|
|
1128
|
+
+ self._preserve_case
|
|
966
1129
|
]
|
|
967
1130
|
|
|
968
1131
|
return cmds
|
|
969
1132
|
|
|
970
|
-
def _test_command(self) -> List[
|
|
1133
|
+
def _test_command(self) -> List[_Command]:
|
|
1134
|
+
work_library = str(get_abs_path(self.build_dir / self.hdl_toplevel_library))
|
|
971
1135
|
cmds = [
|
|
972
|
-
[
|
|
1136
|
+
[
|
|
1137
|
+
"nvc",
|
|
1138
|
+
f"--work={self.hdl_toplevel_library}:{work_library}",
|
|
1139
|
+
"-L",
|
|
1140
|
+
str(get_abs_path(self.build_dir)),
|
|
1141
|
+
]
|
|
973
1142
|
+ self.build_args
|
|
974
1143
|
+ ["-e", self.sim_hdl_toplevel, "--no-save", "--jit"]
|
|
1144
|
+
+ self.elab_args
|
|
975
1145
|
+ self._get_parameter_options(self.parameters)
|
|
976
1146
|
+ ["-r"]
|
|
977
1147
|
+ self.test_args
|
|
978
|
-
+ ["--load=" +
|
|
1148
|
+
+ ["--load=" + cocotb_tools.config.lib_name_path("vhpi", "nvc").as_posix()]
|
|
979
1149
|
+ self.plusargs
|
|
1150
|
+
+ ([f"--wave={self._waves_file()}"] if self.waves or self.gui else [])
|
|
980
1151
|
]
|
|
981
1152
|
|
|
982
1153
|
return cmds
|
|
983
1154
|
|
|
984
1155
|
|
|
985
|
-
class Riviera(
|
|
1156
|
+
class Riviera(Runner):
|
|
1157
|
+
"""Implementation of :class:`Runner` for Riviera-PRO.
|
|
1158
|
+
|
|
1159
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
1160
|
+
* Does not support the ``gui`` argument to :meth:`.test`.
|
|
1161
|
+
* Does not support the ``timescale`` argument to :meth:`.build` or :meth:`.test`.
|
|
1162
|
+
"""
|
|
1163
|
+
|
|
986
1164
|
supported_gpi_interfaces = {"verilog": ["vpi"], "vhdl": ["vhpi"]}
|
|
987
1165
|
|
|
988
|
-
|
|
989
|
-
def _simulator_in_path() -> None:
|
|
1166
|
+
def _simulator_in_path(self) -> None:
|
|
990
1167
|
if shutil.which("vsimsa") is None:
|
|
991
1168
|
raise SystemExit("ERROR: vsimsa executable not found!")
|
|
992
1169
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
return [f"+incdir+{as_tcl_value(str(include))}" for include in includes]
|
|
1170
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
1171
|
+
return [f"+incdir+{_as_tcl_value(str(include))}" for include in includes]
|
|
996
1172
|
|
|
997
|
-
|
|
998
|
-
def _get_define_options(defines: Mapping[str, object]) -> Command:
|
|
1173
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
999
1174
|
return [
|
|
1000
|
-
f"+define+{
|
|
1175
|
+
f"+define+{name}={self._as_define_value(value)}"
|
|
1001
1176
|
for name, value in defines.items()
|
|
1002
1177
|
]
|
|
1003
1178
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1179
|
+
def _as_define_value(self, value: object) -> str:
|
|
1180
|
+
if isinstance(value, int):
|
|
1181
|
+
return str(value)
|
|
1182
|
+
elif isinstance(value, str):
|
|
1183
|
+
for char in value:
|
|
1184
|
+
if ord(char) < 32 or ord(char) >= 255 or char in '\\"':
|
|
1185
|
+
# Control characters are generally not supported.
|
|
1186
|
+
# Not sure if there's any way to escape quotes or backslashes.
|
|
1187
|
+
raise ValueError(
|
|
1188
|
+
f"Character {char!r} not supported in define value"
|
|
1189
|
+
)
|
|
1190
|
+
return '\\"\\\\"' + value + '\\\\"\\"'
|
|
1191
|
+
else:
|
|
1192
|
+
raise TypeError("Can't serialize this type as an SV literal")
|
|
1193
|
+
|
|
1194
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
1006
1195
|
return [f"-g{name}={value}" for name, value in parameters.items()]
|
|
1007
1196
|
|
|
1008
|
-
def _build_command(self) -> List[
|
|
1197
|
+
def _build_command(self) -> List[_Command]:
|
|
1009
1198
|
do_script: List[str] = ["onerror {\n quit -code 1 \n}"]
|
|
1010
1199
|
|
|
1011
1200
|
out_file = self.build_dir / self.hdl_library / f"{self.hdl_library}.lib"
|
|
1012
1201
|
|
|
1013
1202
|
if outdated(out_file, self.verilog_sources + self.vhdl_sources) or self.always:
|
|
1014
|
-
do_script.append(f"alib {
|
|
1203
|
+
do_script.append(f"alib {_as_tcl_value(self.hdl_library)}")
|
|
1015
1204
|
|
|
1016
1205
|
for source in self.sources:
|
|
1017
1206
|
if is_verilog_source(source):
|
|
@@ -1032,74 +1221,81 @@ class Riviera(Simulator):
|
|
|
1032
1221
|
# behavior.
|
|
1033
1222
|
do_script.append("exit")
|
|
1034
1223
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
do_file.close()
|
|
1224
|
+
with tempfile.NamedTemporaryFile(delete=False) as do_file:
|
|
1225
|
+
do_file.write("\n".join(do_script).encode())
|
|
1038
1226
|
|
|
1039
|
-
return [["vsimsa"
|
|
1227
|
+
return [["vsimsa", "-do", "do", do_file.name]]
|
|
1040
1228
|
|
|
1041
1229
|
def _build_vhdl_source(self, source: PathLike) -> str:
|
|
1042
1230
|
return "acom -work {RTL_LIBRARY} {EXTRA_ARGS} {VHDL_SOURCES}".format(
|
|
1043
|
-
RTL_LIBRARY=
|
|
1044
|
-
VHDL_SOURCES=
|
|
1231
|
+
RTL_LIBRARY=_as_tcl_value(self.hdl_library),
|
|
1232
|
+
VHDL_SOURCES=_as_tcl_value(str(source)),
|
|
1045
1233
|
EXTRA_ARGS=" ".join(
|
|
1046
|
-
|
|
1234
|
+
_as_tcl_value(v) for v in self.build_args if type(v) in (str, VHDL)
|
|
1047
1235
|
),
|
|
1048
1236
|
)
|
|
1049
1237
|
|
|
1050
1238
|
def _build_verilog_source(self, source: PathLike) -> str:
|
|
1051
1239
|
return "alog -work {RTL_LIBRARY} -pli {EXT_NAME} -sv {DEFINES} {INCDIR} {EXTRA_ARGS} {VERILOG_SOURCES}".format(
|
|
1052
|
-
RTL_LIBRARY=
|
|
1053
|
-
EXT_NAME=
|
|
1054
|
-
|
|
1240
|
+
RTL_LIBRARY=_as_tcl_value(self.hdl_library),
|
|
1241
|
+
EXT_NAME=_as_tcl_value(
|
|
1242
|
+
cocotb_tools.config.lib_name_path("vpi", "riviera").as_posix()
|
|
1243
|
+
),
|
|
1244
|
+
VERILOG_SOURCES=_as_tcl_value(str(source)),
|
|
1055
1245
|
DEFINES=" ".join(self._get_define_options(self.defines)),
|
|
1056
1246
|
INCDIR=" ".join(self._get_include_options(self.includes)),
|
|
1057
1247
|
EXTRA_ARGS=" ".join(
|
|
1058
|
-
|
|
1248
|
+
_as_tcl_value(v) for v in self.build_args if type(v) in (str, Verilog)
|
|
1059
1249
|
),
|
|
1060
1250
|
)
|
|
1061
1251
|
|
|
1062
|
-
def _test_command(self) -> List[
|
|
1063
|
-
if self.pre_cmd:
|
|
1064
|
-
|
|
1252
|
+
def _test_command(self) -> List[_Command]:
|
|
1253
|
+
if self.pre_cmd is not None:
|
|
1254
|
+
raise RuntimeError("pre_cmd is not implemented for Riviera.")
|
|
1065
1255
|
|
|
1066
1256
|
do_script: str = "\nonerror {\n quit -code 1 \n} \n"
|
|
1067
1257
|
|
|
1068
1258
|
if self.hdl_toplevel_lang == "vhdl":
|
|
1069
1259
|
do_script += "asim +access +w_nets -interceptcoutput -loadvhpi {EXT_NAME} {EXTRA_ARGS} {TOPLEVEL} {PLUSARGS}\n".format(
|
|
1070
|
-
TOPLEVEL=
|
|
1260
|
+
TOPLEVEL=_as_tcl_value(
|
|
1071
1261
|
f"{self.hdl_toplevel_library}.{self.sim_hdl_toplevel}"
|
|
1072
1262
|
),
|
|
1073
|
-
EXT_NAME=
|
|
1263
|
+
EXT_NAME=_as_tcl_value(
|
|
1264
|
+
cocotb_tools.config.lib_name_path("vhpi", "riviera").as_posix()
|
|
1265
|
+
+ ":vhpi_startup_routines_bootstrap"
|
|
1266
|
+
),
|
|
1074
1267
|
EXTRA_ARGS=" ".join(
|
|
1075
|
-
|
|
1268
|
+
_as_tcl_value(v)
|
|
1076
1269
|
for v in (
|
|
1077
1270
|
self.test_args + self._get_parameter_options(self.parameters)
|
|
1078
1271
|
)
|
|
1079
1272
|
),
|
|
1080
|
-
PLUSARGS=" ".join(
|
|
1273
|
+
PLUSARGS=" ".join(_as_tcl_value(v) for v in self.plusargs),
|
|
1081
1274
|
)
|
|
1082
1275
|
|
|
1083
1276
|
self.env["GPI_EXTRA"] = (
|
|
1084
|
-
|
|
1277
|
+
cocotb_tools.config.lib_name_path("vpi", "riviera").as_posix()
|
|
1278
|
+
+ ":cocotbvpi_entry_point"
|
|
1085
1279
|
)
|
|
1086
1280
|
else:
|
|
1087
1281
|
do_script += "asim +access +w_nets -interceptcoutput -pli {EXT_NAME} {EXTRA_ARGS} {TOPLEVEL} {PLUSARGS} \n".format(
|
|
1088
|
-
TOPLEVEL=
|
|
1282
|
+
TOPLEVEL=_as_tcl_value(
|
|
1089
1283
|
f"{self.hdl_toplevel_library}.{self.sim_hdl_toplevel}"
|
|
1090
1284
|
),
|
|
1091
|
-
EXT_NAME=
|
|
1285
|
+
EXT_NAME=_as_tcl_value(
|
|
1286
|
+
cocotb_tools.config.lib_name_path("vpi", "riviera").as_posix()
|
|
1287
|
+
),
|
|
1092
1288
|
EXTRA_ARGS=" ".join(
|
|
1093
|
-
|
|
1289
|
+
_as_tcl_value(v)
|
|
1094
1290
|
for v in (
|
|
1095
1291
|
self.test_args + self._get_parameter_options(self.parameters)
|
|
1096
1292
|
)
|
|
1097
1293
|
),
|
|
1098
|
-
PLUSARGS=" ".join(
|
|
1294
|
+
PLUSARGS=" ".join(_as_tcl_value(v) for v in self.plusargs),
|
|
1099
1295
|
)
|
|
1100
1296
|
|
|
1101
1297
|
self.env["GPI_EXTRA"] = (
|
|
1102
|
-
|
|
1298
|
+
cocotb_tools.config.lib_name_path("vhpi", "riviera").as_posix()
|
|
1103
1299
|
+ ":cocotbvhpi_entry_point"
|
|
1104
1300
|
)
|
|
1105
1301
|
|
|
@@ -1108,39 +1304,52 @@ class Riviera(Simulator):
|
|
|
1108
1304
|
|
|
1109
1305
|
do_script += "run -all \nexit"
|
|
1110
1306
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1307
|
+
with tempfile.NamedTemporaryFile(delete=False) as do_file:
|
|
1308
|
+
do_file.write(do_script.encode())
|
|
1309
|
+
|
|
1310
|
+
return [["vsimsa", "-do", "do", do_file.name]]
|
|
1114
1311
|
|
|
1115
|
-
return [["vsimsa"] + ["-do"] + ["do"] + [do_file.name]]
|
|
1116
1312
|
|
|
1313
|
+
class Verilator(Runner):
|
|
1314
|
+
"""Implementation of :class:`Runner` for Verilator.
|
|
1315
|
+
|
|
1316
|
+
* ``waves=True`` *must* be given to :meth:`.build` if either ``waves`` or ``gui`` are to be used during :meth:`.test`.
|
|
1317
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
1318
|
+
"""
|
|
1117
1319
|
|
|
1118
|
-
class Verilator(Simulator):
|
|
1119
1320
|
supported_gpi_interfaces = {"verilog": ["vpi"]}
|
|
1120
1321
|
|
|
1322
|
+
def _set_env(self) -> None:
|
|
1323
|
+
super()._set_env()
|
|
1324
|
+
if "COCOTB_TRUST_INERTIAL_WRITES" not in self.env:
|
|
1325
|
+
self.env["COCOTB_TRUST_INERTIAL_WRITES"] = "1"
|
|
1326
|
+
|
|
1121
1327
|
def _simulator_in_path(self) -> None:
|
|
1122
1328
|
# the verilator binary is only needed for building
|
|
1123
1329
|
return
|
|
1124
1330
|
|
|
1331
|
+
def _use_external_viewer(self) -> bool:
|
|
1332
|
+
return True
|
|
1333
|
+
|
|
1334
|
+
def _waves_file(self) -> Optional[str]:
|
|
1335
|
+
return "dump.vcd"
|
|
1336
|
+
|
|
1125
1337
|
def _simulator_in_path_build_only(self) -> None:
|
|
1126
1338
|
executable = shutil.which("verilator")
|
|
1127
1339
|
if executable is None:
|
|
1128
1340
|
raise SystemExit("ERROR: verilator executable not found!")
|
|
1129
1341
|
self.executable: str = executable
|
|
1130
1342
|
|
|
1131
|
-
|
|
1132
|
-
def _get_include_options(includes: Sequence[PathLike]) -> Command:
|
|
1343
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
1133
1344
|
return [f"-I{include}" for include in includes]
|
|
1134
1345
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
return [f"-D{name}={value}" for name, value in defines.items()]
|
|
1346
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
1347
|
+
return [f"-D{name}={_as_sv_literal(value)}" for name, value in defines.items()]
|
|
1138
1348
|
|
|
1139
|
-
|
|
1140
|
-
def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
|
|
1349
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
1141
1350
|
return [f"-G{name}={value}" for name, value in parameters.items()]
|
|
1142
1351
|
|
|
1143
|
-
def _build_command(self) -> List[
|
|
1352
|
+
def _build_command(self) -> List[_Command]:
|
|
1144
1353
|
self._simulator_in_path_build_only()
|
|
1145
1354
|
|
|
1146
1355
|
for source in self.sources:
|
|
@@ -1150,8 +1359,8 @@ class Verilator(Simulator):
|
|
|
1150
1359
|
)
|
|
1151
1360
|
for arg in self.build_args:
|
|
1152
1361
|
if type(arg) not in (str, Verilog):
|
|
1153
|
-
|
|
1154
|
-
f"
|
|
1362
|
+
raise ValueError(
|
|
1363
|
+
f"{type(self).__qualname__} only supports Verilog. build_args {arg!r} will not be applied."
|
|
1155
1364
|
)
|
|
1156
1365
|
|
|
1157
1366
|
if self.hdl_toplevel is None:
|
|
@@ -1163,11 +1372,7 @@ class Verilator(Simulator):
|
|
|
1163
1372
|
# TODO: support "--always"
|
|
1164
1373
|
|
|
1165
1374
|
verilator_cpp = str(
|
|
1166
|
-
|
|
1167
|
-
/ "share"
|
|
1168
|
-
/ "lib"
|
|
1169
|
-
/ "verilator"
|
|
1170
|
-
/ "verilator.cpp"
|
|
1375
|
+
cocotb_tools.config.share_dir / "lib" / "verilator" / "verilator.cpp"
|
|
1171
1376
|
)
|
|
1172
1377
|
|
|
1173
1378
|
cmds = []
|
|
@@ -1179,7 +1384,6 @@ class Verilator(Simulator):
|
|
|
1179
1384
|
"--exe",
|
|
1180
1385
|
"-Mdir",
|
|
1181
1386
|
str(self.build_dir),
|
|
1182
|
-
"-DCOCOTB_SIM=1",
|
|
1183
1387
|
"--top-module",
|
|
1184
1388
|
self.hdl_toplevel,
|
|
1185
1389
|
"--vpi",
|
|
@@ -1189,12 +1393,15 @@ class Verilator(Simulator):
|
|
|
1189
1393
|
"-o",
|
|
1190
1394
|
self.hdl_toplevel,
|
|
1191
1395
|
"-LDFLAGS",
|
|
1192
|
-
"-Wl,-rpath,{
|
|
1193
|
-
LIB_DIR=cocotb.config.libs_dir
|
|
1194
|
-
),
|
|
1396
|
+
f"-Wl,-rpath,{cocotb_tools.config.libs_dir} -L{cocotb_tools.config.libs_dir} -lcocotbvpi_verilator",
|
|
1195
1397
|
]
|
|
1196
1398
|
+ (["--trace"] if self.waves else [])
|
|
1197
1399
|
+ [arg for arg in self.build_args if type(arg) in (str, Verilog)]
|
|
1400
|
+
+ (
|
|
1401
|
+
["--timescale", "{}/{}".format(*self.timescale)]
|
|
1402
|
+
if self.timescale is not None
|
|
1403
|
+
else []
|
|
1404
|
+
)
|
|
1198
1405
|
+ self._get_define_options(self.defines)
|
|
1199
1406
|
+ self._get_include_options(self.includes)
|
|
1200
1407
|
+ self._get_parameter_options(self.parameters)
|
|
@@ -1206,6 +1413,8 @@ class Verilator(Simulator):
|
|
|
1206
1413
|
cmds.append(
|
|
1207
1414
|
[
|
|
1208
1415
|
"make",
|
|
1416
|
+
"-j",
|
|
1417
|
+
f"{_get_max_parallel_build_jobs()}",
|
|
1209
1418
|
"-C",
|
|
1210
1419
|
str(self.build_dir),
|
|
1211
1420
|
"-f",
|
|
@@ -1216,40 +1425,60 @@ class Verilator(Simulator):
|
|
|
1216
1425
|
|
|
1217
1426
|
return cmds
|
|
1218
1427
|
|
|
1219
|
-
def _test_command(self) -> List[
|
|
1220
|
-
if self.pre_cmd:
|
|
1221
|
-
|
|
1428
|
+
def _test_command(self) -> List[_Command]:
|
|
1429
|
+
if self.pre_cmd is not None:
|
|
1430
|
+
raise RuntimeError("pre_cmd is not implemented for Verilator.")
|
|
1222
1431
|
|
|
1223
1432
|
out_file = self.build_dir / self.sim_hdl_toplevel
|
|
1224
1433
|
return [
|
|
1225
1434
|
[str(out_file)]
|
|
1226
|
-
+ (["--trace"] if self.waves else [])
|
|
1435
|
+
+ (["--trace"] if self.waves or self.gui else [])
|
|
1227
1436
|
+ self.test_args
|
|
1228
1437
|
+ self.plusargs
|
|
1229
1438
|
]
|
|
1230
1439
|
|
|
1231
1440
|
|
|
1232
|
-
class Xcelium(
|
|
1441
|
+
class Xcelium(Runner):
|
|
1442
|
+
"""Implementation of :class:`Runner` for Xcelium.
|
|
1443
|
+
|
|
1444
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
1445
|
+
* Does not support the ``timescale`` argument to :meth:`.build` or :meth:`.test`.
|
|
1446
|
+
"""
|
|
1447
|
+
|
|
1233
1448
|
supported_gpi_interfaces = {"verilog": ["vpi"], "vhdl": ["vhpi"]}
|
|
1234
1449
|
|
|
1235
|
-
|
|
1236
|
-
def _simulator_in_path() -> None:
|
|
1450
|
+
def _simulator_in_path(self) -> None:
|
|
1237
1451
|
if shutil.which("xrun") is None:
|
|
1238
1452
|
raise SystemExit("ERROR: xrun executable not found!")
|
|
1239
1453
|
|
|
1240
|
-
|
|
1241
|
-
def _get_include_options(includes: Sequence[PathLike]) -> Command:
|
|
1454
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
1242
1455
|
return [f"-incdir {include}" for include in includes]
|
|
1243
1456
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1457
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
1458
|
+
return [
|
|
1459
|
+
f"-define {name}={self._as_define_value(value)}"
|
|
1460
|
+
for name, value in defines.items()
|
|
1461
|
+
]
|
|
1462
|
+
|
|
1463
|
+
def _as_define_value(self, value: object) -> str:
|
|
1464
|
+
if isinstance(value, int):
|
|
1465
|
+
return str(value)
|
|
1466
|
+
elif isinstance(value, str):
|
|
1467
|
+
for char in value:
|
|
1468
|
+
if ord(char) < 32 or ord(char) >= 255 or char == '"':
|
|
1469
|
+
# Control characters are generally not supported.
|
|
1470
|
+
# Not sure if there's any way to escape quotes.
|
|
1471
|
+
raise ValueError(
|
|
1472
|
+
f"Character {char!r} not supported in define value"
|
|
1473
|
+
)
|
|
1474
|
+
return '"\\"' + value.replace("\\", "\\\\") + '\\""'
|
|
1475
|
+
else:
|
|
1476
|
+
raise TypeError("Can't serialize this type as an SV literal")
|
|
1247
1477
|
|
|
1248
|
-
|
|
1249
|
-
def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
|
|
1478
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
1250
1479
|
return [f'-gpg "{name} => {value}"' for name, value in parameters.items()]
|
|
1251
1480
|
|
|
1252
|
-
def _build_command(self) -> List[
|
|
1481
|
+
def _build_command(self) -> List[_Command]:
|
|
1253
1482
|
self.env["CDS_AUTO_64BIT"] = "all"
|
|
1254
1483
|
|
|
1255
1484
|
assert self.hdl_toplevel, "A HDL toplevel is required in all Xcelium compiles."
|
|
@@ -1270,9 +1499,10 @@ class Xcelium(Simulator):
|
|
|
1270
1499
|
verbosity_opts += ["-plinowarn"]
|
|
1271
1500
|
|
|
1272
1501
|
vhpi_opts = []
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1502
|
+
if self.vhdl_sources or any(is_vhdl_source(src) for src in self.sources):
|
|
1503
|
+
# Xcelium 23.09.004 fixes cocotb issue #1076 as long as the
|
|
1504
|
+
# following define is set.
|
|
1505
|
+
vhpi_opts.append("-NEW_VHPI_PROPAGATE_DELAY")
|
|
1276
1506
|
|
|
1277
1507
|
cmds = [
|
|
1278
1508
|
["xrun"]
|
|
@@ -1289,13 +1519,12 @@ class Xcelium(Simulator):
|
|
|
1289
1519
|
+ ["-loadvpi"]
|
|
1290
1520
|
# always start with VPI on Xcelium
|
|
1291
1521
|
+ [
|
|
1292
|
-
|
|
1522
|
+
cocotb_tools.config.lib_name_path("vpi", "xcelium").as_posix()
|
|
1293
1523
|
+ ":vlog_startup_routines_bootstrap"
|
|
1294
1524
|
]
|
|
1295
1525
|
+ vhpi_opts
|
|
1296
1526
|
+ [f"-work {self.hdl_library}"]
|
|
1297
1527
|
+ self.build_args
|
|
1298
|
-
+ ["-define COCOTB_SIM"]
|
|
1299
1528
|
+ self._get_include_options(self.includes)
|
|
1300
1529
|
+ self._get_define_options(self.defines)
|
|
1301
1530
|
+ self._get_parameter_options(self.parameters)
|
|
@@ -1310,9 +1539,9 @@ class Xcelium(Simulator):
|
|
|
1310
1539
|
|
|
1311
1540
|
return cmds
|
|
1312
1541
|
|
|
1313
|
-
def _test_command(self) -> List[
|
|
1314
|
-
if self.pre_cmd:
|
|
1315
|
-
|
|
1542
|
+
def _test_command(self) -> List[_Command]:
|
|
1543
|
+
if self.pre_cmd is not None:
|
|
1544
|
+
raise RuntimeError("pre_cmd is not implemented for Xcelium.")
|
|
1316
1545
|
|
|
1317
1546
|
self.env["CDS_AUTO_64BIT"] = "all"
|
|
1318
1547
|
|
|
@@ -1349,39 +1578,242 @@ class Xcelium(Simulator):
|
|
|
1349
1578
|
input_tcl = ["-input", "@run; exit;"]
|
|
1350
1579
|
|
|
1351
1580
|
vhpi_opts = []
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1581
|
+
if self.vhdl_sources or any(is_vhdl_source(src) for src in self.sources):
|
|
1582
|
+
# Xcelium 23.09.004 fixes cocotb issue #1076 as long as the
|
|
1583
|
+
# following define is set.
|
|
1584
|
+
vhpi_opts.append("-NEW_VHPI_PROPAGATE_DELAY")
|
|
1355
1585
|
|
|
1356
1586
|
cmds = [["mkdir", "-p", tmpdir]]
|
|
1357
1587
|
cmds += [
|
|
1358
|
-
[
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1588
|
+
[
|
|
1589
|
+
"xrun",
|
|
1590
|
+
"-logfile",
|
|
1591
|
+
f"xrun_{self.current_test_name}.log",
|
|
1592
|
+
"-xmlibdirname",
|
|
1593
|
+
f"{self.build_dir}/xrun_snapshot",
|
|
1594
|
+
"-cds_implicit_tmpdir",
|
|
1595
|
+
tmpdir,
|
|
1596
|
+
"-licqueue",
|
|
1597
|
+
*vhpi_opts,
|
|
1598
|
+
*verbosity_opts,
|
|
1599
|
+
"-R",
|
|
1600
|
+
*self.test_args,
|
|
1601
|
+
*self.plusargs,
|
|
1602
|
+
"-gui" if self.gui else "",
|
|
1603
|
+
*input_tcl,
|
|
1604
|
+
]
|
|
1373
1605
|
]
|
|
1374
1606
|
self.env["GPI_EXTRA"] = (
|
|
1375
|
-
|
|
1607
|
+
cocotb_tools.config.lib_name_path("vhpi", "xcelium").as_posix()
|
|
1608
|
+
+ ":cocotbvhpi_entry_point"
|
|
1376
1609
|
)
|
|
1377
1610
|
|
|
1378
1611
|
return cmds
|
|
1379
1612
|
|
|
1380
1613
|
|
|
1381
|
-
|
|
1382
|
-
"""
|
|
1614
|
+
class Vcs(Runner):
|
|
1615
|
+
"""Implementation of :class:`Runner` for VCS.
|
|
1616
|
+
|
|
1617
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
1618
|
+
* Does not support VHDL.
|
|
1619
|
+
* Does not support the ``timescale`` argument to :meth:`.build` or :meth:`.test`.
|
|
1620
|
+
"""
|
|
1621
|
+
|
|
1622
|
+
supported_gpi_interfaces = {"verilog": ["vpi"]}
|
|
1623
|
+
|
|
1624
|
+
def _simulator_in_path(self) -> None:
|
|
1625
|
+
if shutil.which("vcs") is None:
|
|
1626
|
+
raise SystemExit("ERROR: vcs executable not found!")
|
|
1627
|
+
|
|
1628
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
1629
|
+
return [f"+incdir+{include}" for include in includes]
|
|
1630
|
+
|
|
1631
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
1632
|
+
return [
|
|
1633
|
+
f"+define+{name}={_as_sv_literal(value)}" for name, value in defines.items()
|
|
1634
|
+
]
|
|
1635
|
+
|
|
1636
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
1637
|
+
assert self.hdl_toplevel is not None
|
|
1638
|
+
return [
|
|
1639
|
+
f"-pvalue+{self.hdl_toplevel}.{name}={value}"
|
|
1640
|
+
for name, value in parameters.items()
|
|
1641
|
+
]
|
|
1642
|
+
|
|
1643
|
+
@property
|
|
1644
|
+
def sim_file(self) -> Path:
|
|
1645
|
+
return self.build_dir / "simv"
|
|
1646
|
+
|
|
1647
|
+
@property
|
|
1648
|
+
def _build_opts(self) -> List[str]:
|
|
1649
|
+
opts = [
|
|
1650
|
+
"-full64",
|
|
1651
|
+
"-debug_access+all",
|
|
1652
|
+
"+acc+3",
|
|
1653
|
+
"-sverilog",
|
|
1654
|
+
"-LDFLAGS -Wl,--no-as-needed",
|
|
1655
|
+
]
|
|
1656
|
+
|
|
1657
|
+
if self.verbose:
|
|
1658
|
+
opts += ["-diag all"]
|
|
1659
|
+
else:
|
|
1660
|
+
opts += ["-q"]
|
|
1661
|
+
opts += ["-suppress=VPI-CT-NS"]
|
|
1662
|
+
|
|
1663
|
+
return opts
|
|
1664
|
+
|
|
1665
|
+
def _build_command(self) -> List[_Command]:
|
|
1666
|
+
cmds: List[_Command] = []
|
|
1667
|
+
sources = list(self.sources + self.vhdl_sources + self.verilog_sources)
|
|
1668
|
+
|
|
1669
|
+
if outdated(self.sim_file, sources) or self.always:
|
|
1670
|
+
cmds = [
|
|
1671
|
+
["vcs"]
|
|
1672
|
+
+ self._build_opts
|
|
1673
|
+
+ ["-load", cocotb_tools.config.lib_name_path("vpi", "vcs").as_posix()]
|
|
1674
|
+
+ self.build_args
|
|
1675
|
+
+ self._get_include_options(self.includes)
|
|
1676
|
+
+ self._get_define_options(self.defines)
|
|
1677
|
+
+ self._get_parameter_options(self.parameters)
|
|
1678
|
+
+ ["-top", f"{self.hdl_toplevel}"]
|
|
1679
|
+
+ [str(source) for source in sources]
|
|
1680
|
+
+ ["-o", str(self.sim_file)]
|
|
1681
|
+
]
|
|
1682
|
+
else:
|
|
1683
|
+
self.log.warning("Skipping compilation of %s", self.sim_file)
|
|
1684
|
+
|
|
1685
|
+
return cmds
|
|
1686
|
+
|
|
1687
|
+
def _test_command(self) -> List[_Command]:
|
|
1688
|
+
if self.pre_cmd is not None:
|
|
1689
|
+
raise RuntimeError("pre_cmd is not implemented for Vcs.")
|
|
1690
|
+
|
|
1691
|
+
verbosity_opts = []
|
|
1692
|
+
if self.verbose:
|
|
1693
|
+
verbosity_opts += ["-diag all"]
|
|
1694
|
+
else:
|
|
1695
|
+
verbosity_opts += ["-suppress=ASLR_DETECTED_INFO"]
|
|
1696
|
+
|
|
1697
|
+
cmds = [[str(self.sim_file), *verbosity_opts, *self.test_args, *self.plusargs]]
|
|
1698
|
+
|
|
1699
|
+
return cmds
|
|
1700
|
+
|
|
1701
|
+
|
|
1702
|
+
class Dsim(Runner):
|
|
1703
|
+
"""Implementation of :class:`Runner` for DSim.
|
|
1704
|
+
|
|
1705
|
+
* Does not support the ``pre_cmd`` argument to :meth:`.test`.
|
|
1706
|
+
"""
|
|
1707
|
+
|
|
1708
|
+
supported_gpi_interfaces = {"verilog": ["vpi"]}
|
|
1709
|
+
|
|
1710
|
+
def _simulator_in_path(self) -> None:
|
|
1711
|
+
if shutil.which("dsim") is None:
|
|
1712
|
+
raise SystemExit("ERROR: dsim executable not found!")
|
|
1713
|
+
|
|
1714
|
+
def _get_include_options(self, includes: Sequence[PathLike]) -> _Command:
|
|
1715
|
+
return [f"+incdir+{include}" for include in includes]
|
|
1716
|
+
|
|
1717
|
+
def _get_define_options(self, defines: Mapping[str, object]) -> _Command:
|
|
1718
|
+
return [
|
|
1719
|
+
f"+define+{name}={_as_sv_literal(value)}" for name, value in defines.items()
|
|
1720
|
+
]
|
|
1721
|
+
|
|
1722
|
+
def _get_parameter_options(self, parameters: Mapping[str, object]) -> _Command:
|
|
1723
|
+
return [
|
|
1724
|
+
f"-defparam {name}={_as_sv_literal(value)}"
|
|
1725
|
+
for name, value in parameters.items()
|
|
1726
|
+
]
|
|
1727
|
+
|
|
1728
|
+
@property
|
|
1729
|
+
def sim_file(self) -> Path:
|
|
1730
|
+
return self.build_dir / "image.so"
|
|
1731
|
+
|
|
1732
|
+
def _use_external_viewer(self) -> bool:
|
|
1733
|
+
return True
|
|
1734
|
+
|
|
1735
|
+
def _waves_file(self) -> Optional[str]:
|
|
1736
|
+
return "file.vcd"
|
|
1737
|
+
|
|
1738
|
+
def _test_command(self) -> List[_Command]:
|
|
1739
|
+
plusargs = self.plusargs
|
|
1740
|
+
if self.waves or self.gui:
|
|
1741
|
+
plusargs += [f"-waves {self._waves_file()}"]
|
|
1742
|
+
|
|
1743
|
+
if self.timescale:
|
|
1744
|
+
plusargs += ["-timescale {}/{}".format(*self.timescale)]
|
|
1745
|
+
if self.pre_cmd is not None:
|
|
1746
|
+
raise ValueError("WARNING: pre_cmd is not implemented for DSim.")
|
|
1747
|
+
|
|
1748
|
+
return [
|
|
1749
|
+
[
|
|
1750
|
+
"dsim",
|
|
1751
|
+
"-work",
|
|
1752
|
+
str(self.build_dir),
|
|
1753
|
+
"-pli_lib",
|
|
1754
|
+
cocotb_tools.config.lib_name_path("vpi", "dsim").as_posix(),
|
|
1755
|
+
"+acc+rwcbfsWF",
|
|
1756
|
+
"-image",
|
|
1757
|
+
"image",
|
|
1758
|
+
*self.test_args,
|
|
1759
|
+
*plusargs,
|
|
1760
|
+
]
|
|
1761
|
+
]
|
|
1762
|
+
|
|
1763
|
+
def _build_command(self) -> List[_Command]:
|
|
1764
|
+
for source in self.sources:
|
|
1765
|
+
if not is_verilog_source(source):
|
|
1766
|
+
raise ValueError(
|
|
1767
|
+
f"{type(self).__qualname__} only supports Verilog. {str(source)!r} cannot be compiled."
|
|
1768
|
+
)
|
|
1769
|
+
for arg in self.build_args:
|
|
1770
|
+
if type(arg) not in (str, Verilog):
|
|
1771
|
+
raise ValueError(
|
|
1772
|
+
f"{type(self).__qualname__} only supports Verilog. build_args {arg!r} cannot be applied."
|
|
1773
|
+
)
|
|
1774
|
+
|
|
1775
|
+
build_args = list(self.build_args)
|
|
1776
|
+
|
|
1777
|
+
cmds: list[_Command] = []
|
|
1778
|
+
sources = [
|
|
1779
|
+
source for source in self.sources if is_verilog_source(source)
|
|
1780
|
+
] + self.verilog_sources
|
|
1781
|
+
if outdated(self.sim_file, sources) or self.always:
|
|
1782
|
+
cmds = [
|
|
1783
|
+
[
|
|
1784
|
+
"dsim",
|
|
1785
|
+
"-work",
|
|
1786
|
+
str(self.build_dir),
|
|
1787
|
+
"-pli_lib",
|
|
1788
|
+
cocotb_tools.config.lib_name_path("vpi", "dsim").as_posix(),
|
|
1789
|
+
"+acc+rwcbfsWF",
|
|
1790
|
+
"-genimage",
|
|
1791
|
+
"image",
|
|
1792
|
+
]
|
|
1793
|
+
+ self._get_define_options(self.defines)
|
|
1794
|
+
+ self._get_include_options(self.includes)
|
|
1795
|
+
+ self._get_parameter_options(self.parameters)
|
|
1796
|
+
+ [arg for arg in build_args if type(arg) in (str, Verilog)]
|
|
1797
|
+
+ [str(source_file) for source_file in sources]
|
|
1798
|
+
]
|
|
1799
|
+
|
|
1800
|
+
else:
|
|
1801
|
+
self.log.warning("Skipping compilation of %s", self.sim_file)
|
|
1802
|
+
|
|
1803
|
+
return cmds
|
|
1804
|
+
|
|
1805
|
+
|
|
1806
|
+
def get_runner(simulator_name: str) -> Runner:
|
|
1807
|
+
"""Return an instance of a runner for *simulator_name*.
|
|
1808
|
+
|
|
1809
|
+
Args:
|
|
1810
|
+
simulator_name: Name of simulator to get runner for.
|
|
1811
|
+
|
|
1812
|
+
Raises:
|
|
1813
|
+
ValueError: If *simulator_name* is not one of the supported simulators or an alias of one.
|
|
1814
|
+
"""
|
|
1383
1815
|
|
|
1384
|
-
supported_sims: Dict[str, Type[
|
|
1816
|
+
supported_sims: Dict[str, Type[Runner]] = {
|
|
1385
1817
|
"icarus": Icarus,
|
|
1386
1818
|
"questa": Questa,
|
|
1387
1819
|
"ghdl": Ghdl,
|
|
@@ -1389,7 +1821,8 @@ def get_runner(simulator_name: str) -> Simulator:
|
|
|
1389
1821
|
"verilator": Verilator,
|
|
1390
1822
|
"xcelium": Xcelium,
|
|
1391
1823
|
"nvc": Nvc,
|
|
1392
|
-
|
|
1824
|
+
"vcs": Vcs,
|
|
1825
|
+
"dsim": Dsim,
|
|
1393
1826
|
# TODO: "activehdl": ActiveHdl,
|
|
1394
1827
|
}
|
|
1395
1828
|
try:
|