cocotb 1.9.2__cp310-cp310-win32.whl → 2.0.0rc2__cp310-cp310-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.cp310-win32.exp +0 -0
  86. cocotb/simulator.cp310-win32.lib +0 -0
  87. cocotb/simulator.cp310-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/triggers.py CHANGED
@@ -1,1104 +1,67 @@
1
- # Copyright (c) 2013 Potential Ventures Ltd
2
- # Copyright (c) 2013 SolarFlare Communications Inc
3
- # All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- # * Redistributions of source code must retain the above copyright
8
- # notice, this list of conditions and the following disclaimer.
9
- # * Redistributions in binary form must reproduce the above copyright
10
- # notice, this list of conditions and the following disclaimer in the
11
- # documentation and/or other materials provided with the distribution.
12
- # * Neither the name of Potential Ventures Ltd,
13
- # SolarFlare Communications Inc nor the
14
- # names of its contributors may be used to endorse or promote products
15
- # derived from this software without specific prior written permission.
16
- #
17
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
- # DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
21
- # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
-
28
- """A collections of triggers which a testbench can await."""
29
-
30
- import abc
31
- import functools
32
- import inspect
1
+ # Copyright cocotb contributors
2
+ # Licensed under the Revised BSD License, see LICENSE for details.
3
+ # SPDX-License-Identifier: BSD-3-Clause
33
4
  import warnings
34
- from collections.abc import Awaitable
35
- from decimal import Decimal
36
- from numbers import Real
37
- from typing import Any, Callable, Coroutine, Optional, TypeVar, Union
38
5
 
