cocotb 2.0.1__cp38-cp38-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.
Files changed (152) hide show
  1. cocotb/_ANSI.py +65 -0
  2. cocotb/__init__.py +127 -0
  3. cocotb/_base_triggers.py +515 -0
  4. cocotb/_bridge.py +186 -0
  5. cocotb/_decorators.py +515 -0
  6. cocotb/_deprecation.py +36 -0
  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 +150 -0
  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 +4 -0
  21. cocotb/_xunit_reporter.py +103 -0
  22. cocotb/clock.py +419 -0
  23. cocotb/debug.py +24 -0
  24. cocotb/handle.py +1752 -0
  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/cocotbvhpi_nvc.dll +0 -0
  41. cocotb/libs/cocotbvhpi_nvc.exp +0 -0
  42. cocotb/libs/cocotbvhpi_nvc.lib +0 -0
  43. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  44. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  45. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  46. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  47. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  48. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  49. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  50. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  51. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  52. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  53. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  54. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  55. cocotb/libs/embed.dll +0 -0
  56. cocotb/libs/embed.exp +0 -0
  57. cocotb/libs/embed.lib +0 -0
  58. cocotb/libs/gpi.dll +0 -0
  59. cocotb/libs/gpi.exp +0 -0
  60. cocotb/libs/gpi.lib +0 -0
  61. cocotb/libs/gpilog.dll +0 -0
  62. cocotb/libs/gpilog.exp +0 -0
  63. cocotb/libs/gpilog.lib +0 -0
  64. cocotb/libs/pygpilog.dll +0 -0
  65. cocotb/libs/pygpilog.exp +0 -0
  66. cocotb/libs/pygpilog.lib +0 -0
  67. cocotb/logging.py +417 -0
  68. cocotb/py.typed +0 -0
  69. cocotb/queue.py +235 -0
  70. cocotb/regression.py +900 -0
  71. cocotb/result.py +38 -0
  72. cocotb/share/def/.gitignore +2 -0
  73. cocotb/share/def/README.md +4 -0
  74. cocotb/share/def/aldec.def +61 -0
  75. cocotb/share/def/aldec.exp +0 -0
  76. cocotb/share/def/aldec.lib +0 -0
  77. cocotb/share/def/ghdl.def +43 -0
  78. cocotb/share/def/ghdl.exp +0 -0
  79. cocotb/share/def/ghdl.lib +0 -0
  80. cocotb/share/def/icarus.def +43 -0
  81. cocotb/share/def/icarus.exp +0 -0
  82. cocotb/share/def/icarus.lib +0 -0
  83. cocotb/share/def/modelsim.def +138 -0
  84. cocotb/share/def/modelsim.exp +0 -0
  85. cocotb/share/def/modelsim.lib +0 -0
  86. cocotb/share/def/nvcvhpi.def +18 -0
  87. cocotb/share/def/nvcvhpi.exp +0 -0
  88. cocotb/share/def/nvcvhpi.lib +0 -0
  89. cocotb/share/include/cocotb_utils.h +70 -0
  90. cocotb/share/include/embed.h +33 -0
  91. cocotb/share/include/exports.h +20 -0
  92. cocotb/share/include/gpi.h +459 -0
  93. cocotb/share/include/gpi_logging.h +291 -0
  94. cocotb/share/include/py_gpi_logging.h +33 -0
  95. cocotb/share/include/vhpi_user_ext.h +26 -0
  96. cocotb/share/include/vpi_user_ext.h +33 -0
  97. cocotb/share/lib/verilator/verilator.cpp +209 -0
  98. cocotb/simtime.py +238 -0
  99. cocotb/simulator.cp38-win32.exp +0 -0
  100. cocotb/simulator.cp38-win32.lib +0 -0
  101. cocotb/simulator.cp38-win32.pyd +0 -0
  102. cocotb/simulator.cp38-win32.pyd.2.config +8 -0
  103. cocotb/simulator.pyi +107 -0
  104. cocotb/task.py +590 -0
  105. cocotb/triggers.py +67 -0
  106. cocotb/types/__init__.py +31 -0
  107. cocotb/types/_abstract_array.py +151 -0
  108. cocotb/types/_array.py +297 -0
  109. cocotb/types/_indexing.py +17 -0
  110. cocotb/types/_logic.py +333 -0
  111. cocotb/types/_logic_array.py +884 -0
  112. cocotb/types/_range.py +197 -0
  113. cocotb/types/_resolve.py +76 -0
  114. cocotb/utils.py +110 -0
  115. cocotb-2.0.1.dist-info/LICENSE +29 -0
  116. cocotb-2.0.1.dist-info/METADATA +44 -0
  117. cocotb-2.0.1.dist-info/RECORD +152 -0
  118. cocotb-2.0.1.dist-info/WHEEL +5 -0
  119. cocotb-2.0.1.dist-info/entry_points.txt +2 -0
  120. cocotb-2.0.1.dist-info/top_level.txt +18 -0
  121. cocotb_tools/__init__.py +0 -0
  122. cocotb_tools/_coverage.py +33 -0
  123. cocotb_tools/_vendor/__init__.py +3 -0
  124. cocotb_tools/_vendor/distutils_version.py +346 -0
  125. cocotb_tools/check_results.py +65 -0
  126. cocotb_tools/combine_results.py +152 -0
  127. cocotb_tools/config.py +242 -0
  128. cocotb_tools/ipython_support.py +99 -0
  129. cocotb_tools/makefiles/Makefile.deprecations +27 -0
  130. cocotb_tools/makefiles/Makefile.inc +198 -0
  131. cocotb_tools/makefiles/Makefile.sim +96 -0
  132. cocotb_tools/makefiles/simulators/Makefile.activehdl +72 -0
  133. cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
  134. cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
  135. cocotb_tools/makefiles/simulators/Makefile.ghdl +84 -0
  136. cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
  137. cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
  138. cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
  139. cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
  140. cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
  141. cocotb_tools/makefiles/simulators/Makefile.questa-compat +143 -0
  142. cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
  143. cocotb_tools/makefiles/simulators/Makefile.riviera +144 -0
  144. cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
  145. cocotb_tools/makefiles/simulators/Makefile.verilator +79 -0
  146. cocotb_tools/makefiles/simulators/Makefile.xcelium +104 -0
  147. cocotb_tools/py.typed +0 -0
  148. cocotb_tools/runner.py +2001 -0
  149. cocotb_tools/sim_versions.py +140 -0
  150. pygpi/__init__.py +0 -0
  151. pygpi/entry.py +42 -0
  152. pygpi/py.typed +0 -0
