cocotb 1.9.2__cp313-cp313-win32.whl → 2.0.0rc2__cp313-cp313-win32.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.

Files changed (161) hide show
  1. cocotb/_ANSI.py +65 -0
  2. cocotb/__init__.py +81 -327
  3. cocotb/_base_triggers.py +515 -0
  4. cocotb/_bridge.py +186 -0
  5. cocotb/_decorators.py +515 -0
  6. cocotb/_deprecation.py +3 -3
  7. cocotb/_exceptions.py +7 -0
  8. cocotb/_extended_awaitables.py +419 -0
  9. cocotb/_gpi_triggers.py +385 -0
  10. cocotb/_init.py +301 -0
  11. cocotb/_outcomes.py +54 -0
  12. cocotb/_profiling.py +46 -0
  13. cocotb/_py_compat.py +114 -29
  14. cocotb/_scheduler.py +448 -0
  15. cocotb/_test.py +248 -0
  16. cocotb/_test_factory.py +312 -0
  17. cocotb/_test_functions.py +42 -0
  18. cocotb/_typing.py +7 -0
  19. cocotb/_utils.py +274 -0
  20. cocotb/_version.py +3 -7
  21. cocotb/_xunit_reporter.py +66 -0
  22. cocotb/clock.py +353 -108
  23. cocotb/debug.py +24 -0
  24. cocotb/handle.py +1370 -793
  25. cocotb/libs/cocotb.dll +0 -0
  26. cocotb/libs/cocotb.exp +0 -0
  27. cocotb/libs/cocotb.lib +0 -0
  28. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  29. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  30. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  31. cocotb/libs/cocotbutils.dll +0 -0
  32. cocotb/libs/cocotbutils.exp +0 -0
  33. cocotb/libs/cocotbutils.lib +0 -0
  34. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  35. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  36. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  37. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  38. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  39. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  40. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  41. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  42. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  43. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  44. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  45. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  46. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  47. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  48. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  49. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  50. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  51. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  52. cocotb/libs/embed.dll +0 -0
  53. cocotb/libs/embed.exp +0 -0
  54. cocotb/libs/embed.lib +0 -0
  55. cocotb/libs/gpi.dll +0 -0
  56. cocotb/libs/gpi.exp +0 -0
  57. cocotb/libs/gpi.lib +0 -0
  58. cocotb/libs/gpilog.dll +0 -0
  59. cocotb/libs/gpilog.exp +0 -0
  60. cocotb/libs/gpilog.lib +0 -0
  61. cocotb/libs/pygpilog.dll +0 -0
  62. cocotb/libs/pygpilog.exp +0 -0
  63. cocotb/libs/pygpilog.lib +0 -0
  64. cocotb/logging.py +424 -0
  65. cocotb/queue.py +103 -57
  66. cocotb/regression.py +680 -717
  67. cocotb/result.py +17 -188
  68. cocotb/share/def/aldec.exp +0 -0
  69. cocotb/share/def/aldec.lib +0 -0
  70. cocotb/share/def/ghdl.exp +0 -0
  71. cocotb/share/def/ghdl.lib +0 -0
  72. cocotb/share/def/icarus.exp +0 -0
  73. cocotb/share/def/icarus.lib +0 -0
  74. cocotb/share/def/modelsim.def +1 -0
  75. cocotb/share/def/modelsim.exp +0 -0
  76. cocotb/share/def/modelsim.lib +0 -0
  77. cocotb/share/include/cocotb_utils.h +9 -32
  78. cocotb/share/include/embed.h +7 -30
  79. cocotb/share/include/gpi.h +331 -137
  80. cocotb/share/include/gpi_logging.h +221 -142
  81. cocotb/share/include/py_gpi_logging.h +8 -5
  82. cocotb/share/include/vpi_user_ext.h +4 -26
  83. cocotb/share/lib/verilator/verilator.cpp +80 -67
  84. cocotb/simtime.py +230 -0
  85. cocotb/simulator.cp313-win32.exp +0 -0
  86. cocotb/simulator.cp313-win32.lib +0 -0
  87. cocotb/simulator.cp313-win32.pyd +0 -0
  88. cocotb/simulator.pyi +107 -0
  89. cocotb/task.py +478 -213
  90. cocotb/triggers.py +55 -1092
  91. cocotb/types/__init__.py +28 -47
  92. cocotb/types/_abstract_array.py +151 -0
  93. cocotb/types/_array.py +295 -0
  94. cocotb/types/_indexing.py +17 -0
  95. cocotb/types/_logic.py +333 -0
  96. cocotb/types/_logic_array.py +868 -0
  97. cocotb/types/{range.py → _range.py} +47 -48
  98. cocotb/types/_resolve.py +76 -0
  99. cocotb/utils.py +58 -646
  100. cocotb-2.0.0rc2.dist-info/METADATA +60 -0
  101. cocotb-2.0.0rc2.dist-info/RECORD +146 -0
  102. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +1 -1
  103. cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
  104. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info/licenses}/LICENSE +1 -0
  105. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +1 -0
  106. cocotb_tools/__init__.py +0 -0
  107. cocotb_tools/_coverage.py +33 -0
  108. cocotb_tools/_vendor/__init__.py +3 -0
  109. cocotb_tools/check_results.py +65 -0
  110. cocotb_tools/combine_results.py +152 -0
  111. cocotb_tools/config.py +241 -0
  112. {cocotb → cocotb_tools}/ipython_support.py +29 -22
  113. cocotb_tools/makefiles/Makefile.deprecations +27 -0
  114. {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +77 -55
  115. {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +16 -33
  116. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
  117. cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
  118. cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
  119. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
  120. cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
  121. cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
  122. cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
  123. cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
  124. cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
  125. cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
  126. cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
  127. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
  128. cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
  129. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
  130. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
  131. cocotb_tools/py.typed +0 -0
  132. cocotb_tools/runner.py +1868 -0
  133. cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
  134. pygpi/entry.py +34 -18
  135. pygpi/py.typed +0 -0
  136. cocotb/ANSI.py +0 -92
  137. cocotb/binary.py +0 -858
  138. cocotb/config.py +0 -289
  139. cocotb/decorators.py +0 -332
  140. cocotb/log.py +0 -303
  141. cocotb/memdebug.py +0 -35
  142. cocotb/outcomes.py +0 -56
  143. cocotb/runner.py +0 -1400
  144. cocotb/scheduler.py +0 -1099
  145. cocotb/share/makefiles/Makefile.deprecations +0 -12
  146. cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
  147. cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
  148. cocotb/share/makefiles/simulators/Makefile.ius +0 -125
  149. cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
  150. cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
  151. cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
  152. cocotb/types/array.py +0 -309
  153. cocotb/types/logic.py +0 -292
  154. cocotb/types/logic_array.py +0 -298
  155. cocotb/wavedrom.py +0 -199
  156. cocotb/xunit_reporter.py +0 -80
  157. cocotb-1.9.2.dist-info/METADATA +0 -168
  158. cocotb-1.9.2.dist-info/RECORD +0 -121
  159. cocotb-1.9.2.dist-info/entry_points.txt +0 -2
  160. /cocotb/{_vendor/__init__.py → py.typed} +0 -0
  161. {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
cocotb/runner.py DELETED
@@ -1,1400 +0,0 @@
1
- # Copyright cocotb contributors
2
- # Licensed under the Revised BSD License, see LICENSE for details.
3
- # SPDX-License-Identifier: BSD-3-Clause
4
-
5
- """Build HDL and run cocotb tests."""
6
-
7
- # TODO: maybe do globbing and expanduser/expandvars in --include, --vhdl-sources, --verilog-sources
8
- # TODO: create a short README and a .gitignore (content: "*") in both build_dir and test_dir? (Some other tools do this.)
9
- # TODO: support timescale on all simulators
10
- # TODO: support custom dependencies
11
-
12
- import abc
13
- import os
14
- import re
15
- import shlex
16
- import shutil
17
- import subprocess
18
- import sys
19
- import tempfile
20
- import warnings
21
- from contextlib import suppress
22
- from pathlib import Path
23
- from typing import Dict, List, Mapping, Optional, Sequence, TextIO, Tuple, Type, Union
24
- from xml.etree import ElementTree as ET
25
-
26
- import find_libpython
27
-
28
- import cocotb.config
29
-
30
- PathLike = Union["os.PathLike[str]", str]
31
- Command = List[str]
32
- Timescale = Tuple[str, str]
33
-
34
- warnings.warn(
35
- "Python runners and associated APIs are an experimental feature and subject to change.",
36
- UserWarning,
37
- stacklevel=2,
38
- )
39
-
40
- _magic_re = re.compile(r"([\\{}])")
41
- _space_re = re.compile(r"([\s])", re.ASCII)
42
-
43
-
44
- def as_tcl_value(value: str) -> str:
45
- # add '\' before special characters and spaces
46
- value = _magic_re.sub(r"\\\1", value)
47
- value = value.replace("\n", r"\n")
48
- value = _space_re.sub(r"\\\1", value)
49
- if value[0] == '"':
50
- value = "\\" + value
51
-
52
- return value
53
-
54
-
55
- def shlex_join(split_command):
56
- """
57
- Return a shell-escaped string from *split_command*
58
- This is here more for compatibility purposes
59
- """
60
- return " ".join(shlex.quote(arg) for arg in split_command)
61
-
62
-
63
- class VHDL(str):
64
- """Tags source files and build arguments to :meth:`~cocotb.runner.Simulator.build` as VHDL-specific."""
65
-
66
-
67
- class Verilog(str):
68
- """Tags source files and build arguments to :meth:`~cocotb.runner.Simulator.build` as Verilog-specific."""
69
-
70
-
71
- class Simulator(abc.ABC):
72
-
73
- supported_gpi_interfaces: Dict[str, List[str]] = {}
74
-
75
- def __init__(self) -> None:
76
-
77
- self._simulator_in_path()
78
-
79
- self.env: Dict[str, str] = {}
80
-
81
- # for running test() independently of build()
82
- self.build_dir: Path = get_abs_path("sim_build")
83
- self.parameters: Mapping[str, object] = {}
84
-
85
- @abc.abstractmethod
86
- def _simulator_in_path(self) -> None:
87
- """Raise exception if the simulator executable does not exist in :envvar:`PATH`.
88
-
89
- Raises:
90
- SystemExit: Simulator executable does not exist in :envvar:`PATH`.
91
- """
92
-
93
- raise NotImplementedError()
94
-
95
- def _check_hdl_toplevel_lang(self, hdl_toplevel_lang: Optional[str]) -> str:
96
- """Return *hdl_toplevel_lang* if supported by simulator, raise exception otherwise.
97
-
98
- Returns:
99
- *hdl_toplevel_lang* if supported by the simulator.
100
-
101
- Raises:
102
- ValueError: *hdl_toplevel_lang* is not supported by the simulator.
103
- """
104
- if hdl_toplevel_lang is None:
105
- if self.vhdl_sources and not self.verilog_sources and not self.sources:
106
- lang = "vhdl"
107
- elif self.verilog_sources and not self.vhdl_sources and not self.sources:
108
- lang = "verilog"
109
- elif self.sources and not self.vhdl_sources and not self.verilog_sources:
110
- if is_vhdl_source(self.sources[-1]):
111
- lang = "vhdl"
112
- elif is_verilog_source(self.sources[-1]):
113
- lang = "verilog"
114
- else:
115
- raise UnknownFileExtension(self.sources[-1])
116
- else:
117
- raise ValueError(
118
- f"{type(self).__qualname__}: Must specify a hdl_toplevel_lang in a mixed-language design"
119
- )
120
- else:
121
- lang = hdl_toplevel_lang
122
-
123
- if lang in self.supported_gpi_interfaces.keys():
124
- return lang
125
- else:
126
- raise ValueError(
127
- f"{type(self).__qualname__}: hdl_toplevel_lang {hdl_toplevel_lang!r} is not "
128
- f"in supported list: {', '.join(self.supported_gpi_interfaces.keys())}"
129
- )
130
-
131
- def _set_env(self) -> None:
132
- """Set environment variables for sub-processes."""
133
-
134
- for e in os.environ:
135
- self.env[e] = os.environ[e]
136
-
137
- if "LIBPYTHON_LOC" not in self.env:
138
- libpython_path = find_libpython.find_libpython()
139
- if not libpython_path:
140
- raise ValueError(
141
- "Unable to find libpython, please make sure the appropriate libpython is installed"
142
- )
143
- self.env["LIBPYTHON_LOC"] = libpython_path
144
-
145
- self.env["PATH"] += os.pathsep + cocotb.config.libs_dir
146
- self.env["PYTHONPATH"] = os.pathsep.join(sys.path)
147
- self.env["PYTHONHOME"] = sys.prefix
148
- self.env["TOPLEVEL"] = self.sim_hdl_toplevel
149
- self.env["MODULE"] = self.test_module
150
-
151
- @abc.abstractmethod
152
- def _build_command(self) -> Sequence[Command]:
153
- """Return command to build the HDL sources."""
154
-
155
- raise NotImplementedError()
156
-
157
- @abc.abstractmethod
158
- def _test_command(self) -> Sequence[Command]:
159
- """Return command to run a test."""
160
-
161
- raise NotImplementedError()
162
-
163
- def build(
164
- self,
165
- hdl_library: str = "top",
166
- verilog_sources: Sequence[PathLike] = [],
167
- vhdl_sources: Sequence[PathLike] = [],
168
- sources: Sequence[Union[PathLike, VHDL, Verilog]] = [],
169
- includes: Sequence[PathLike] = [],
170
- defines: Mapping[str, object] = {},
171
- parameters: Mapping[str, object] = {},
172
- build_args: Sequence[Union[str, VHDL, Verilog]] = [],
173
- hdl_toplevel: Optional[str] = None,
174
- always: bool = False,
175
- build_dir: PathLike = "sim_build",
176
- clean: bool = False,
177
- verbose: bool = False,
178
- timescale: Optional[Timescale] = None,
179
- waves: Optional[bool] = None,
180
- log_file: Optional[PathLike] = None,
181
- ) -> None:
182
- """Build the HDL sources.
183
-
184
- With mixed language simulators, *sources* will be built,
185
- followed by *vhdl_sources*, then *verilog_sources*.
186
- With simulators that only support either VHDL or Verilog, *sources* will be built,
187
- followed by *vhdl_sources* and *verilog_sources*, respectively.
188
-
189
- If your source files use an atypical file extension,
190
- use :class:`VHDL` and :class:`Verilog` to tag the path as a VHDL or Verilog source file, respectively.
191
- If the filepaths aren't tagged, the extension is used to determine if they are VHDL or Verilog files.
192
-
193
- +----------+------------------------------------+
194
- | Language | File Extensions |
195
- +==========+====================================+
196
- | VHDL | ``.vhd``, ``.vhdl`` |
197
- +----------+------------------------------------+
198
- | Verilog | ``.v``, ``.sv``, ``.vh``, ``.svh`` |
199
- +----------+------------------------------------+
200
-
201
-
202
- .. code-block:: python3
203
-
204
- runner.build(
205
- sources=[
206
- VHDL("/my/file.is_actually_vhdl"),
207
- Verilog("/other/file.verilog"),
208
- ],
209
- )
210
-
211
- The same tagging works for *build_args*.
212
- Tagged *build_args* only supply that option to the compiler when building the source file for the tagged language.
213
- Non-tagged *build_args* are supplied when compiling any language.
214
-
215
- Args:
216
- hdl_library: The library name to compile into.
217
- verilog_sources: Verilog source files to build.
218
- vhdl_sources: VHDL source files to build.
219
- sources: Language-agnostic list of source files to build.
220
- includes: Verilog include directories.
221
- defines: Defines to set.
222
- parameters: Verilog parameters or VHDL generics.
223
- build_args: Extra build arguments for the simulator.
224
- hdl_toplevel: The name of the HDL toplevel module.
225
- always: Always run the build step.
226
- build_dir: Directory to run the build step in.
227
- clean: Delete build_dir before building
228
- verbose: Enable verbose messages.
229
- timescale: Tuple containing time unit and time precision for simulation.
230
- waves: Record signal traces.
231
- log_file: File to write the build log to.
232
- """
233
-
234
- self.clean: bool = clean
235
- self.build_dir = get_abs_path(build_dir)
236
- if self.clean:
237
- self.rm_build_folder(self.build_dir)
238
- os.makedirs(self.build_dir, exist_ok=True)
239
-
240
- # note: to avoid mutating argument defaults, we ensure that no value
241
- # is written without a copy. This is much more concise and leads to
242
- # a better docstring than using `None` as a default in the parameters
243
- # list.
244
- self.hdl_library: str = hdl_library
245
- self.verilog_sources: List[Path] = get_abs_paths(verilog_sources)
246
- self.vhdl_sources: List[Path] = get_abs_paths(vhdl_sources)
247
- self.sources: List[Path] = get_abs_paths(sources)
248
- self.includes: List[Path] = get_abs_paths(includes)
249
- self.defines = dict(defines)
250
- self.parameters = dict(parameters)
251
- self.build_args = list(build_args)
252
- self.always: bool = always
253
- self.hdl_toplevel: Optional[str] = hdl_toplevel
254
- self.verbose: bool = verbose
255
- self.timescale: Optional[Timescale] = timescale
256
- self.log_file: Optional[PathLike] = log_file
257
-
258
- self.waves = bool(waves)
259
-
260
- for e in os.environ:
261
- self.env[e] = os.environ[e]
262
-
263
- cmds: Sequence[Command] = self._build_command()
264
- self._execute(cmds, cwd=self.build_dir)
265
-
266
- def test(
267
- self,
268
- test_module: Union[str, Sequence[str]],
269
- hdl_toplevel: str,
270
- hdl_toplevel_library: str = "top",
271
- hdl_toplevel_lang: Optional[str] = None,
272
- gpi_interfaces: Optional[List[str]] = None,
273
- testcase: Optional[Union[str, Sequence[str]]] = None,
274
- seed: Optional[Union[str, int]] = None,
275
- test_args: Sequence[str] = [],
276
- plusargs: Sequence[str] = [],
277
- extra_env: Mapping[str, str] = {},
278
- waves: Optional[bool] = None,
279
- gui: Optional[bool] = None,
280
- parameters: Mapping[str, object] = None,
281
- build_dir: Optional[PathLike] = None,
282
- test_dir: Optional[PathLike] = None,
283
- results_xml: Optional[str] = None,
284
- pre_cmd: List[str] = [],
285
- verbose: bool = False,
286
- timescale: Optional[Timescale] = None,
287
- log_file: Optional[PathLike] = None,
288
- ) -> Path:
289
- """Run the tests.
290
-
291
- Args:
292
- test_module: Name(s) of the Python module(s) containing the tests to run.
293
- Can be a comma-separated list.
294
- hdl_toplevel: Name of the HDL toplevel module.
295
- hdl_toplevel_library: The library name for HDL toplevel module.
296
- hdl_toplevel_lang: Language of the HDL toplevel module.
297
- gpi_interfaces: List of GPI interfaces to use, with the first one being the entry point.
298
- testcase: Name(s) of a specific testcase(s) to run.
299
- If not set, run all testcases found in *test_module*.
300
- Can be a comma-separated list.
301
- seed: A specific random seed to use.
302
- test_args: Extra arguments for the simulator.
303
- plusargs: 'plusargs' to set for the simulator.
304
- extra_env: Extra environment variables to set.
305
- waves: Record signal traces.
306
- gui: Run with simulator GUI.
307
- parameters: Verilog parameters or VHDL generics.
308
- build_dir: Directory the build step has been run in.
309
- test_dir: Directory to run the tests in.
310
- results_xml: Name of xUnit XML file to store test results in.
311
- If an absolute path is provided it will be used as-is,
312
- ``{build_dir}/results.xml`` otherwise.
313
- This argument should not be set when run with ``pytest``.
314
- verbose: Enable verbose messages.
315
- pre_cmd: Commands to run before simulation begins.
316
- timescale: Tuple containing time unit and time precision for simulation.
317
- log_file: File to write the test log to.
318
-
319
- Returns:
320
- The absolute location of the results XML file which can be
321
- defined by the *results_xml* argument.
322
- """
323
-
324
- __tracebackhide__ = True # Hide the traceback when using pytest
325
-
326
- if build_dir is not None:
327
- self.build_dir = get_abs_path(build_dir)
328
-
329
- if parameters is not None:
330
- self.parameters = dict(parameters)
331
-
332
- if test_dir is None:
333
- self.test_dir = self.build_dir
334
- else:
335
- self.test_dir = get_abs_path(test_dir)
336
- os.makedirs(self.test_dir, exist_ok=True)
337
-
338
- if isinstance(test_module, str):
339
- self.test_module = test_module
340
- else:
341
- self.test_module = ",".join(test_module)
342
-
343
- # note: to avoid mutating argument defaults, we ensure that no value
344
- # is written without a copy. This is much more concise and leads to
345
- # a better docstring than using `None` as a default in the parameters
346
- # list.
347
- self.sim_hdl_toplevel = hdl_toplevel
348
- self.hdl_toplevel_library: str = hdl_toplevel_library
349
- self.hdl_toplevel_lang = self._check_hdl_toplevel_lang(hdl_toplevel_lang)
350
- if gpi_interfaces:
351
- self.gpi_interfaces = gpi_interfaces
352
- else:
353
- self.gpi_interfaces = []
354
- for gpi_if in self.supported_gpi_interfaces.values():
355
- self.gpi_interfaces.append(gpi_if[0])
356
-
357
- self.pre_cmd = pre_cmd
358
-
359
- self.test_args = list(test_args)
360
- self.plusargs = list(plusargs)
361
- self.env = dict(extra_env)
362
-
363
- if testcase is not None:
364
- if isinstance(testcase, str):
365
- self.env["TESTCASE"] = testcase
366
- else:
367
- self.env["TESTCASE"] = ",".join(testcase)
368
-
369
- if seed is not None:
370
- self.env["RANDOM_SEED"] = str(seed)
371
-
372
- self.log_file = log_file
373
- self.waves = bool(waves)
374
- self.gui = bool(gui)
375
- self.timescale: Optional[Timescale] = timescale
376
-
377
- if verbose is not None:
378
- self.verbose = verbose
379
-
380
- # When using pytest, use test name as result file name
381
- pytest_current_test = os.getenv("PYTEST_CURRENT_TEST", None)
382
- test_dir_path = Path(self.test_dir)
383
- self.current_test_name = "test"
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:
393
- 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
- else:
396
- results_xml_file = test_dir_path / "results.xml"
397
-
398
- with suppress(OSError):
399
- os.remove(results_xml_file)
400
-
401
- # transport the settings to cocotb via environment variables
402
- self._set_env()
403
- self.env["COCOTB_RESULTS_FILE"] = str(results_xml_file)
404
-
405
- cmds: Sequence[Command] = self._test_command()
406
- self._execute(cmds, cwd=self.test_dir)
407
-
408
- # Only when running under pytest, check the results file here,
409
- # potentially raising an exception with failing testcases,
410
- # otherwise return the results file for later analysis.
411
- if pytest_current_test:
412
- check_results_file(results_xml_file)
413
-
414
- print(f"INFO: Results file: {results_xml_file}")
415
- return results_xml_file
416
-
417
- @staticmethod
418
- def _get_include_options(self, includes: Sequence[PathLike]) -> Command:
419
- """Return simulator-specific formatted option strings with *includes* directories."""
420
-
421
- raise NotImplementedError()
422
-
423
- @staticmethod
424
- def _get_define_options(self, defines: Mapping[str, object]) -> Command:
425
- """Return simulator-specific formatted option strings with *defines* macros."""
426
-
427
- raise NotImplementedError()
428
-
429
- @abc.abstractmethod
430
- def _get_parameter_options(self, parameters: Mapping[str, object]) -> Command:
431
- """Return simulator-specific formatted option strings with *parameters*/generics."""
432
-
433
- raise NotImplementedError()
434
-
435
- def _execute(self, cmds: Sequence[Command], cwd: PathLike) -> None:
436
-
437
- __tracebackhide__ = True # Hide the traceback when using PyTest.
438
-
439
- if self.log_file is None:
440
- self._execute_cmds(cmds, cwd)
441
- else:
442
- with open(self.log_file, "w") as f:
443
- self._execute_cmds(cmds, cwd, f)
444
-
445
- def _execute_cmds(
446
- self, cmds: Sequence[Command], cwd: PathLike, stdout: Optional[TextIO] = None
447
- ) -> None:
448
- __tracebackhide__ = True # Hide the traceback when using PyTest.
449
-
450
- for cmd in cmds:
451
- print(f"INFO: Running command {shlex_join(cmd)} in directory {cwd}")
452
-
453
- # TODO: create a thread to handle stderr and log as error?
454
- # TODO: log forwarding
455
-
456
- stderr = None if stdout is None else subprocess.STDOUT
457
- process = subprocess.run(
458
- cmd, cwd=cwd, env=self.env, stdout=stdout, stderr=stderr
459
- )
460
-
461
- if process.returncode != 0:
462
- raise SystemExit(
463
- f"Process {process.args[0]!r} terminated with error {process.returncode}"
464
- )
465
-
466
- def rm_build_folder(self, build_dir: Path):
467
- if os.path.isdir(build_dir):
468
- print("Removing:", build_dir)
469
- shutil.rmtree(build_dir, ignore_errors=True)
470
-
471
-
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
- def outdated(output: Path, dependencies: Sequence[Path]) -> bool:
518
- """Return ``True`` if any source files in *dependencies* are newer than the *output* directory.
519
-
520
- Returns:
521
- ``True`` if any source files are newer, ``False`` otherwise.
522
- """
523
-
524
- if not output.is_file():
525
- return True
526
-
527
- output_mtime = output.stat().st_mtime
528
-
529
- dep_mtime = 0.0
530
- for dependency in dependencies:
531
- mtime = dependency.stat().st_mtime
532
- if mtime > dep_mtime:
533
- dep_mtime = mtime
534
-
535
- return dep_mtime > output_mtime
536
-
537
-
538
- def get_abs_path(path: PathLike) -> Path:
539
- """Return *path* in absolute form."""
540
-
541
- path = Path(path)
542
- if path.is_absolute():
543
- return path.resolve()
544
- else:
545
- return Path(Path.cwd() / path).resolve()
546
-
547
-
548
- def get_abs_paths(paths: Sequence[PathLike]) -> List[Path]:
549
- """Return list of *paths* in absolute form."""
550
-
551
- return [get_abs_path(path) for path in paths]
552
-
553
-
554
- _verilog_extensions = (".v", ".sv", ".vh", ".svh")
555
- _vhdl_extensions = (".vhd", ".vhdl")
556
-
557
- _vhdl_extensions_s = ", ".join(f"`{c}`" for c in _vhdl_extensions)
558
- _verilog_extensions_s = ", ".join(f"`{c}`" for c in _verilog_extensions)
559
-
560
-
561
- class UnknownFileExtension(ValueError):
562
- def __init__(self, source: PathLike) -> None:
563
- super().__init__(
564
- f"Can't determine if {source} is a VHDL or Verilog file. "
565
- f"Use a standard file extension ({_vhdl_extensions_s} for VHDL files and {_verilog_extensions_s} for Verilog files) "
566
- "or tag the source with `VHDL(source)` or `Verilog(source)`."
567
- )
568
-
569
-
570
- def is_vhdl_source(source: PathLike) -> bool:
571
- if isinstance(source, VHDL):
572
- return True
573
- source_as_path = Path(source)
574
- if source_as_path.suffix in _vhdl_extensions:
575
- return True
576
- return False
577
-
578
-
579
- def is_verilog_source(source: PathLike) -> bool:
580
- if isinstance(source, Verilog):
581
- return True
582
- source_as_path = Path(source)
583
- if source_as_path.suffix in _verilog_extensions:
584
- return True
585
- return False
586
-
587
-
588
- class Icarus(Simulator):
589
- supported_gpi_interfaces = {"verilog": ["vpi"]}
590
-
591
- @staticmethod
592
- def _simulator_in_path() -> None:
593
- if shutil.which("iverilog") is None:
594
- raise SystemExit("ERROR: iverilog executable not found!")
595
-
596
- @staticmethod
597
- def _get_include_options(includes: Sequence[PathLike]) -> Command:
598
- return [f"-I{include}" for include in includes]
599
-
600
- @staticmethod
601
- def _get_define_options(defines: Mapping[str, object]) -> Command:
602
- return [f"-D{name}={value}" for name, value in defines.items()]
603
-
604
- def _get_parameter_options(self, parameters: Mapping[str, object]) -> Command:
605
- assert self.hdl_toplevel is not None
606
- return [
607
- f"-P{self.hdl_toplevel}.{name}={value}"
608
- for name, value in parameters.items()
609
- ]
610
-
611
- def _create_cmd_file(self) -> None:
612
- with open(self.cmds_file, "w") as f:
613
- f.write("+timescale+{}/{}\n".format(*self.timescale))
614
-
615
- def _create_iverilog_dump_file(self) -> None:
616
- dumpfile_path = Path(self.build_dir, f"{self.hdl_toplevel}.fst").as_posix()
617
- with open(self.iverilog_dump_file, "w") as f:
618
- f.write("module cocotb_iverilog_dump();\n")
619
- f.write("initial begin\n")
620
- f.write(f' $dumpfile("{dumpfile_path}");\n')
621
- f.write(f" $dumpvars(0, {self.hdl_toplevel});\n")
622
- f.write("end\n")
623
- f.write("endmodule\n")
624
-
625
- @property
626
- def sim_file(self) -> Path:
627
- return self.build_dir / "sim.vvp"
628
-
629
- @property
630
- def iverilog_dump_file(self) -> Path:
631
- return self.build_dir / "cocotb_iverilog_dump.v"
632
-
633
- @property
634
- def cmds_file(self) -> Path:
635
- return self.build_dir / "cmds.f"
636
-
637
- def _test_command(self) -> List[Command]:
638
- plusargs = self.plusargs
639
- if self.waves:
640
- plusargs += ["-fst"]
641
-
642
- if self.pre_cmd:
643
- print("WARNING: pre_cmd is not implemented for Icarus Verilog.")
644
-
645
- return [
646
- [
647
- "vvp",
648
- "-M",
649
- cocotb.config.libs_dir,
650
- "-m",
651
- cocotb.config.lib_name("vpi", "icarus"),
652
- ]
653
- + self.test_args
654
- + [str(self.sim_file)]
655
- + plusargs
656
- ]
657
-
658
- def _build_command(self) -> List[Command]:
659
- for source in self.sources:
660
- if not is_verilog_source(source):
661
- raise ValueError(
662
- f"{type(self).__qualname__} only supports Verilog. {str(source)!r} cannot be compiled."
663
- )
664
- for arg in self.build_args:
665
- if type(arg) not in (str, Verilog):
666
- print(
667
- f"WARNING: {type(self).__qualname__} only supports Verilog. build_args {arg!r} will not be applied."
668
- )
669
-
670
- build_args = list(self.build_args)
671
- if self.waves:
672
- self._create_iverilog_dump_file()
673
- build_args += ["-s", "cocotb_iverilog_dump"]
674
-
675
- if self.timescale is not None:
676
- self._create_cmd_file()
677
- build_args += ["-f", str(self.cmds_file)]
678
-
679
- cmds = []
680
- sources = [
681
- source for source in self.sources if is_verilog_source(source)
682
- ] + self.verilog_sources
683
- if outdated(self.sim_file, sources) or self.always:
684
- cmds = [
685
- [
686
- "iverilog",
687
- "-o",
688
- str(self.sim_file),
689
- "-D",
690
- "COCOTB_SIM=1",
691
- "-s",
692
- self.hdl_toplevel,
693
- "-g2012",
694
- ]
695
- + self._get_define_options(self.defines)
696
- + self._get_include_options(self.includes)
697
- + self._get_parameter_options(self.parameters)
698
- + [arg for arg in build_args if type(arg) in (str, Verilog)]
699
- + [str(source_file) for source_file in sources]
700
- + [
701
- str(source_file)
702
- for source_file in [self.iverilog_dump_file]
703
- if self.waves
704
- ]
705
- ]
706
-
707
- else:
708
- print("WARNING: Skipping compilation of", self.sim_file)
709
-
710
- return cmds
711
-
712
-
713
- class Questa(Simulator):
714
- supported_gpi_interfaces = {"verilog": ["vpi"], "vhdl": ["fli", "vhpi"]}
715
-
716
- @staticmethod
717
- def _simulator_in_path() -> None:
718
- if shutil.which("vsim") is None:
719
- raise SystemExit("ERROR: vsim executable not found!")
720
-
721
- @staticmethod
722
- def _get_include_options(includes: Sequence[PathLike]) -> Command:
723
- return [f"+incdir+{as_tcl_value(str(include))}" for include in includes]
724
-
725
- @staticmethod
726
- def _get_define_options(defines: Mapping[str, object]) -> Command:
727
- return [
728
- f"+define+{as_tcl_value(name)}={as_tcl_value(str(value))}"
729
- for name, value in defines.items()
730
- ]
731
-
732
- @staticmethod
733
- def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
734
- return [f"-g{name}={value}" for name, value in parameters.items()]
735
-
736
- def _build_command(self) -> List[Command]:
737
-
738
- cmds = []
739
-
740
- cmds.append(["vlib", as_tcl_value(self.hdl_library)])
741
- for source in self.sources:
742
- if is_vhdl_source(source):
743
- cmds.append(self._build_vhdl_command(source))
744
- elif is_verilog_source(source):
745
- cmds.append(self._build_verilog_command(source))
746
- else:
747
- raise UnknownFileExtension(source)
748
- for source in self.vhdl_sources:
749
- cmds.append(self._build_vhdl_command(source))
750
- for source in self.verilog_sources:
751
- cmds.append(self._build_verilog_command(source))
752
-
753
- return cmds
754
-
755
- def _build_vhdl_command(self, source: PathLike) -> Command:
756
- return (
757
- ["vcom"]
758
- + ["-work", as_tcl_value(self.hdl_library)]
759
- + [as_tcl_value(v) for v in self.build_args if type(v) in (str, VHDL)]
760
- + [as_tcl_value(str(source))]
761
- )
762
-
763
- def _build_verilog_command(self, source: PathLike) -> Command:
764
- return (
765
- ["vlog"]
766
- + ([] if self.always else ["-incr"])
767
- + ["-work", as_tcl_value(self.hdl_library)]
768
- + ["-sv"]
769
- + self._get_define_options(self.defines)
770
- + self._get_include_options(self.includes)
771
- + [as_tcl_value(v) for v in self.build_args if type(v) in (str, Verilog)]
772
- + [as_tcl_value(str(source))]
773
- )
774
-
775
- def _test_command(self) -> List[Command]:
776
-
777
- cmds = []
778
-
779
- if self.pre_cmd:
780
- self.pre_cmd = ["-do"] + self.pre_cmd
781
-
782
- do_script = ""
783
- if self.waves:
784
- do_script += "log -recursive /*;"
785
-
786
- if not self.gui:
787
- do_script += "run -all; quit"
788
-
789
- gpi_if_entry = self.gpi_interfaces[0]
790
- if gpi_if_entry == "fli":
791
- lib_opts = [
792
- "-foreign",
793
- "cocotb_init "
794
- + as_tcl_value(cocotb.config.lib_name_path("fli", "questa")),
795
- ]
796
- elif gpi_if_entry == "vhpi":
797
- lib_opts = ["-voptargs=-access=rw+/."]
798
- lib_opts += [
799
- "-foreign",
800
- "vhpi_startup_routines_bootstrap "
801
- + as_tcl_value(cocotb.config.lib_name_path("vhpi", "questa")),
802
- ]
803
- else:
804
- lib_opts = [
805
- "-pli",
806
- as_tcl_value(cocotb.config.lib_name_path("vpi", "questa")),
807
- ]
808
-
809
- cmds.append(
810
- ["vsim"]
811
- + ["-gui" if self.gui else "-c"]
812
- + ["-onfinish", "stop" if self.gui else "exit"]
813
- + lib_opts
814
- + [as_tcl_value(v) for v in self.test_args]
815
- + [as_tcl_value(v) for v in self._get_parameter_options(self.parameters)]
816
- + [as_tcl_value(f"{self.hdl_toplevel_library}.{self.sim_hdl_toplevel}")]
817
- + [as_tcl_value(v) for v in self.plusargs]
818
- + self.pre_cmd
819
- + ["-do", do_script]
820
- )
821
-
822
- gpi_extra_list = []
823
- for gpi_if in self.gpi_interfaces[1:]:
824
- gpi_if_lib_path = cocotb.config.lib_name_path(gpi_if, "questa")
825
- if Path(gpi_if_lib_path).is_file():
826
- gpi_extra_list.append(
827
- cocotb.config.lib_name_path(gpi_if, "questa")
828
- + f":cocotb{gpi_if}_entry_point"
829
- )
830
- else:
831
- print("WARNING: {gpi_if_lib_path} library not found.")
832
- self.env["GPI_EXTRA"] = ",".join(gpi_extra_list)
833
-
834
- return cmds
835
-
836
-
837
- class Ghdl(Simulator):
838
- supported_gpi_interfaces = {"vhdl": ["vpi"]}
839
-
840
- @staticmethod
841
- def _simulator_in_path() -> None:
842
- if shutil.which("ghdl") is None:
843
- raise SystemExit("ERROR: ghdl executable not found!")
844
-
845
- def _is_mcode_backend(self) -> bool:
846
- """Is GHDL using the mcode backend?"""
847
- result = subprocess.run(
848
- ["ghdl", "--version"],
849
- check=True,
850
- text=True,
851
- stdout=subprocess.PIPE,
852
- stderr=subprocess.STDOUT,
853
- )
854
- return "mcode" in result.stdout
855
-
856
- @staticmethod
857
- def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
858
- return [f"-g{name}={value}" for name, value in parameters.items()]
859
-
860
- def _build_command(self) -> List[Command]:
861
- for source in self.sources:
862
- if not is_vhdl_source(source):
863
- raise ValueError(
864
- f"{type(self).__qualname__} only supports VHDL. {str(source)!r} cannot be compiled."
865
- )
866
- for arg in self.build_args:
867
- if type(arg) not in (str, VHDL):
868
- print(
869
- f"WARNING: {type(self).__qualname__} only supports VHDL. build_args {arg!r} will not be applied."
870
- )
871
-
872
- cmds = [
873
- ["ghdl", "-i"]
874
- + [f"--work={self.hdl_library}"]
875
- + [arg for arg in self.build_args if type(arg) in (str, VHDL)]
876
- + [str(source) for source in self.sources if is_vhdl_source(source)]
877
- + [str(source) for source in self.vhdl_sources]
878
- ]
879
-
880
- if self.hdl_toplevel is not None:
881
- cmds += [
882
- ["ghdl", "-m"]
883
- + [f"--work={self.hdl_library}"]
884
- + self.build_args
885
- + [self.hdl_toplevel]
886
- ]
887
-
888
- return cmds
889
-
890
- def _test_command(self) -> List[Command]:
891
- if self.pre_cmd:
892
- print("WARNING: pre_cmd is not implemented for GHDL.")
893
-
894
- ghdl_run_args = self.test_args
895
-
896
- if self._is_mcode_backend() and self.timescale:
897
- _, precision = self.timescale
898
- # Convert the time precision to a format string supported by GHDL,
899
- # if possible.
900
- # GHDL only supports setting the time precision if the mcode backend
901
- # is used, using the --time-resolution argument causes GHDL to error
902
- # out otherwise.
903
- # https://ghdl.github.io/ghdl/using/InvokingGHDL.html#cmdoption-ghdl-time-resolution
904
- if precision == "1fs":
905
- ghdl_time_resolution = "fs"
906
- elif precision == "1ps":
907
- ghdl_time_resolution = "ps"
908
- elif precision == "1ns":
909
- ghdl_time_resolution = "ns"
910
- elif precision == "1us":
911
- ghdl_time_resolution = "us"
912
- elif precision == "1ms":
913
- ghdl_time_resolution = "ms"
914
- elif precision == "1s":
915
- ghdl_time_resolution = "sec"
916
- else:
917
- raise ValueError(
918
- "GHDL only supports the following precisions in timescale: 1fs, 1ps, 1us, 1ms, 1s"
919
- )
920
-
921
- ghdl_run_args.append(f"--time-resolution={ghdl_time_resolution}")
922
-
923
- cmds = [
924
- ["ghdl", "-r"]
925
- + [f"--work={self.hdl_toplevel_library}"]
926
- + ghdl_run_args
927
- + [self.sim_hdl_toplevel]
928
- + ["--vpi=" + cocotb.config.lib_name_path("vpi", "ghdl")]
929
- + self.plusargs
930
- + self._get_parameter_options(self.parameters)
931
- ]
932
-
933
- return cmds
934
-
935
-
936
- class Nvc(Simulator):
937
- supported_gpi_interfaces = {"vhdl": ["vhpi"]}
938
-
939
- @staticmethod
940
- def _simulator_in_path() -> None:
941
- if shutil.which("nvc") is None:
942
- raise SystemExit("ERROR: nvc executable not found!")
943
-
944
- @staticmethod
945
- def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
946
- return [f"-g{name}={value}" for name, value in parameters.items()]
947
-
948
- def _build_command(self) -> List[Command]:
949
- for source in self.sources:
950
- if not is_vhdl_source(source):
951
- raise ValueError(
952
- f"{type(self).__qualname__} only supports VHDL. {str(source)!r} cannot be compiled."
953
- )
954
- for arg in self.build_args:
955
- if type(arg) not in (str, VHDL):
956
- print(
957
- f"WARNING: {type(self).__qualname__} only supports VHDL. build_args {arg!r} will not be applied."
958
- )
959
-
960
- cmds = [
961
- ["nvc", f"--work={self.hdl_library}"]
962
- + [arg for arg in self.build_args if type(arg) in (str, VHDL)]
963
- + ["-a"]
964
- + [str(source) for source in self.sources if is_vhdl_source(source)]
965
- + [str(source) for source in self.vhdl_sources]
966
- ]
967
-
968
- return cmds
969
-
970
- def _test_command(self) -> List[Command]:
971
- cmds = [
972
- ["nvc", f"--work={self.hdl_toplevel_library}"]
973
- + self.build_args
974
- + ["-e", self.sim_hdl_toplevel, "--no-save", "--jit"]
975
- + self._get_parameter_options(self.parameters)
976
- + ["-r"]
977
- + self.test_args
978
- + ["--load=" + cocotb.config.lib_name_path("vhpi", "nvc")]
979
- + self.plusargs
980
- ]
981
-
982
- return cmds
983
-
984
-
985
- class Riviera(Simulator):
986
- supported_gpi_interfaces = {"verilog": ["vpi"], "vhdl": ["vhpi"]}
987
-
988
- @staticmethod
989
- def _simulator_in_path() -> None:
990
- if shutil.which("vsimsa") is None:
991
- raise SystemExit("ERROR: vsimsa executable not found!")
992
-
993
- @staticmethod
994
- def _get_include_options(includes: Sequence[PathLike]) -> Command:
995
- return [f"+incdir+{as_tcl_value(str(include))}" for include in includes]
996
-
997
- @staticmethod
998
- def _get_define_options(defines: Mapping[str, object]) -> Command:
999
- return [
1000
- f"+define+{as_tcl_value(name)}={as_tcl_value(str(value))}"
1001
- for name, value in defines.items()
1002
- ]
1003
-
1004
- @staticmethod
1005
- def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
1006
- return [f"-g{name}={value}" for name, value in parameters.items()]
1007
-
1008
- def _build_command(self) -> List[Command]:
1009
- do_script: List[str] = ["onerror {\n quit -code 1 \n}"]
1010
-
1011
- out_file = self.build_dir / self.hdl_library / f"{self.hdl_library}.lib"
1012
-
1013
- if outdated(out_file, self.verilog_sources + self.vhdl_sources) or self.always:
1014
- do_script.append(f"alib {as_tcl_value(self.hdl_library)}")
1015
-
1016
- for source in self.sources:
1017
- if is_verilog_source(source):
1018
- do_script.append(self._build_verilog_source(source))
1019
- elif is_vhdl_source(source):
1020
- do_script.append(self._build_vhdl_source(source))
1021
- else:
1022
- raise UnknownFileExtension(source)
1023
- for source in self.vhdl_sources:
1024
- do_script.append(self._build_vhdl_source(source))
1025
- for source in self.verilog_sources:
1026
- do_script.append(self._build_verilog_source(source))
1027
-
1028
- # Explicitly exit the script at the end. In batch mode, which is invoked
1029
- # implicitly by redirecting STDOUT/STDERR of the alog/acom commands,
1030
- # the tool exits by itself even without this 'exit' command -- but not
1031
- # when running from an interactive terminal. Be explicit for predictable
1032
- # behavior.
1033
- do_script.append("exit")
1034
-
1035
- do_file = tempfile.NamedTemporaryFile(delete=False)
1036
- do_file.write("\n".join(do_script).encode())
1037
- do_file.close()
1038
-
1039
- return [["vsimsa"] + ["-do"] + ["do"] + [do_file.name]]
1040
-
1041
- def _build_vhdl_source(self, source: PathLike) -> str:
1042
- return "acom -work {RTL_LIBRARY} {EXTRA_ARGS} {VHDL_SOURCES}".format(
1043
- RTL_LIBRARY=as_tcl_value(self.hdl_library),
1044
- VHDL_SOURCES=as_tcl_value(str(source)),
1045
- EXTRA_ARGS=" ".join(
1046
- as_tcl_value(v) for v in self.build_args if type(v) in (str, VHDL)
1047
- ),
1048
- )
1049
-
1050
- def _build_verilog_source(self, source: PathLike) -> str:
1051
- return "alog -work {RTL_LIBRARY} -pli {EXT_NAME} -sv {DEFINES} {INCDIR} {EXTRA_ARGS} {VERILOG_SOURCES}".format(
1052
- RTL_LIBRARY=as_tcl_value(self.hdl_library),
1053
- EXT_NAME=as_tcl_value(cocotb.config.lib_name_path("vpi", "riviera")),
1054
- VERILOG_SOURCES=as_tcl_value(str(source)),
1055
- DEFINES=" ".join(self._get_define_options(self.defines)),
1056
- INCDIR=" ".join(self._get_include_options(self.includes)),
1057
- EXTRA_ARGS=" ".join(
1058
- as_tcl_value(v) for v in self.build_args if type(v) in (str, Verilog)
1059
- ),
1060
- )
1061
-
1062
- def _test_command(self) -> List[Command]:
1063
- if self.pre_cmd:
1064
- print("WARNING: pre_cmd is not implemented for Riviera.")
1065
-
1066
- do_script: str = "\nonerror {\n quit -code 1 \n} \n"
1067
-
1068
- if self.hdl_toplevel_lang == "vhdl":
1069
- do_script += "asim +access +w_nets -interceptcoutput -loadvhpi {EXT_NAME} {EXTRA_ARGS} {TOPLEVEL} {PLUSARGS}\n".format(
1070
- TOPLEVEL=as_tcl_value(
1071
- f"{self.hdl_toplevel_library}.{self.sim_hdl_toplevel}"
1072
- ),
1073
- EXT_NAME=as_tcl_value(cocotb.config.lib_name_path("vhpi", "riviera")),
1074
- EXTRA_ARGS=" ".join(
1075
- as_tcl_value(v)
1076
- for v in (
1077
- self.test_args + self._get_parameter_options(self.parameters)
1078
- )
1079
- ),
1080
- PLUSARGS=" ".join(as_tcl_value(v) for v in self.plusargs),
1081
- )
1082
-
1083
- self.env["GPI_EXTRA"] = (
1084
- cocotb.config.lib_name_path("vpi", "riviera") + ":cocotbvpi_entry_point"
1085
- )
1086
- else:
1087
- do_script += "asim +access +w_nets -interceptcoutput -pli {EXT_NAME} {EXTRA_ARGS} {TOPLEVEL} {PLUSARGS} \n".format(
1088
- TOPLEVEL=as_tcl_value(
1089
- f"{self.hdl_toplevel_library}.{self.sim_hdl_toplevel}"
1090
- ),
1091
- EXT_NAME=as_tcl_value(cocotb.config.lib_name_path("vpi", "riviera")),
1092
- EXTRA_ARGS=" ".join(
1093
- as_tcl_value(v)
1094
- for v in (
1095
- self.test_args + self._get_parameter_options(self.parameters)
1096
- )
1097
- ),
1098
- PLUSARGS=" ".join(as_tcl_value(v) for v in self.plusargs),
1099
- )
1100
-
1101
- self.env["GPI_EXTRA"] = (
1102
- cocotb.config.lib_name_path("vhpi", "riviera")
1103
- + ":cocotbvhpi_entry_point"
1104
- )
1105
-
1106
- if self.waves:
1107
- do_script += "log -recursive /*;"
1108
-
1109
- do_script += "run -all \nexit"
1110
-
1111
- do_file = tempfile.NamedTemporaryFile(delete=False)
1112
- do_file.write(do_script.encode())
1113
- do_file.close()
1114
-
1115
- return [["vsimsa"] + ["-do"] + ["do"] + [do_file.name]]
1116
-
1117
-
1118
- class Verilator(Simulator):
1119
- supported_gpi_interfaces = {"verilog": ["vpi"]}
1120
-
1121
- def _simulator_in_path(self) -> None:
1122
- # the verilator binary is only needed for building
1123
- return
1124
-
1125
- def _simulator_in_path_build_only(self) -> None:
1126
- executable = shutil.which("verilator")
1127
- if executable is None:
1128
- raise SystemExit("ERROR: verilator executable not found!")
1129
- self.executable: str = executable
1130
-
1131
- @staticmethod
1132
- def _get_include_options(includes: Sequence[PathLike]) -> Command:
1133
- return [f"-I{include}" for include in includes]
1134
-
1135
- @staticmethod
1136
- def _get_define_options(defines: Mapping[str, object]) -> Command:
1137
- return [f"-D{name}={value}" for name, value in defines.items()]
1138
-
1139
- @staticmethod
1140
- def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
1141
- return [f"-G{name}={value}" for name, value in parameters.items()]
1142
-
1143
- def _build_command(self) -> List[Command]:
1144
- self._simulator_in_path_build_only()
1145
-
1146
- for source in self.sources:
1147
- if not is_verilog_source(source):
1148
- raise ValueError(
1149
- f"{type(self).__qualname__} only supports Verilog. {str(source)!r} cannot be compiled."
1150
- )
1151
- for arg in self.build_args:
1152
- if type(arg) not in (str, Verilog):
1153
- print(
1154
- f"WARNING: {type(self).__qualname__} only supports Verilog. build_args {arg!r} will not be applied."
1155
- )
1156
-
1157
- if self.hdl_toplevel is None:
1158
- raise ValueError(
1159
- f"{type(self).__qualname__} requires the hdl_toplevel parameter to be specified"
1160
- )
1161
-
1162
- # TODO: set "--debug" if self.verbose
1163
- # TODO: support "--always"
1164
-
1165
- verilator_cpp = str(
1166
- Path(cocotb.__file__).parent
1167
- / "share"
1168
- / "lib"
1169
- / "verilator"
1170
- / "verilator.cpp"
1171
- )
1172
-
1173
- cmds = []
1174
- cmds.append(
1175
- [
1176
- "perl",
1177
- self.executable,
1178
- "-cc",
1179
- "--exe",
1180
- "-Mdir",
1181
- str(self.build_dir),
1182
- "-DCOCOTB_SIM=1",
1183
- "--top-module",
1184
- self.hdl_toplevel,
1185
- "--vpi",
1186
- "--public-flat-rw",
1187
- "--prefix",
1188
- "Vtop",
1189
- "-o",
1190
- self.hdl_toplevel,
1191
- "-LDFLAGS",
1192
- "-Wl,-rpath,{LIB_DIR} -L{LIB_DIR} -lcocotbvpi_verilator".format(
1193
- LIB_DIR=cocotb.config.libs_dir
1194
- ),
1195
- ]
1196
- + (["--trace"] if self.waves else [])
1197
- + [arg for arg in self.build_args if type(arg) in (str, Verilog)]
1198
- + self._get_define_options(self.defines)
1199
- + self._get_include_options(self.includes)
1200
- + self._get_parameter_options(self.parameters)
1201
- + [verilator_cpp]
1202
- + [str(source) for source in self.sources if is_verilog_source(source)]
1203
- + [str(source) for source in self.verilog_sources]
1204
- )
1205
-
1206
- cmds.append(
1207
- [
1208
- "make",
1209
- "-C",
1210
- str(self.build_dir),
1211
- "-f",
1212
- "Vtop.mk",
1213
- f"VM_TRACE={int(self.waves)}",
1214
- ]
1215
- )
1216
-
1217
- return cmds
1218
-
1219
- def _test_command(self) -> List[Command]:
1220
- if self.pre_cmd:
1221
- print("WARNING: pre_cmd is not implemented for Verilator.")
1222
-
1223
- out_file = self.build_dir / self.sim_hdl_toplevel
1224
- return [
1225
- [str(out_file)]
1226
- + (["--trace"] if self.waves else [])
1227
- + self.test_args
1228
- + self.plusargs
1229
- ]
1230
-
1231
-
1232
- class Xcelium(Simulator):
1233
- supported_gpi_interfaces = {"verilog": ["vpi"], "vhdl": ["vhpi"]}
1234
-
1235
- @staticmethod
1236
- def _simulator_in_path() -> None:
1237
- if shutil.which("xrun") is None:
1238
- raise SystemExit("ERROR: xrun executable not found!")
1239
-
1240
- @staticmethod
1241
- def _get_include_options(includes: Sequence[PathLike]) -> Command:
1242
- return [f"-incdir {include}" for include in includes]
1243
-
1244
- @staticmethod
1245
- def _get_define_options(defines: Mapping[str, object]) -> Command:
1246
- return [f"-define {name}={value}" for name, value in defines.items()]
1247
-
1248
- @staticmethod
1249
- def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
1250
- return [f'-gpg "{name} => {value}"' for name, value in parameters.items()]
1251
-
1252
- def _build_command(self) -> List[Command]:
1253
- self.env["CDS_AUTO_64BIT"] = "all"
1254
-
1255
- assert self.hdl_toplevel, "A HDL toplevel is required in all Xcelium compiles."
1256
-
1257
- verbosity_opts = []
1258
- if self.verbose:
1259
- verbosity_opts += ["-messages"]
1260
- verbosity_opts += ["-status"]
1261
- verbosity_opts += ["-gverbose"] # print assigned generics/parameters
1262
- verbosity_opts += ["-pliverbose"]
1263
- verbosity_opts += ["-plidebug"] # Enhance the profile output with PLI info
1264
- verbosity_opts += [
1265
- "-plierr_verbose"
1266
- ] # Expand handle info in PLI/VPI/VHPI messages
1267
-
1268
- else:
1269
- verbosity_opts += ["-quiet"]
1270
- verbosity_opts += ["-plinowarn"]
1271
-
1272
- vhpi_opts = []
1273
- # Xcelium 23.09.004 fixes cocotb issue #1076 as long as the
1274
- # following define is set.
1275
- vhpi_opts.append("-NEW_VHPI_PROPAGATE_DELAY")
1276
-
1277
- cmds = [
1278
- ["xrun"]
1279
- + ["-logfile"]
1280
- + ["xrun_build.log"]
1281
- + ["-elaborate"]
1282
- + ["-xmlibdirname"]
1283
- + [f"{self.build_dir}/xrun_snapshot"]
1284
- + ["-licqueue"]
1285
- + (["-clean"] if self.always else [])
1286
- + verbosity_opts
1287
- # + ["-vpicompat 1800v2005"] # <1364v1995|1364v2001|1364v2005|1800v2005> Specify the IEEE VPI
1288
- + ["-access +rwc"]
1289
- + ["-loadvpi"]
1290
- # always start with VPI on Xcelium
1291
- + [
1292
- cocotb.config.lib_name_path("vpi", "xcelium")
1293
- + ":vlog_startup_routines_bootstrap"
1294
- ]
1295
- + vhpi_opts
1296
- + [f"-work {self.hdl_library}"]
1297
- + self.build_args
1298
- + ["-define COCOTB_SIM"]
1299
- + self._get_include_options(self.includes)
1300
- + self._get_define_options(self.defines)
1301
- + self._get_parameter_options(self.parameters)
1302
- + [f"-top {self.hdl_toplevel}"]
1303
- + [
1304
- str(source_file)
1305
- for source_file in (
1306
- self.sources + self.vhdl_sources + self.verilog_sources
1307
- )
1308
- ]
1309
- ]
1310
-
1311
- return cmds
1312
-
1313
- def _test_command(self) -> List[Command]:
1314
- if self.pre_cmd:
1315
- print("WARNING: pre_cmd is not implemented for Xcelium.")
1316
-
1317
- self.env["CDS_AUTO_64BIT"] = "all"
1318
-
1319
- verbosity_opts = []
1320
- if self.verbose:
1321
- verbosity_opts += ["-messages"]
1322
- verbosity_opts += ["-status"]
1323
- verbosity_opts += ["-gverbose"] # print assigned generics/parameters
1324
- verbosity_opts += ["-pliverbose"]
1325
- verbosity_opts += ["-plidebug"] # Enhance the profile output with PLI info
1326
- verbosity_opts += [
1327
- "-plierr_verbose"
1328
- ] # Expand handle info in PLI/VPI/VHPI messages
1329
-
1330
- else:
1331
- verbosity_opts += ["-quiet"]
1332
- verbosity_opts += ["-plinowarn"]
1333
-
1334
- tmpdir = f"implicit_tmpdir_{self.current_test_name}"
1335
-
1336
- if self.hdl_toplevel_lang == "vhdl":
1337
- xrun_top = ":"
1338
- else:
1339
- xrun_top = self.sim_hdl_toplevel
1340
-
1341
- if self.waves:
1342
- input_tcl = [
1343
- f'-input "@database -open cocotb_waves -default" '
1344
- f'-input "@probe -database cocotb_waves -create {xrun_top} -all -depth all" '
1345
- f'-input "@run" '
1346
- f'-input "@exit" '
1347
- ]
1348
- else:
1349
- input_tcl = ["-input", "@run; exit;"]
1350
-
1351
- vhpi_opts = []
1352
- # Xcelium 23.09.004 fixes cocotb issue #1076 as long as the
1353
- # following define is set.
1354
- vhpi_opts.append("-NEW_VHPI_PROPAGATE_DELAY")
1355
-
1356
- cmds = [["mkdir", "-p", tmpdir]]
1357
- cmds += [
1358
- ["xrun"]
1359
- + ["-logfile"]
1360
- + [f"xrun_{self.current_test_name}.log"]
1361
- + ["-xmlibdirname"]
1362
- + [f"{self.build_dir}/xrun_snapshot"]
1363
- + ["-cds_implicit_tmpdir"]
1364
- + [tmpdir]
1365
- + ["-licqueue"]
1366
- + vhpi_opts
1367
- + verbosity_opts
1368
- + ["-R"]
1369
- + self.test_args
1370
- + self.plusargs
1371
- + ["-gui" if self.gui else ""]
1372
- + input_tcl
1373
- ]
1374
- self.env["GPI_EXTRA"] = (
1375
- cocotb.config.lib_name_path("vhpi", "xcelium") + ":cocotbvhpi_entry_point"
1376
- )
1377
-
1378
- return cmds
1379
-
1380
-
1381
- def get_runner(simulator_name: str) -> Simulator:
1382
- """Return the *simulator_name* instance."""
1383
-
1384
- supported_sims: Dict[str, Type[Simulator]] = {
1385
- "icarus": Icarus,
1386
- "questa": Questa,
1387
- "ghdl": Ghdl,
1388
- "riviera": Riviera,
1389
- "verilator": Verilator,
1390
- "xcelium": Xcelium,
1391
- "nvc": Nvc,
1392
- # TODO: "vcs": Vcs,
1393
- # TODO: "activehdl": ActiveHdl,
1394
- }
1395
- try:
1396
- return supported_sims[simulator_name]()
1397
- except KeyError:
1398
- raise ValueError(
1399
- f"Simulator {simulator_name!r} is not in supported list: {', '.join(supported_sims)}"
1400
- ) from None