39
- import cocotb
40
- from cocotb import outcomes, simulator
41
- from cocotb._deprecation import deprecated
42
- from cocotb.log import SimLog
43
- from cocotb.task import Task
44
- from cocotb.utils import (
45
- ParametrizedSingleton,
46
- get_sim_steps,
47
- get_time_from_sim_steps,
48
- lazy_property,
49
- remove_traceback_frames,
6
+ from cocotb._base_triggers import Event, Lock, NullTrigger, Trigger
7
+ from cocotb._extended_awaitables import (
8
+ ClockCycles,
9
+ Combine,
10
+ First,
11
+ SimTimeoutError,
12
+ Waitable,
13
+ with_timeout,
14
+ )
15
+ from cocotb._gpi_triggers import (
16
+ Edge,
17
+ FallingEdge,
18
+ GPITrigger,
19
+ NextTimeStep,
20
+ ReadOnly,
21
+ ReadWrite,
22
+ RisingEdge,
23
+ Timer,
24
+ ValueChange,
25
+ current_gpi_trigger,
50
26
  )
51
27
 
52
- T = TypeVar("T")
53
-
54
-
55
- def _pointer_str(obj):
56
- """
57
- Get the memory address of *obj* as used in :meth:`object.__repr__`.
58
-
59
- This is equivalent to ``sprintf("%p", id(obj))``, but python does not
60
- support ``%p``.
61
- """
62
- full_repr = object.__repr__(obj) # gives "<{type} object at {address}>"
63
- return full_repr.rsplit(" ", 1)[1][:-1]
64
-
65
-
66
- class TriggerException(Exception):
67
- pass
68
-
69
-
70
- class Trigger(Awaitable):
71
- """Base class to derive from."""
72
-
73
- # __dict__ is needed here for the `.log` lazy_property below to work.
74
- # The implementation of `_PyObject_GenericGetAttrWithDict` suggests that
75
- # despite its inclusion, __slots__ will overall give speed and memory
76
- # improvements:
77
- # - the `__dict__` is not actually constructed until it's needed, and that
78
- # only happens if the `.log` attribute is used, where performance
79
- # concerns no longer matter.
80
- # - Attribute setting and getting will still go through the slot machinery
81
- # first, as "data descriptors" take priority over dict access
82
- __slots__ = ("primed", "__weakref__", "__dict__")
83
-
84
- def __init__(self):
85
- self.primed = False
86
-
87
- @lazy_property
88
- def log(self):
89
- return SimLog("cocotb.%s" % (type(self).__qualname__), id(self))
90
-
91
- @abc.abstractmethod
92
- def prime(self, callback):
93
- """Set a callback to be invoked when the trigger fires.
94
-
95
- The callback will be invoked with a single argument, `self`.
96
-
97
- Sub-classes must override this, but should end by calling the base class
98
- method.
99
-
100
- .. warning::
101
- Do not call this directly within a :term:`task`. It is intended to be used
102
- only by the scheduler.
103
- """
104
- self.primed = True
105
-
106
- def unprime(self):
107
- """Remove the callback, and perform cleanup if necessary.
108
-
109
- After being un-primed, a Trigger may be re-primed again in the future.
110
- Calling `unprime` multiple times is allowed, subsequent calls should be
111
- a no-op.
112
-
113
- Sub-classes may override this, but should end by calling the base class
114
- method.
115
-
116
- .. warning::
117
- Do not call this directly within a :term:`task`. It is intended to be used
118
- only by the scheduler.
119
- """
120
- self.primed = False
121
-
122
- def __del__(self):
123
- # Ensure if a trigger drops out of scope we remove any pending callbacks
124
- self.unprime()
125
-
126
- @property
127
- def _outcome(self):
128
- """The result that `await this_trigger` produces in a coroutine.
129
-
130
- The default is to produce the trigger itself, which is done for
131
- ease of use with :class:`~cocotb.triggers.First`.
132
- """
133
- return outcomes.Value(self)
134
-
135
- def __await__(self):
136
- # hand the trigger back to the scheduler trampoline
137
- return (yield self)
138
-
139
-
140
- class PythonTrigger(Trigger):
141
- """Python triggers don't use GPI at all.
142
-
143
- For example: notification of coroutine completion.
144
- """
145
-
146
-
147
- class GPITrigger(Trigger):
148
- """Base Trigger class for GPI triggers.
149
-
150
- Consumes simulation time.
151
- """
152
-
153
- __slots__ = ("cbhdl",)
154
-
155
- def __init__(self):
156
- Trigger.__init__(self)
157
-
158
- # Required to ensure documentation can build
159
- # if simulator is not None:
160
- # self.cbhdl = simulator.create_callback(self)
161
- # else:
162
- self.cbhdl = None
163
-
164
- def unprime(self):
165
- """Disable a primed trigger, can be re-primed."""
166
- if self.cbhdl is not None:
167
- self.cbhdl.deregister()
168
- self.cbhdl = None
169
- Trigger.unprime(self)
170
-
171
-
172
- class Timer(GPITrigger):
173
- """Fire after the specified simulation time period has elapsed."""
174
-
175
- round_mode: str = "error"
176
-
177
- def __init__(
178
- self,
179
- time: Union[Real, Decimal] = None,
180
- units: str = "step",
181
- *,
182
- round_mode: Optional[str] = None,
183
- time_ps: Union[Real, Decimal] = None,
184
- ) -> None:
185
- """
186
- Args:
187
- time: The time value.
188
-
189
- .. versionchanged:: 1.5.0
190
- Previously this argument was misleadingly called `time_ps`.
191
-
192
- units: One of
193
- ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``.
194
- When *units* is ``'step'``,
195
- the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`).
196
-
197
- round_mode (str, optional):
198
- String specifying how to handle time values that sit between time steps
199
- (one of ``'error'``, ``'round'``, ``'ceil'``, ``'floor'``).
200
-
201
- Examples:
202
-
203
- >>> await Timer(100, units='ps')
204
-
205
- The time can also be a ``float``:
206
-
207
- >>> await Timer(100e-9, units='sec')
208
-
209
- which is particularly convenient when working with frequencies:
210
-
211
- >>> freq = 10e6 # 10 MHz
212
- >>> await Timer(1 / freq, units='sec')
213
-
214
- Other builtin exact numeric types can be used too:
215
-
216
- >>> from fractions import Fraction
217
- >>> await Timer(Fraction(1, 10), units='ns')
218
-
219
- >>> from decimal import Decimal
220
- >>> await Timer(Decimal('100e-9'), units='sec')
221
-
222
- These are most useful when using computed durations while
223
- avoiding floating point inaccuracies.
224
-
225
- See Also:
226
- :func:`~cocotb.utils.get_sim_steps`
227
-
228
- Raises:
229
- TriggerException: If a negative value is passed for Timer setup.
230
-
231
- .. versionchanged:: 1.5
232
- Raise an exception when Timer uses a negative value as it is undefined behavior.
233
- Warn for 0 as this will cause erratic behavior in some simulators as well.
234
-
235
- .. versionchanged:: 1.5
236
- Support ``'step'`` as the *units* argument to mean "simulator time step".
237
-
238
- .. deprecated:: 1.5
239
- Using ``None`` as the *units* argument is deprecated, use ``'step'`` instead.
240
-
241
- .. versionchanged:: 1.6
242
- Support rounding modes.
243
- """
244
- GPITrigger.__init__(self)
245
- if time_ps is not None:
246
- if time is not None:
247
- raise TypeError(
248
- "Gave argument to both the 'time' and deprecated 'time_ps' parameter"
249
- )
250
- time = time_ps
251
- warnings.warn(
252
- "The parameter name 'time_ps' has been renamed to 'time'. Please update your invocation.",
253
- DeprecationWarning,
254
- stacklevel=2,
255
- )
256
- else:
257
- if time is None:
258
- raise TypeError("Missing required argument 'time'")
259
- if time <= 0:
260
- if time == 0:
261
- warnings.warn(
262
- "Timer setup with value 0, which might exhibit undefined behavior in some simulators",
263
- category=RuntimeWarning,
264
- stacklevel=2,
265
- )
266
- else:
267
- raise TriggerException("Timer value time_ps must not be negative")
268
- if units is None:
269
- warnings.warn(
270
- 'Using units=None is deprecated, use units="step" instead.',
271
- DeprecationWarning,
272
- stacklevel=2,
273
- )
274
- units = "step" # don't propagate deprecated value
275
- if round_mode is None:
276
- round_mode = type(self).round_mode
277
- self.sim_steps = get_sim_steps(time, units, round_mode=round_mode)
278
-
279
- def prime(self, callback):
280
- """Register for a timed callback."""
281
- if self.cbhdl is None:
282
- self.cbhdl = simulator.register_timed_callback(
283
- self.sim_steps, callback, self
284
- )
285
- if self.cbhdl is None:
286
- raise TriggerException("Unable set up %s Trigger" % (str(self)))
287
- GPITrigger.prime(self, callback)
288
-
289
- def __repr__(self):
290
- return "<{} of {:1.2f}ps at {}>".format(
291
- type(self).__qualname__,
292
- get_time_from_sim_steps(self.sim_steps, units="ps"),
293
- _pointer_str(self),
294
- )
295
-
296
-
297
- # This is needed to make our custom metaclass work with abc.ABCMeta used in the
298
- # `Trigger` base class.
299
- class _ParameterizedSingletonAndABC(ParametrizedSingleton, abc.ABCMeta):
300
- pass
301
-
302
-
303
- class ReadOnly(GPITrigger, metaclass=_ParameterizedSingletonAndABC):
304
- """Fires when the current simulation timestep moves to the read-only phase.
305
-
306
- The read-only phase is entered when the current timestep no longer has any further delta steps.
307
- This will be a point where all the signal values are stable as there are no more RTL events scheduled for the timestep.
308
- The simulator will not allow scheduling of more events in this timestep.
309
- Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final.
310
- """
311
-
312
- __slots__ = ()
313
-
314
- @classmethod
315
- def __singleton_key__(cls):
316
- return None
317
-
318
- def __init__(self):
319
- GPITrigger.__init__(self)
320
-
321
- def prime(self, callback):
322
- if self.cbhdl is None:
323
- self.cbhdl = simulator.register_readonly_callback(callback, self)
324
- if self.cbhdl is None:
325
- raise TriggerException("Unable set up %s Trigger" % (str(self)))
326
- GPITrigger.prime(self, callback)
327
-
328
- def __repr__(self):
329
- return "{}()".format(type(self).__qualname__)
330
-
331
-
332
- class ReadWrite(GPITrigger, metaclass=_ParameterizedSingletonAndABC):
333
- """Fires when the read-write portion of the simulation cycles is reached."""
334
-
335
- __slots__ = ()
336
-
337
- @classmethod
338
- def __singleton_key__(cls):
339
- return None
340
-
341
- def __init__(self):
342
- GPITrigger.__init__(self)
343
-
344
- def prime(self, callback):
345
- if self.cbhdl is None:
346
- self.cbhdl = simulator.register_rwsynch_callback(callback, self)
347
- if self.cbhdl is None:
348
- raise TriggerException("Unable set up %s Trigger" % (str(self)))
349
- GPITrigger.prime(self, callback)
350
-
351
- def __repr__(self):
352
- return "{}()".format(type(self).__qualname__)
353
-
354
-
355
- class NextTimeStep(GPITrigger, metaclass=_ParameterizedSingletonAndABC):
356
- """Fires when the next time step is started."""
357
-
358
- __slots__ = ()
359
-
360
- @classmethod
361
- def __singleton_key__(cls):
362
- return None
363
-
364
- def __init__(self):
365
- GPITrigger.__init__(self)
366
-
367
- def prime(self, callback):
368
- if self.cbhdl is None:
369
- self.cbhdl = simulator.register_nextstep_callback(callback, self)
370
- if self.cbhdl is None:
371
- raise TriggerException("Unable set up %s Trigger" % (str(self)))
372
- GPITrigger.prime(self, callback)
373
-
374
- def __repr__(self):
375
- return "{}()".format(type(self).__qualname__)
376
-
377
-
378
- class _EdgeBase(GPITrigger, metaclass=_ParameterizedSingletonAndABC):
379
- """Internal base class that fires on a given edge of a signal."""
380
-
381
- __slots__ = ("signal",)
382
-
383
- @classmethod
384
- @property
385
- def _edge_type(self):
386
- """The edge type, as understood by the C code. Must be set in sub-classes."""
387
- raise NotImplementedError
388
-
389
- @classmethod
390
- def __singleton_key__(cls, signal):
391
- return signal
392
-
393
- def __init__(self, signal):
394
- super().__init__()
395
- self.signal = signal
396
-
397
- def prime(self, callback):
398
- """Register notification of a value change via a callback"""
399
- if self.cbhdl is None:
400
- self.cbhdl = simulator.register_value_change_callback(
401
- self.signal._handle, callback, type(self)._edge_type, self
402
- )
403
- if self.cbhdl is None:
404
- raise TriggerException("Unable set up %s Trigger" % (str(self)))
405
- super().prime(callback)
406
-
407
- def __repr__(self):
408
- return "{}({!r})".format(type(self).__qualname__, self.signal)
409
-
410
-
411
- class RisingEdge(_EdgeBase):
412
- """Fires on the rising edge of *signal*, on a transition from ``0`` to ``1``."""
413
-
414
- __slots__ = ()
415
- _edge_type = 1
416
-
417
-
418
- class FallingEdge(_EdgeBase):
419
- """Fires on the falling edge of *signal*, on a transition from ``1`` to ``0``."""
420
-
421
- __slots__ = ()
422
- _edge_type = 2
423
-
424
-
425
- class Edge(_EdgeBase):
426
- """Fires on any value change of *signal*."""
427
-
428
- __slots__ = ()
429
- _edge_type = 3
430
-
431
-
432
- class _Event(PythonTrigger):
433
- """Unique instance used by the Event object.
434
-
435
- One created for each attempt to wait on the event so that the scheduler
436
- can maintain a dictionary of indexing each individual coroutine.
437
-
438
- FIXME: This will leak - need to use peers to ensure everything is removed
439
- """
440
-
441
- def __init__(self, parent):
442
- PythonTrigger.__init__(self)
443
- self.parent = parent
444
-
445
- def prime(self, callback):
446
- self._callback = callback
447
- self.parent._prime_trigger(self, callback)
448
- Trigger.prime(self, callback)
449
-
450
- def __call__(self):
451
- self._callback(self)
452
-
453
- def __repr__(self):
454
- return "<{!r}.wait() at {}>".format(self.parent, _pointer_str(self))
455
-
456
-
457
- class Event:
458
- """Event to permit synchronization between two coroutines.
459
-
460
- Awaiting :meth:`wait()` from one coroutine will block the coroutine until
461
- :meth:`set()` is called somewhere else.
462
- """
463
-
464
- def __init__(self, name=None):
465
- self._pending = []
466
- self.name = name
467
- self._fired = False
468
- self.data = None
469
-
470
- @property
471
- @deprecated("The `.fired` attribute is deprecated, use `.is_set()` instead.")
472
- def fired(self) -> bool:
473
- return self._fired
474
-
475
- def _prime_trigger(self, trigger, callback):
476
- self._pending.append(trigger)
477
-
478
- def set(self, data=None):
479
- """Wake up all coroutines blocked on this event."""
480
- self._fired = True
481
- self.data = data
482
-
483
- p = self._pending[:]
484
-
485
- self._pending = []
486
-
487
- for trigger in p:
488
- trigger()
489
-
490
- def wait(self):
491
- """Get a trigger which fires when another coroutine sets the event.
492
-
493
- If the event has already been set, the trigger will fire immediately.
494
-
495
- To reset the event (and enable the use of ``wait`` again),
496
- :meth:`clear` should be called.
497
- """
498
- if self._fired:
499
- return NullTrigger(name=f"{str(self)}.wait()")
500
- return _Event(self)
501
-
502
- def clear(self):
503
- """Clear this event that has fired.
504
-
505
- Subsequent calls to :meth:`~cocotb.triggers.Event.wait` will block until
506
- :meth:`~cocotb.triggers.Event.set` is called again."""
507
- self._fired = False
508
-
509
- def is_set(self) -> bool:
510
- """Return ``True`` if event has been set."""
511
- return self._fired
512
-
513
- def __repr__(self):
514
- if self.name is None:
515
- fmt = "<{0} at {2}>"
516
- else:
517
- fmt = "<{0} for {1} at {2}>"
518
- return fmt.format(type(self).__qualname__, self.name, _pointer_str(self))
519
-
520
-
521
- class _InternalEvent(PythonTrigger):
522
- """Event used internally for triggers that need cross-coroutine synchronization.
523
-
524
- This Event can only be waited on once, by a single coroutine.
525
-
526
- Provides transparent __repr__ pass-through to the Trigger using this event,
527
- providing a better debugging experience.
528
- """
529
-
530
- def __init__(self, parent):
531
- PythonTrigger.__init__(self)
532
- self.parent = parent
533
- self._callback = None
534
- self.fired = False
535
- self.data = None
536
-
537
- def prime(self, callback):
538
- if self._callback is not None:
539
- raise RuntimeError("This Trigger may only be awaited once")
540
- self._callback = callback
541
- Trigger.prime(self, callback)
542
- if self.fired:
543
- self._callback(self)
544
-
545
- def set(self, data=None):
546
- """Wake up coroutine blocked on this event."""
547
- self.fired = True
548
- self.data = data
549
-
550
- if self._callback is not None:
551
- self._callback(self)
552
-
553
- def is_set(self) -> bool:
554
- """Return true if event has been set."""
555
- return self.fired
556
-
557
- def __await__(self):
558
- if self.primed:
559
- raise RuntimeError("Only one coroutine may await this Trigger")
560
- # hand the trigger back to the scheduler trampoline
561
- return (yield self)
562
-
563
- def __repr__(self):
564
- return repr(self.parent)
565
-
566
-
567
- class _Lock(PythonTrigger):
568
- """Unique instance used by the Lock object.
569
-
570
- One created for each attempt to acquire the Lock so that the scheduler
571
- can maintain a dictionary of indexing each individual coroutine.
572
-
573
- FIXME: This will leak - need to use peers to ensure everything is removed.
574
- """
575
-
576
- def __init__(self, parent):
577
- PythonTrigger.__init__(self)
578
- self.parent = parent
579
-
580
- def prime(self, callback):
581
- self._callback = callback
582
- self.parent._prime_trigger(self, callback)
583
- Trigger.prime(self, callback)
584
-
585
- def __call__(self):
586
- self._callback(self)
587
-
588
- def __repr__(self):
589
- return "<{!r}.acquire() at {}>".format(self.parent, _pointer_str(self))
590
-
591
-
592
- _FT = TypeVar("_FT", bound=Callable)
593
-
594
-
595
- def _locked_back_compat_dec(func: _FT) -> _FT:
596
- # this hack is implemented this way so that it is easy to delete later
597
-
598
- def get(inst, _=None):
599
- method = _LockBackCompat(inst, func)
600
- # cache bound method on object to override the descriptor
601
- setattr(inst, func.__name__, method)
602
- return method
603
-
604
- # Override the default function descriptor with one that returns a _LockBackCompat object that *acts* like a bound method,
605
- # but also defines the __bool__ overload that provides the deprecation warning.
606
- func.__get__ = get
607
- return func
608
-
28
+ __all__ = (
29
+ "ClockCycles",
30
+ "Combine",
31
+ "Edge",
32
+ "Event",
33
+ "FallingEdge",
34
+ "First",
35
+ "GPITrigger",
36
+ "Lock",
37
+ "NextTimeStep",
38
+ "NullTrigger",
39
+ "ReadOnly",
40
+ "ReadWrite",
41
+ "RisingEdge",
42
+ "SimTimeoutError",
43
+ "Timer",
44
+ "Trigger",
45
+ "ValueChange",
46
+ "Waitable",
47
+ "current_gpi_trigger",
48
+ "with_timeout",
49
+ )
609
50
 
610
- class _LockBackCompat:
611
- def __init__(self, inst, func):
612
- self._inst = inst
613
- self._func = func
614
- functools.update_wrapper(self, func)
51
+ # Set __module__ on re-exports
52
+ for name in __all__:
53
+ obj = globals()[name]
54
+ obj.__module__ = __name__
615
55
 
616
- def __call__(self):
617
- return self._func(self._inst)
618
56
 
619
- def __bool__(self):
57
+ def __getattr__(name: str) -> object:
58
+ if name == "Join":
620
59
  warnings.warn(
621
- f"Using `{self._func.__qualname__}` as a boolean attribute is deprecated. Call it as if it were a method instead.",
60
+ "Join has been moved to `cocotb.task`.",
622
61
  DeprecationWarning,
623
62
  stacklevel=2,
624
63
  )
625
- return self._func(self._inst)
626
-
627
-
628
- class Lock:
629
- """Lock primitive (not re-entrant).
630
-
631
- This can be used as::
632
-
633
- await lock.acquire()
634
- try:
635
- # do some stuff
636
- finally:
637
- lock.release()
638
-
639
- .. versionchanged:: 1.4
640
-
641
- The lock can be used as an asynchronous context manager in an
642
- :keyword:`async with` statement::
643
-
644
- async with lock:
645
- # do some stuff
646
- """
647
-
648
- def __init__(self, name=None):
649
- self._pending_unprimed = []
650
- self._pending_primed = []
651
- self.name = name
652
- self._locked = False
653
-
654
- @_locked_back_compat_dec
655
- def locked(self) -> bool:
656
- """Return ``True`` if the lock has been acquired.
657
-
658
- .. versionchanged:: 2.0
659
- This is now a method rather than an attribute, to match :meth:`asyncio.Lock.locked`.
660
- """
661
- return self._locked
662
-
663
- def _prime_trigger(self, trigger, callback):
664
- self._pending_unprimed.remove(trigger)
665
-
666
- if not self._locked:
667
- self._locked = True
668
- callback(trigger)
669
- else:
670
- self._pending_primed.append(trigger)
671
-
672
- def acquire(self):
673
- """Produce a trigger which fires when the lock is acquired."""
674
- trig = _Lock(self)
675
- self._pending_unprimed.append(trig)
676
- return trig
677
-
678
- def release(self):
679
- """Release the lock."""
680
- if not self._locked:
681
- raise TriggerException(
682
- "Attempt to release an unacquired Lock %s" % (str(self))
683
- )
684
-
685
- self._locked = False
686
-
687
- # nobody waiting for this lock
688
- if not self._pending_primed:
689
- return
690
-
691
- trigger = self._pending_primed.pop(0)
692
- self._locked = True
693
- trigger()
694
-
695
- def __repr__(self):
696
- if self.name is None:
697
- fmt = "<{0} [{2} waiting] at {3}>"
698
- else:
699
- fmt = "<{0} for {1} [{2} waiting] at {3}>"
700
- return fmt.format(
701
- type(self).__qualname__,
702
- self.name,
703
- len(self._pending_primed),
704
- _pointer_str(self),
705
- )
706
-
707
- @deprecated("`bool(lock)` is deprecated. Use the `.locked()` method instead.")
708
- def __bool__(self):
709
- """Provide boolean of a Lock"""
710
- return self._locked
711
-
712
- async def __aenter__(self):
713
- return await self.acquire()
714
-
715
- async def __aexit__(self, exc_type, exc, tb):
716
- self.release()
717
-
718
-
719
- class NullTrigger(Trigger):
720
- """Fires immediately.
721
-
722
- Primarily for internal scheduler use.
723
- """
724
-
725
- def __init__(self, name=None, outcome=None, _outcome=None):
726
- super().__init__()
727
- self._callback = None
728
- self.name = name
729
- if outcome is not None:
730
- warnings.warn(
731
- "Passing the `outcome` argument and having that be the result of the `await` expression on this Trigger is deprecated.",
732
- DeprecationWarning,
733
- stacklevel=2,
734
- )
735
- self.__outcome = _outcome if _outcome is not None else outcome
736
-
737
- @property
738
- def _outcome(self):
739
- if self.__outcome is not None:
740
- return self.__outcome
741
- return super()._outcome
742
-
743
- def prime(self, callback):
744
- callback(self)
745
-
746
- def __repr__(self):
747
- if self.name is None:
748
- fmt = "<{0} at {2}>"
749
- else:
750
- fmt = "<{0} for {1} at {2}>"
751
- return fmt.format(type(self).__qualname__, self.name, _pointer_str(self))
752
-
753
-
754
- class Join(PythonTrigger, metaclass=_ParameterizedSingletonAndABC):
755
- r"""Fires when a task completes.
756
-
757
- The result of blocking on the trigger can be used to get the coroutine
758
- result::
759
-
760
- async def coro_inner():
761
- await Timer(1, units='ns')
762
- return "Hello world"
763
-
764
- task = cocotb.start_soon(coro_inner())
765
- result = await Join(task)
766
- assert result == "Hello world"
767
-
768
- If the coroutine threw an exception, the :keyword:`await` will re-raise it.
769
-
770
- """
771
-
772
- __slots__ = ("_coroutine",)
773
-
774
- @classmethod
775
- def __singleton_key__(cls, coroutine):
776
- return coroutine
777
-
778
- def __init__(self, coroutine):
779
- super().__init__()
780
- self._coroutine = coroutine
781
-
782
- @property
783
- def _outcome(self):
784
- outcome = self._coroutine._outcome
785
- if type(self._coroutine) is Task and isinstance(outcome, outcomes.Error):
786
- warnings.warn(
787
- "Tasks started with `cocotb.start_soon()` that raise Exceptions will not propagate those Exceptions in 2.0. "
788
- "Instead such Tasks will *always* fail the test. "
789
- "An alternative for `cocotb.start_soon()` that *always* propagates Exceptions will be added in 2.0.",
790
- FutureWarning,
791
- )
792
- return outcome
793
-
794
- @property
795
- @deprecated("Use `task.result()` to get the result of a joined Task.")
796
- def retval(self):
797
- """The return value of the joined coroutine.
798
-
799
- .. deprecated:: 1.9
800
-
801
- Use :meth:`Task.result() <cocotb.task.Task.result` to get the result of a joined Task.
802
-
803
- .. code-block: python3
804
-
805
- forked = cocotb.start_soon(mycoro())
806
- await forked.join()
807
- result = forked.result()
808
- """
809
- return self._coroutine.result()
810
-
811
- def prime(self, callback):
812
- if self._coroutine.done():
813
- callback(self)
814
- else:
815
- super().prime(callback)
64
+ from cocotb.task import Join # noqa: PLC0415
816
65
 
817
- def __repr__(self):
818
- return "{}({!s})".format(type(self).__qualname__, self._coroutine)
819
-
820
- def __await__(self):
821
- warnings.warn(
822
- "`await`ing a Join trigger will return the Join trigger and not the result of the joined Task in 2.0.",
823
- FutureWarning,
824
- stacklevel=2,
825
- )
826
- return (yield self)
827
-
828
-
829
- class Waitable(Awaitable):
830
- """
831
- Base class for trigger-like objects implemented using coroutines.
832
-
833
- This converts a `_wait` abstract method into a suitable `__await__`.
834
- """
835
-
836
- __slots__ = ()
837
-
838
- async def _wait(self):
839
- """
840
- Should be implemented by the sub-class. Called by `await self` to
841
- convert the waitable object into a coroutine.
842
- """
843
- raise NotImplementedError
844
-
845
- def __await__(self):
846
- return self._wait().__await__()
847
-
848
-
849
- class _AggregateWaitable(Waitable):
850
- """
851
- Base class for Waitables that take mutiple triggers in their constructor
852
- """
853
-
854
- __slots__ = ("triggers",)
855
-
856
- def __init__(self, *triggers):
857
- self.triggers = triggers
858
-
859
- # Do some basic type-checking up front, rather than waiting until we
860
- # await them.
861
- allowed_types = (Trigger, Waitable, Task)
862
- for trigger in self.triggers:
863
- if not isinstance(trigger, allowed_types):
864
- raise TypeError(
865
- "All triggers must be instances of Trigger! Got: {}".format(
866
- type(trigger).__qualname__
867
- )
868
- )
869
-
870
- def __repr__(self):
871
- # no _pointer_str here, since this is not a trigger, so identity
872
- # doesn't matter.
873
- return "{}({})".format(
874
- type(self).__qualname__,
875
- ", ".join(
876
- repr(Join(t)) if isinstance(t, Task) else repr(t) for t in self.triggers
877
- ),
878
- )
879
-
880
-
881
- async def _wait_callback(trigger, callback):
882
- """
883
- Wait for a trigger, and call `callback` with the outcome of the await.
884
- """
885
- try:
886
- ret = outcomes.Value(await trigger)
887
- except BaseException as exc:
888
- # hide this from the traceback
889
- ret = outcomes.Error(remove_traceback_frames(exc, ["_wait_callback"]))
890
- callback(ret)
891
-
892
-
893
- class Combine(_AggregateWaitable):
894
- """
895
- Fires when all of *triggers* have fired.
896
-
897
- Like most triggers, this simply returns itself.
898
-
899
- This is similar to Verilog's ``join``.
900
- """
901
-
902
- __slots__ = ()
903
-
904
- async def _wait(self):
905
- waiters = []
906
- e = _InternalEvent(self)
907
- triggers = list(self.triggers)
908
-
909
- # start a parallel task for each trigger
910
- for t in triggers:
911
- # t=t is needed for the closure to bind correctly
912
- def on_done(ret, t=t):
913
- triggers.remove(t)
914
- if not triggers:
915
- e.set()
916
- ret.get() # re-raise any exception
917
-
918
- waiters.append(cocotb.start_soon(_wait_callback(t, on_done)))
919
-
920
- # wait for the last waiter to complete
921
- await e
922
- return self
923
-
924
-
925
- class First(_AggregateWaitable):
926
- """
927
- Fires when the first trigger in *triggers* fires.
928
-
929
- Returns the result of the trigger that fired.
930
-
931
- This is similar to Verilog's ``join_any``.
932
-
933
- .. note::
934
- The event loop is single threaded, so while events may be simultaneous
935
- in simulation time, they can never be simultaneous in real time.
936
- For this reason, the value of ``t_ret is t1`` in the following example
937
- is implementation-defined, and will vary by simulator::
938
-
939
- t1 = Timer(10, units='ps')
940
- t2 = Timer(10, units='ps')
941
- t_ret = await First(t1, t2)
942
-
943
- .. note::
944
- In the old-style :ref:`generator-based coroutines <yield-syntax>`, ``t = yield [a, b]`` was another spelling of
945
- ``t = yield First(a, b)``. This spelling is no longer available when using :keyword:`await`-based
946
- coroutines.
947
- """
948
-
949
- __slots__ = ()
950
-
951
- async def _wait(self):
952
- waiters = []
953
- e = _InternalEvent(self)
954
- completed = []
955
- # start a parallel task for each trigger
956
- for t in self.triggers:
957
-
958
- def on_done(ret):
959
- completed.append(ret)
960
- e.set()
961
-
962
- waiters.append(cocotb.start_soon(_wait_callback(t, on_done)))
963
-
964
- # wait for a waiter to complete
965
- await e
966
-
967
- # kill all the other waiters
968
- # TODO: Should this kill the coroutines behind any Join triggers?
969
- # Right now it does not.
970
- for w in waiters:
971
- w.kill()
972
-
973
- # These lines are the way they are to make tracebacks readable:
974
- # - The comment helps the user understand why they are seeing the
975
- # traceback, even if it is obvious top cocotb maintainers.
976
- # - Using `NullTrigger` here instead of `result = completed[0].get()`
977
- # means we avoid inserting an `outcome.get` frame in the traceback
978
- first_trigger = NullTrigger(_outcome=completed[0])
979
- return await first_trigger # the first of multiple triggers that fired
980
-
981
-
982
- class ClockCycles(Waitable):
983
- """Fires after *num_cycles* transitions of *signal* from ``0`` to ``1``."""
984
-
985
- def __init__(self, signal, num_cycles, rising=True):
986
- """
987
- Args:
988
- signal: The signal to monitor.
989
- num_cycles (int): The number of cycles to count.
990
- rising (bool, optional): If ``True``, the default, count rising edges.
991
- Otherwise, count falling edges.
992
- """
993
- self.signal = signal
994
- self.num_cycles = num_cycles
995
- if rising is True:
996
- self._type = RisingEdge
997
- else:
998
- self._type = FallingEdge
999
-
1000
- async def _wait(self):
1001
- trigger = self._type(self.signal)
1002
- for _ in range(self.num_cycles):
1003
- await trigger
1004
- return self
1005
-
1006
- def __repr__(self):
1007
- # no _pointer_str here, since this is not a trigger, so identity
1008
- # doesn't matter.
1009
- if self._type is RisingEdge:
1010
- fmt = "{}({!r}, {!r})"
1011
- else:
1012
- fmt = "{}({!r}, {!r}, rising=False)"
1013
- return fmt.format(type(self).__qualname__, self.signal, self.num_cycles)
1014
-
1015
-
1016
- async def with_timeout(
1017
- trigger: Union[Trigger, Waitable, Task, Coroutine[Any, Any, T]],
1018
- timeout_time: Union[float, Decimal],
1019
- timeout_unit: str = "step",
1020
- round_mode: Optional[str] = None,
1021
- ) -> T:
1022
- r"""
1023
- Waits on triggers or coroutines, throws an exception if it waits longer than the given time.
1024
-
1025
- When a :term:`python:coroutine` is passed,
1026
- the callee coroutine is started,
1027
- the caller blocks until the callee completes,
1028
- and the callee's result is returned to the caller.
1029
- If timeout occurs, the callee is killed
1030
- and :exc:`SimTimeoutError` is raised.
1031
-
1032
- When an unstarted :class:`~cocotb.coroutine`\ is passed,
1033
- the callee coroutine is started,
1034
- the caller blocks until the callee completes,
1035
- and the callee's result is returned to the caller.
1036
- If timeout occurs, the callee `continues to run`
1037
- and :exc:`SimTimeoutError` is raised.
1038
-
1039
- When a :term:`task` is passed,
1040
- the caller blocks until the callee completes
1041
- and the callee's result is returned to the caller.
1042
- If timeout occurs, the callee `continues to run`
1043
- and :exc:`SimTimeoutError` is raised.
1044
-
1045
- If a :class:`~cocotb.triggers.Trigger` or :class:`~cocotb.triggers.Waitable` is passed,
1046
- the caller blocks until the trigger fires,
1047
- and the trigger is returned to the caller.
1048
- If timeout occurs, the trigger is cancelled
1049
- and :exc:`SimTimeoutError` is raised.
1050
-
1051
- Usage:
1052
-
1053
- .. code-block:: python
1054
-
1055
- await with_timeout(coro, 100, 'ns')
1056
- await with_timeout(First(coro, event.wait()), 100, 'ns')
1057
-
1058
- Args:
1059
- trigger (:class:`~cocotb.triggers.Trigger`, :class:`~cocotb.triggers.Waitable`, :class:`~cocotb.task.Task`, or :term:`python:coroutine`):
1060
- A single object that could be right of an :keyword:`await` expression in cocotb.
1061
- timeout_time (numbers.Real or decimal.Decimal):
1062
- Simulation time duration before timeout occurs.
1063
- timeout_unit (str, optional):
1064
- Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does.
1065
- round_mode (str, optional):
1066
- String specifying how to handle time values that sit between time steps
1067
- (one of ``'error'``, ``'round'``, ``'ceil'``, ``'floor'``).
1068
-
1069
- Returns:
1070
- First trigger that completed if timeout did not occur.
1071
-
1072
- Raises:
1073
- :exc:`SimTimeoutError`: If timeout occurs.
1074
-
1075
- .. versionadded:: 1.3
1076
-
1077
- .. deprecated:: 1.5
1078
- Using ``None`` as the *timeout_unit* argument is deprecated, use ``'step'`` instead.
1079
-
1080
- .. versionchanged:: 1.7.0
1081
- Support passing :term:`python:coroutine`\ s.
1082
- """
1083
- if timeout_unit is None:
1084
- warnings.warn(
1085
- 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.',
1086
- DeprecationWarning,
1087
- stacklevel=2,
1088
- )
1089
- timeout_unit = "step" # don't propagate deprecated value
1090
- if inspect.iscoroutine(trigger):
1091
- trigger = cocotb.start_soon(trigger)
1092
- shielded = False
1093
- else:
1094
- shielded = True
1095
- timeout_timer = cocotb.triggers.Timer(
1096
- timeout_time, timeout_unit, round_mode=round_mode
1097
- )
1098
- res = await First(timeout_timer, trigger)
1099
- if res is timeout_timer:
1100
- if not shielded:
1101
- trigger.kill()
1102
- raise cocotb.result.SimTimeoutError
1103
- else:
1104
- return res
66
+ return Join
67
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")