cocotb/_bridge.py ADDED
@@ -0,0 +1,186 @@
1
+ # Copyright cocotb contributors
2
+ # Licensed under the Revised BSD License, see LICENSE for details.
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+ import functools
5
+ import logging
6
+ import threading
7
+ from enum import IntEnum
8
+ from typing import (
9
+ TYPE_CHECKING,
10
+ Callable,
11
+ Coroutine,
12
+ Generic,
13
+ TypeVar,
14
+ Union,
15
+ )
16
+
17
+ import cocotb
18
+ from cocotb import debug
19
+ from cocotb._base_triggers import Event, Trigger
20
+ from cocotb._exceptions import InternalError
21
+ from cocotb._py_compat import ParamSpec
22
+
23
+ if TYPE_CHECKING:
24
+ from cocotb._outcomes import Outcome
25
+
26
+ P = ParamSpec("P")
27
+
28
+ Result = TypeVar("Result")
29
+
30
+
31
+ def resume(
32
+ func: "Callable[P, Coroutine[Trigger, None, Result]]",
33
+ ) -> "Callable[P, Result]":
34
+ """Converts a coroutine function into a blocking function.
35
+
36
+ This allows a :term:`coroutine function` that awaits cocotb triggers to be
37
+ called from a :term:`blocking function` converted by :func:`.bridge`.
38
+ This completes the bridge through non-:keyword:`async` code.
39
+
40
+ When a converted coroutine function is called the current function blocks until the
41
+ converted function exits.
42
+
43
+ Results of the converted function are returned from the function call.
44
+
45
+ Args:
46
+ func: The :term:`coroutine function` to convert into a :term:`blocking function`.
47
+
48
+ Returns:
49
+ *func* as a :term:`blocking function`.
50
+
51
+ Raises:
52
+ RuntimeError:
53
+ If the function that is returned is subsequently called from a
54
+ thread that was not started with :class:`.bridge`.
55
+
56
+ .. versionchanged:: 2.0
57
+ Renamed from ``function``.
58
+ No longer implemented as a type.
59
+ The ``log`` attribute is no longer available.
60
+ """
61
+
62
+ @functools.wraps(func)
63
+ def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> Result:
64
+ return cocotb._scheduler_inst._queue_function(func(*args, **kwargs))
65
+
66
+ return wrapper
67
+
68
+
69
+ def bridge(
70
+ func: "Callable[P, Result]",
71
+ ) -> "Callable[P, Coroutine[Trigger, None, Result]]":
72
+ r"""Converts a blocking function into a coroutine function.
73
+
74
+ This function converts a :term:`blocking function` into a :term:`coroutine function`
75
+ with the expectation that the function being converted is intended to call a
76
+ :func:`.resume` converted function. This creates a bridge through
77
+ non-:keyword:`async` code for code wanting to eventually :keyword:`await` on cocotb
78
+ triggers.
79
+
80
+ When a converted function call is used in an :keyword:`await` statement, the current
81
+ Task blocks until the converted function finishes.
82
+
83
+ Results of the converted function are returned from the :keyword:`await` expression.
84
+
85
+ .. note::
86
+ Bridge threads *must* either finish or block on a :func:`.resume`
87
+ converted function before control is given back to the simulator.
88
+ This is done to prevent any code from executing in parallel with the simulation.
89
+
90
+ Args:
91
+ func: The :term:`blocking function` to convert into a :term:`coroutine function`.
92
+
93
+ Returns:
94
+ *func* as a :term:`coroutine function`.
95
+
96
+ .. versionchanged:: 2.0
97
+ Renamed from ``external``.
98
+ No longer implemented as a type.
99
+ The ``log`` attribute is no longer available.
100
+ """
101
+
102
+ @functools.wraps(func)
103
+ def wrapper(
104
+ *args: "P.args", **kwargs: "P.kwargs"
105
+ ) -> Coroutine[Trigger, None, Result]:
106
+ return cocotb._scheduler_inst._run_in_executor(func, *args, **kwargs)
107
+
108
+ return wrapper
109
+
110
+
111
+ class external_state(IntEnum):
112
+ INIT = 0
113
+ RUNNING = 1
114
+ PAUSED = 2
115
+ EXITED = 3
116
+
117
+
118
+ class external_waiter(Generic[Result]):
119
+ def __init__(self) -> None:
120
+ self._outcome: Union[Outcome[Result], None] = None
121
+ self.thread: threading.Thread
122
+ self.event = Event()
123
+ self.state = external_state.INIT
124
+ self.cond = threading.Condition()
125
+ self._log = logging.getLogger(f"cocotb.bridge.0x{id(self):x}")
126
+
127
+ @property
128
+ def result(self) -> Result:
129
+ if self._outcome is None:
130
+ raise InternalError("Got result of external before it finished")
131
+ return self._outcome.get()
132
+
133
+ def _propagate_state(self, new_state: external_state) -> None:
134
+ with self.cond:
135
+ if debug.debug:
136
+ self._log.debug(
137
+ f"Changing state from {self.state} -> {new_state} from {threading.current_thread()}"
138
+ )
139
+ self.state = new_state
140
+ self.cond.notify()
141
+
142
+ def thread_done(self) -> None:
143
+ if debug.debug:
144
+ self._log.debug(f"Thread finished from {threading.current_thread()}")
145
+ self._propagate_state(external_state.EXITED)
146
+
147
+ def thread_suspend(self) -> None:
148
+ self._propagate_state(external_state.PAUSED)
149
+
150
+ def thread_start(self) -> None:
151
+ if self.state > external_state.INIT:
152
+ return
153
+
154
+ if not self.thread.is_alive():
155
+ self._propagate_state(external_state.RUNNING)
156
+ self.thread.start()
157
+
158
+ def thread_resume(self) -> None:
159
+ self._propagate_state(external_state.RUNNING)
160
+
161
+ def thread_wait(self) -> external_state:
162
+ if debug.debug:
163
+ self._log.debug(
164
+ f"Waiting for the condition lock {threading.current_thread()}"
165
+ )
166
+
167
+ with self.cond:
168
+ while self.state == external_state.RUNNING:
169
+ self.cond.wait()
170
+
171
+ if debug.debug:
172
+ if self.state == external_state.EXITED:
173
+ self._log.debug(
174
+ f"Thread {self.thread} has exited from {threading.current_thread()}"
175
+ )
176
+ elif self.state == external_state.PAUSED:
177
+ self._log.debug(
178
+ f"Thread {self.thread} has called yield from {threading.current_thread()}"
179
+ )
180
+
181
+ if self.state == external_state.INIT:
182
+ raise Exception(
183
+ f"Thread {self.thread} state was not allowed from {threading.current_thread()}"
184
+ )
185
+
186
+ return self.state
cocotb/_decorators.py ADDED
@@ -0,0 +1,515 @@
1
+ # Copyright cocotb contributors
2
+ # Copyright (c) 2013 Potential Ventures Ltd
3
+ # Copyright (c) 2013 SolarFlare Communications Inc
4
+ # Licensed under the Revised BSD License, see LICENSE for details.
5
+ # SPDX-License-Identifier: BSD-3-Clause
6
+
7
+ import functools
8
+ import inspect
9
+ from enum import Enum
10
+ from itertools import product
11
+ from typing import (
12
+ Callable,
13
+ Coroutine,
14
+ Dict,
15
+ Iterable,
16
+ List,
17
+ Optional,
18
+ Sequence,
19
+ Tuple,
20
+ Type,
21
+ Union,
22
+ cast,
23
+ overload,
24
+ )
25
+
26
+ from cocotb._base_triggers import Trigger
27
+ from cocotb._py_compat import Protocol, TypeAlias
28
+ from cocotb._typing import TimeUnit
29
+
30
+
31
+ class Test:
32
+ """A cocotb test in a regression.
33
+
34
+ Args:
35
+ func:
36
+ The test function object.
37
+
38
+ name:
39
+ The name of the test function.
40
+ Defaults to ``func.__qualname__`` (the dotted path to the test function in the module).
41
+
42
+ module:
43
+ The name of the module containing the test function.
44
+ Defaults to ``func.__module__`` (the name of the module containing the test function).
45
+
46
+ doc:
47
+ The docstring for the test.
48
+ Defaults to ``func.__doc__`` (the docstring of the test function).
49
+
50
+ timeout_time:
51
+ Simulation time duration before the test is forced to fail with a :exc:`~cocotb.triggers.SimTimeoutError`.
52
+
53
+ timeout_unit:
54
+ Unit of ``timeout_time``, accepts any unit that :class:`~cocotb.triggers.Timer` does.
55
+
56
+ expect_fail:
57
+ If ``True`` and the test fails a functional check via an :keyword:`assert` statement, :func:`pytest.raises`,
58
+ :func:`pytest.warns`, or :func:`pytest.deprecated_call`, the test is considered to have passed.
59
+ If ``True`` and the test passes successfully, the test is considered to have failed.
60
+
61
+ expect_error:
62
+ Mark the result as a pass only if one of the given exception types is raised in the test.
63
+
64
+ skip:
65
+ Don't execute this test as part of the regression.
66
+ The test can still be run manually by setting :envvar:`COCOTB_TESTCASE`.
67
+
68
+ stage:
69
+ Order tests logically into stages.
70
+ Tests from earlier stages are run before tests from later stages.
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ *,
76
+ func: Callable[..., Coroutine[Trigger, None, None]],
77
+ name: Optional[str] = None,
78
+ module: Optional[str] = None,
79
+ doc: Optional[str] = None,
80
+ timeout_time: Optional[float] = None,
81
+ timeout_unit: TimeUnit = "step",
82
+ expect_fail: bool = False,
83
+ expect_error: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = (),
84
+ skip: bool = False,
85
+ stage: int = 0,
86
+ ) -> None:
87
+ self.func: Callable[..., Coroutine[Trigger, None, None]] = func
88
+ self.timeout_time = timeout_time
89
+ self.timeout_unit = timeout_unit
90
+ self.expect_fail = expect_fail
91
+ if isinstance(expect_error, type):
92
+ expect_error = (expect_error,)
93
+ self.expect_error = expect_error
94
+ self.skip = skip
95
+ self.stage = stage
96
+ self.name = self.func.__qualname__ if name is None else name
97
+ self.module = self.func.__module__ if module is None else module
98
+ self.doc = self.func.__doc__ if doc is None else doc
99
+ if self.doc is not None:
100
+ # cleanup docstring using `trim` function from PEP257
101
+ self.doc = inspect.cleandoc(self.doc)
102
+ self.fullname = f"{self.module}.{self.name}"
103
+
104
+
105
+ TestFuncType: TypeAlias = Callable[..., Coroutine[Trigger, None, None]]
106
+
107
+
108
+ class Parameterized:
109
+ def __init__(
110
+ self,
111
+ test_function: TestFuncType,
112
+ options: List[
113
+ Union[
114
+ Tuple[str, Sequence[object]],
115
+ Tuple[Sequence[str], Sequence[Sequence[object]]],
116
+ ]
117
+ ],
118
+ ) -> None:
119
+ self.test_template = Test(func=test_function)
120
+ self.options = options
121
+ # we are assuming the input checking is done in parametrize()
122
+
123
+ self._option_reprs: Dict[str, List[str]] = {}
124
+
125
+ for name, values in options:
126
+ if isinstance(name, str):
127
+ self._option_reprs[name] = _reprs(values)
128
+ else:
129
+ # transform to Dict[name, values]
130
+ transformed: Dict[str, List[object]] = {}
131
+ for nam_idx, nam in enumerate(name):
132
+ transformed[nam] = []
133
+ for value_array in cast("Sequence[Sequence[object]]", values):
134
+ value = value_array[nam_idx]
135
+ transformed[nam].append(value)
136
+ for n, vs in transformed.items():
137
+ self._option_reprs[n] = _reprs(vs)
138
+
139
+ def generate_tests(self) -> Iterable[Test]:
140
+ test_func = self.test_template.func
141
+ test_func_name = self.test_template.name
142
+
143
+ # this value is a list of ranges of the same length as each set of values in self.options for passing to itertools.product
144
+ option_indexes = [range(len(option[1])) for option in self.options]
145
+
146
+ # go through the cartesian product of all values of all options
147
+ for selected_options in product(*option_indexes):
148
+ test_kwargs: Dict[str, object] = {}
149
+ test_name_pieces: List[str] = [test_func_name]
150
+ for option_idx, select_idx in enumerate(selected_options):
151
+ option_name, option_values = self.options[option_idx]
152
+ selected_value = option_values[select_idx]
153
+
154
+ if isinstance(option_name, str):
155
+ # single params per option
156
+ selected_value = cast("Sequence[object]", selected_value)
157
+ test_kwargs[option_name] = selected_value
158
+ test_name_pieces.append(
159
+ f"/{option_name}={self._option_reprs[option_name][select_idx]}"
160
+ )
161
+ else:
162
+ # multiple params per option
163
+ selected_value = cast("Sequence[object]", selected_value)
164
+ for n, v in zip(option_name, selected_value):
165
+ test_kwargs[n] = v
166
+ test_name_pieces.append(
167
+ f"/{n}={self._option_reprs[n][select_idx]}"
168
+ )
169
+
170
+ parametrized_test_name = "".join(test_name_pieces)
171
+
172
+ # create wrapper function to bind kwargs
173
+ @functools.wraps(test_func)
174
+ async def _my_test(
175
+ dut: object, kwargs: Dict[str, object] = test_kwargs
176
+ ) -> None:
177
+ await test_func(dut, **kwargs)
178
+
179
+ yield Test(
180
+ func=_my_test,
181
+ name=parametrized_test_name,
182
+ timeout_time=self.test_template.timeout_time,
183
+ timeout_unit=self.test_template.timeout_unit,
184
+ expect_fail=self.test_template.expect_fail,
185
+ expect_error=self.test_template.expect_error,
186
+ skip=self.test_template.skip,
187
+ stage=self.test_template.stage,
188
+ )
189
+
190
+
191
+ def _reprs(values: Sequence[object]) -> List[str]:
192
+ result: List[str] = []
193
+ for value in values:
194
+ value_repr = _repr(value)
195
+ if value_repr is None:
196
+ # non-representable value in option, so default to index strings and give up
197
+ return [str(i) for i in range(len(values))]
198
+ else:
199
+ result.append(value_repr)
200
+ return result
201
+
202
+
203
+ def _repr(v: object) -> Optional[str]:
204
+ if isinstance(v, Enum):
205
+ return v.name
206
+ elif isinstance(v, str):
207
+ if len(v) <= 10 and v.isidentifier():
208
+ return v
209
+ else:
210
+ return None
211
+ elif isinstance(v, (int, float, bool, type(None))):
212
+ return repr(v)
213
+ elif isinstance(v, type):
214
+ return v.__qualname__
215
+ elif hasattr(v, "__qualname__"):
216
+ return v.__qualname__
217
+ else:
218
+ return None
219
+
220
+
221
+ class TestDecoratorType(Protocol):
222
+ # TODO use position only argument for *obj* so we aren't tied to that name.
223
+ @overload
224
+ def __call__(self, obj: TestFuncType) -> Test: ...
225
+ @overload
226
+ def __call__(self, obj: Parameterized) -> Parameterized: ...
227
+
228
+
229
+ @overload
230
+ def test(obj: TestFuncType) -> Test: ...
231
+
232
+
233
+ @overload
234
+ def test(obj: Parameterized) -> Parameterized: ...
235
+
236
+
237
+ @overload
238
+ def test(
239
+ *,
240
+ timeout_time: Optional[float] = None,
241
+ timeout_unit: TimeUnit = "step",
242
+ expect_fail: bool = False,
243
+ expect_error: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = (),
244
+ skip: bool = False,
245
+ stage: int = 0,
246
+ name: Optional[str] = None,
247
+ ) -> TestDecoratorType: ...
248
+
249
+
250
+ def test(
251
+ obj: Optional[Union[TestFuncType, Parameterized]] = None,
252
+ *,
253
+ timeout_time: Optional[float] = None,
254
+ timeout_unit: TimeUnit = "step",
255
+ expect_fail: bool = False,
256
+ expect_error: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = (),
257
+ skip: bool = False,
258
+ stage: int = 0,
259
+ name: Optional[str] = None,
260
+ ) -> Union[
261
+ Test,
262
+ Parameterized,
263
+ TestDecoratorType,
264
+ ]:
265
+ r"""
266
+ Decorator to register a Callable which returns a Coroutine as a test.
267
+
268
+ The test decorator provides a test timeout, and allows us to mark tests as skipped or expecting errors or failures.
269
+ Tests are evaluated in the order they are defined in a test module.
270
+
271
+ Usage:
272
+ .. code-block:: python
273
+
274
+ @cocotb.test(timeout_time=10, timeout_unit="ms")
275
+ async def test_thing(dut): ...
276
+
277
+ Args:
278
+ timeout_time:
279
+ Simulation time duration before timeout occurs.
280
+
281
+ .. versionadded:: 1.3
282
+
283
+ .. note::
284
+ Test timeout is intended for protection against deadlock.
285
+ Users should use :class:`~cocotb.triggers.with_timeout` if they require a
286
+ more general-purpose timeout mechanism.
287
+
288
+ timeout_unit:
289
+ Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does.
290
+
291
+ .. versionadded:: 1.3
292
+
293
+ .. versionchanged:: 2.0
294
+ Passing ``None`` as the *timeout_unit* argument was removed, use ``'step'`` instead.
295
+
296
+ expect_fail:
297
+ If ``True`` and the test fails a functional check via an :keyword:`assert` statement, :class:`pytest.raises`,
298
+ :class:`pytest.warns`, or :class:`pytest.deprecated_call` the test is considered to have passed.
299
+ If ``True`` and the test passes successfully, the test is considered to have failed.
300
+
301
+ expect_error:
302
+ Mark the result as a pass only if one of the exception types is raised in the test.
303
+ This is primarily for cocotb internal regression use for when a simulator error is expected.
304
+
305
+ Users are encouraged to use the following idiom instead::
306
+
307
+ @cocotb.test()
308
+ async def my_test(dut):
309
+ try:
310
+ await thing_that_should_fail()
311
+ except ExceptionIExpect:
312
+ pass
313
+ else:
314
+ assert False, "Exception did not occur"
315
+
316
+ .. versionchanged:: 1.3
317
+ Specific exception types can be expected
318
+
319
+ .. versionchanged:: 2.0
320
+ Passing a :class:`bool` value was removed.
321
+ Pass a specific :class:`Exception` or a tuple of Exceptions instead.
322
+
323
+ skip:
324
+ Don't execute this test as part of the regression. Test can still be run
325
+ manually by setting :make:var:`COCOTB_TESTCASE`.
326
+
327
+ stage:
328
+ Order tests logically into stages, where multiple tests can share a stage.
329
+ Defaults to 0.
330
+
331
+ name:
332
+ Override the default name of the test.
333
+ The default test name is the :meth:`__qualname__` of the decorated test function.
334
+
335
+ .. versionadded:: 2.0
336
+
337
+ Returns:
338
+ The test function to which the decorator is applied.
339
+
340
+ .. note::
341
+
342
+ To extend the test decorator, use the following template to create a new
343
+ ``cocotb.test``\-like wrapper.
344
+
345
+ .. code-block:: python
346
+
347
+ import functools
348
+
349
+
350
+ def test_extender(**decorator_kwargs):
351
+ def decorator(obj):
352
+ @cocotb.test(**decorator_kwargs)
353
+ @functools.wraps(obj)
354
+ async def test(dut, **test_kwargs):
355
+ # your code here
356
+ ...
357
+
358
+ return obj
359
+
360
+ return decorator
361
+
362
+ .. versionchanged:: 2.0
363
+ Support using decorator on test function without supplying parameters first.
364
+
365
+ Assumes all default values for the test parameters.
366
+
367
+ .. code-block:: python
368
+
369
+ @cocotb.test
370
+ async def test_thing(dut): ...
371
+
372
+
373
+ .. versionchanged:: 2.0
374
+ Decorated tests now return the decorated object.
375
+
376
+ """
377
+ if obj is not None:
378
+ if isinstance(obj, Parameterized):
379
+ return obj
380
+ else:
381
+ return Test(func=obj)
382
+
383
+ @overload
384
+ def wrapper(obj: TestFuncType) -> Test: ...
385
+
386
+ @overload
387
+ def wrapper(obj: Parameterized) -> Parameterized: ...
388
+
389
+ def wrapper(obj: Union[TestFuncType, Parameterized]) -> Union[Test, Parameterized]:
390
+ if isinstance(obj, Parameterized):
391
+ obj.test_template = Test(
392
+ func=obj.test_template.func,
393
+ name=name,
394
+ timeout_time=timeout_time,
395
+ timeout_unit=timeout_unit,
396
+ expect_fail=expect_fail,
397
+ expect_error=expect_error,
398
+ skip=skip,
399
+ stage=stage,
400
+ )
401
+ return obj
402
+ else:
403
+ return Test(
404
+ func=obj,
405
+ name=name,
406
+ timeout_time=timeout_time,
407
+ timeout_unit=timeout_unit,
408
+ expect_fail=expect_fail,
409
+ expect_error=expect_error,
410
+ skip=skip,
411
+ stage=stage,
412
+ )
413
+
414
+ return wrapper
415
+
416
+
417
+ def parametrize(
418
+ *options_by_tuple: Union[
419
+ Tuple[str, Sequence[object]], Tuple[Sequence[str], Sequence[Sequence[object]]]
420
+ ],
421
+ **options_by_name: Sequence[object],
422
+ ) -> Callable[[TestFuncType], Parameterized]:
423
+ """Decorator to generate parametrized tests from a single test function.
424
+
425
+ Decorates a test function with named test parameters.
426
+ The call to ``parametrize`` should include the name of each test parameter and the possible values each parameter can hold.
427
+ This will generate a test for each of the Cartesian products of the parameters and their values.
428
+
429
+ .. code-block:: python
430
+
431
+ @cocotb.test(
432
+ skip=False,
433
+ )
434
+ @cocotb.parametrize(
435
+ arg1=[0, 1],
436
+ arg2=["a", "b"],
437
+ )
438
+ async def my_test(arg1: int, arg2: str) -> None: ...
439
+
440
+ The above is equivalent to the following.
441
+
442
+ .. code-block:: python
443
+
444
+ @cocotb.test(skip=False)
445
+ async def my_test_0_a() -> None:
446
+ arg1, arg2 = 0, "a"
447
+ ...
448
+
449
+
450
+ @cocotb.test(skip=False)
451
+ async def my_test_0_b() -> None:
452
+ arg1, arg2 = 0, "b"
453
+ ...
454
+
455
+
456
+ @cocotb.test(skip=False)
457
+ async def my_test_1_a() -> None:
458
+ arg1, arg2 = 1, "a"
459
+ ...
460
+
461
+
462
+ @cocotb.test(skip=False)
463
+ async def my_test_1_b() -> None:
464
+ arg1, arg2 = 1, "b"
465
+ ...
466
+
467
+ Options can also be specified in much the same way that :meth:`TestFactory.add_option <cocotb.regression.TestFactory.add_option>` can,
468
+ either by supplying tuples of the parameter name to values,
469
+ or a sequence of variable names and a sequence of values.
470
+
471
+ .. code-block:: python
472
+
473
+ @cocotb.parametrize(
474
+ ("arg1", [0, 1]),
475
+ (("arg2", "arg3"), [(1, 2), (3, 4)]),
476
+ )
477
+ async def my_test_2(arg1: int, arg2: int, arg3: int) -> None: ...
478
+
479
+ Args:
480
+ options_by_tuple:
481
+ Tuple of parameter name to sequence of values for that parameter,
482
+ or tuple of sequence of parameter names to sequence of sequences of values for that pack of parameters.
483
+
484
+ options_by_name:
485
+ Mapping of parameter name to sequence of values for that parameter.
486
+
487
+ .. versionadded:: 2.0
488
+ """
489
+
490
+ # check good inputs
491
+ for i, option_by_tuple in enumerate(options_by_tuple):
492
+ if len(option_by_tuple) != 2:
493
+ raise ValueError(
494
+ f"Invalid option tuple {i}, expected exactly two fields `(name, values)`"
495
+ )
496
+ name, values = option_by_tuple
497
+ if not isinstance(name, str):
498
+ for n in name:
499
+ if not n.isidentifier():
500
+ raise ValueError("Option names must be valid Python identifiers")
501
+ values = cast("Sequence[Sequence[object]]", values)
502
+ for value in values:
503
+ if len(name) != len(value):
504
+ raise ValueError(
505
+ f"Invalid option tuple {i}, mismatching number of parameters ({name}) and values ({value})"
506
+ )
507
+ elif not name.isidentifier():
508
+ raise ValueError("Option names must be valid Python identifiers")
509
+
510
+ options = [*options_by_tuple, *options_by_name.items()]
511
+
512
+ def wrapper(f: TestFuncType) -> Parameterized:
513
+ return Parameterized(f, options)
514
+
515
+ return wrapper