cocotb 2.0.0rc2__cp311-cp311-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cocotb might be problematic. Click here for more details.
- cocotb/_ANSI.py +65 -0
- cocotb/__init__.py +125 -0
- cocotb/_base_triggers.py +515 -0
- cocotb/_bridge.py +186 -0
- cocotb/_decorators.py +515 -0
- cocotb/_deprecation.py +36 -0
- cocotb/_exceptions.py +7 -0
- cocotb/_extended_awaitables.py +419 -0
- cocotb/_gpi_triggers.py +385 -0
- cocotb/_init.py +301 -0
- cocotb/_outcomes.py +54 -0
- cocotb/_profiling.py +46 -0
- cocotb/_py_compat.py +148 -0
- cocotb/_scheduler.py +448 -0
- cocotb/_test.py +248 -0
- cocotb/_test_factory.py +312 -0
- cocotb/_test_functions.py +42 -0
- cocotb/_typing.py +7 -0
- cocotb/_utils.py +274 -0
- cocotb/_version.py +4 -0
- cocotb/_xunit_reporter.py +66 -0
- cocotb/clock.py +419 -0
- cocotb/debug.py +24 -0
- cocotb/handle.py +1752 -0
- cocotb/libs/libcocotb.so +0 -0
- cocotb/libs/libcocotbfli_modelsim.so +0 -0
- cocotb/libs/libcocotbutils.so +0 -0
- cocotb/libs/libcocotbvhpi_aldec.so +0 -0
- cocotb/libs/libcocotbvhpi_ius.so +0 -0
- cocotb/libs/libcocotbvhpi_modelsim.so +0 -0
- cocotb/libs/libcocotbvhpi_nvc.so +0 -0
- cocotb/libs/libcocotbvpi_aldec.so +0 -0
- cocotb/libs/libcocotbvpi_dsim.so +0 -0
- cocotb/libs/libcocotbvpi_ghdl.so +0 -0
- cocotb/libs/libcocotbvpi_icarus.vpl +0 -0
- cocotb/libs/libcocotbvpi_ius.so +0 -0
- cocotb/libs/libcocotbvpi_modelsim.so +0 -0
- cocotb/libs/libcocotbvpi_vcs.so +0 -0
- cocotb/libs/libcocotbvpi_verilator.so +0 -0
- cocotb/libs/libembed.so +0 -0
- cocotb/libs/libgpi.so +0 -0
- cocotb/libs/libgpilog.so +0 -0
- cocotb/libs/libpygpilog.so +0 -0
- cocotb/logging.py +424 -0
- cocotb/py.typed +0 -0
- cocotb/queue.py +225 -0
- cocotb/regression.py +896 -0
- cocotb/result.py +38 -0
- cocotb/share/def/.gitignore +2 -0
- cocotb/share/def/README.md +4 -0
- cocotb/share/def/aldec.def +61 -0
- cocotb/share/def/ghdl.def +43 -0
- cocotb/share/def/icarus.def +43 -0
- cocotb/share/def/modelsim.def +138 -0
- cocotb/share/include/cocotb_utils.h +70 -0
- cocotb/share/include/embed.h +33 -0
- cocotb/share/include/exports.h +20 -0
- cocotb/share/include/gpi.h +459 -0
- cocotb/share/include/gpi_logging.h +291 -0
- cocotb/share/include/py_gpi_logging.h +33 -0
- cocotb/share/include/vhpi_user_ext.h +26 -0
- cocotb/share/include/vpi_user_ext.h +33 -0
- cocotb/share/lib/verilator/verilator.cpp +209 -0
- cocotb/simtime.py +230 -0
- cocotb/simulator.cpython-311-darwin.so +0 -0
- cocotb/simulator.pyi +107 -0
- cocotb/task.py +590 -0
- cocotb/triggers.py +67 -0
- cocotb/types/__init__.py +31 -0
- cocotb/types/_abstract_array.py +151 -0
- cocotb/types/_array.py +295 -0
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +333 -0
- cocotb/types/_logic_array.py +868 -0
- cocotb/types/_range.py +197 -0
- cocotb/types/_resolve.py +76 -0
- cocotb/utils.py +110 -0
- cocotb-2.0.0rc2.dist-info/METADATA +60 -0
- cocotb-2.0.0rc2.dist-info/RECORD +115 -0
- cocotb-2.0.0rc2.dist-info/WHEEL +5 -0
- cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
- cocotb-2.0.0rc2.dist-info/licenses/LICENSE +29 -0
- cocotb-2.0.0rc2.dist-info/top_level.txt +23 -0
- cocotb_tools/__init__.py +0 -0
- cocotb_tools/_coverage.py +33 -0
- cocotb_tools/_vendor/__init__.py +3 -0
- cocotb_tools/_vendor/distutils_version.py +346 -0
- cocotb_tools/check_results.py +65 -0
- cocotb_tools/combine_results.py +152 -0
- cocotb_tools/config.py +241 -0
- cocotb_tools/ipython_support.py +99 -0
- cocotb_tools/makefiles/Makefile.deprecations +27 -0
- cocotb_tools/makefiles/Makefile.inc +198 -0
- cocotb_tools/makefiles/Makefile.sim +96 -0
- cocotb_tools/makefiles/simulators/Makefile.activehdl +72 -0
- cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
- cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
- cocotb_tools/makefiles/simulators/Makefile.ghdl +84 -0
- cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
- cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
- cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
- cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
- cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
- cocotb_tools/makefiles/simulators/Makefile.questa-compat +143 -0
- cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
- cocotb_tools/makefiles/simulators/Makefile.riviera +144 -0
- cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
- cocotb_tools/makefiles/simulators/Makefile.verilator +79 -0
- cocotb_tools/makefiles/simulators/Makefile.xcelium +104 -0
- cocotb_tools/py.typed +0 -0
- cocotb_tools/runner.py +1868 -0
- cocotb_tools/sim_versions.py +140 -0
- pygpi/__init__.py +0 -0
- pygpi/entry.py +42 -0
- pygpi/py.typed +0 -0
cocotb/clock.py
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
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
|
+
"""A clock class."""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import warnings
|
|
11
|
+
from decimal import Decimal
|
|
12
|
+
from fractions import Fraction
|
|
13
|
+
from logging import Logger
|
|
14
|
+
from typing import ClassVar, Type, Union
|
|
15
|
+
|
|
16
|
+
import cocotb
|
|
17
|
+
from cocotb._py_compat import (
|
|
18
|
+
Literal,
|
|
19
|
+
TypeAlias,
|
|
20
|
+
cached_property,
|
|
21
|
+
)
|
|
22
|
+
from cocotb._typing import TimeUnit
|
|
23
|
+
from cocotb.handle import (
|
|
24
|
+
Deposit,
|
|
25
|
+
Force,
|
|
26
|
+
Immediate,
|
|
27
|
+
LogicObject,
|
|
28
|
+
_GPISetAction,
|
|
29
|
+
_trust_inertial,
|
|
30
|
+
)
|
|
31
|
+
from cocotb.simulator import clock_create
|
|
32
|
+
from cocotb.task import Task
|
|
33
|
+
from cocotb.triggers import (
|
|
34
|
+
Event,
|
|
35
|
+
FallingEdge,
|
|
36
|
+
NullTrigger,
|
|
37
|
+
RisingEdge,
|
|
38
|
+
Timer,
|
|
39
|
+
ValueChange,
|
|
40
|
+
)
|
|
41
|
+
from cocotb.utils import get_sim_steps, get_time_from_sim_steps
|
|
42
|
+
|
|
43
|
+
__all__ = ("Clock",)
|
|
44
|
+
|
|
45
|
+
Impl: TypeAlias = Literal["gpi", "py"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_valid_impls = ("gpi", "py")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Clock:
|
|
52
|
+
r"""Simple 50:50 duty cycle clock driver.
|
|
53
|
+
|
|
54
|
+
.. code-block:: python
|
|
55
|
+
|
|
56
|
+
c = Clock(dut.clk, 10, "ns")
|
|
57
|
+
c.start()
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
signal: The clock pin/signal to be driven.
|
|
61
|
+
period:
|
|
62
|
+
The clock period.
|
|
63
|
+
Must be a multiple of the time precision of the simulator.
|
|
64
|
+
|
|
65
|
+
unit:
|
|
66
|
+
One of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``.
|
|
67
|
+
When *unit* is ``'step'``,
|
|
68
|
+
the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`).
|
|
69
|
+
|
|
70
|
+
.. versionchanged:: 2.0
|
|
71
|
+
Renamed from ``units``.
|
|
72
|
+
|
|
73
|
+
impl:
|
|
74
|
+
One of ``'auto'``, ``'gpi'``, ``'py'``.
|
|
75
|
+
Specify whether the clock is implemented with a :class:`~cocotb.simulator.GpiClock` (faster), or with a Python coroutine.
|
|
76
|
+
When ``'auto'`` is used (default), the fastest implementation that supports your environment and use case is picked.
|
|
77
|
+
|
|
78
|
+
.. versionadded:: 2.0
|
|
79
|
+
|
|
80
|
+
set_action:
|
|
81
|
+
One of :class:`.Immediate`, :class:`.Deposit`, or :class:`.Force`.
|
|
82
|
+
Specify the action to use when setting the clock signal value.
|
|
83
|
+
Defaults to the value of :attr:`default_set_action`.
|
|
84
|
+
|
|
85
|
+
.. versionadded:: 2.0
|
|
86
|
+
|
|
87
|
+
period_high:
|
|
88
|
+
The period of time when the clock is driven to ``1``.
|
|
89
|
+
Defaults to half of the *period*.
|
|
90
|
+
Must be a multiple of the time precision of the simulator and less than *period*.
|
|
91
|
+
|
|
92
|
+
.. versionadded:: 2.0
|
|
93
|
+
|
|
94
|
+
When *impl* is ``'auto'``, if :envvar:`COCOTB_TRUST_INERTIAL_WRITES` is defined,
|
|
95
|
+
the :class:`~cocotb.simulator.GpiClock` implementation will be used.
|
|
96
|
+
Otherwise, the Python coroutine implementation will be used.
|
|
97
|
+
See the environment variable's documentation for more information on the consequences
|
|
98
|
+
of using the simulator's inertial write mechanism.
|
|
99
|
+
|
|
100
|
+
If you need more features like a phase shift and an asymmetric duty cycle,
|
|
101
|
+
it is simple to create your own clock generator (that you then :func:`cocotb.start_soon`):
|
|
102
|
+
|
|
103
|
+
.. code-block:: python
|
|
104
|
+
|
|
105
|
+
async def custom_clock():
|
|
106
|
+
# pre-construct triggers for performance
|
|
107
|
+
high_time = Timer(high_delay, unit="ns")
|
|
108
|
+
low_time = Timer(low_delay, unit="ns")
|
|
109
|
+
await Timer(initial_delay, unit="ns")
|
|
110
|
+
while True:
|
|
111
|
+
dut.clk.value = 1
|
|
112
|
+
await high_time
|
|
113
|
+
dut.clk.value = 0
|
|
114
|
+
await low_time
|
|
115
|
+
|
|
116
|
+
If you also want to change the timing during simulation,
|
|
117
|
+
use this slightly more inefficient example instead where
|
|
118
|
+
the :class:`Timer`\ s inside the while loop are created with
|
|
119
|
+
current delay values:
|
|
120
|
+
|
|
121
|
+
.. code-block:: python
|
|
122
|
+
|
|
123
|
+
async def custom_clock():
|
|
124
|
+
while True:
|
|
125
|
+
dut.clk.value = 1
|
|
126
|
+
await Timer(high_delay, unit="ns")
|
|
127
|
+
dut.clk.value = 0
|
|
128
|
+
await Timer(low_delay, unit="ns")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
high_delay = low_delay = 100
|
|
132
|
+
cocotb.start_soon(custom_clock())
|
|
133
|
+
await Timer(1000, unit="ns")
|
|
134
|
+
high_delay = low_delay = 10 # change the clock speed
|
|
135
|
+
await Timer(1000, unit="ns")
|
|
136
|
+
|
|
137
|
+
.. versionadded:: 1.5
|
|
138
|
+
Support ``'step'`` as the *unit* argument to mean "simulator time step".
|
|
139
|
+
|
|
140
|
+
.. versionremoved:: 2.0
|
|
141
|
+
Passing ``None`` as the *unit* argument was removed, use ``'step'`` instead.
|
|
142
|
+
|
|
143
|
+
.. versionchanged:: 2.0
|
|
144
|
+
:meth:`start` now automatically calls :func:`cocotb.start_soon` and stores the Task
|
|
145
|
+
on the Clock object, so that it may later be :meth:`stop`\ ped.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
_impl: Impl
|
|
149
|
+
|
|
150
|
+
default_set_action: ClassVar[Union[Type[Immediate], Type[Deposit], Type[Force]]] = (
|
|
151
|
+
Deposit
|
|
152
|
+
)
|
|
153
|
+
"""The default action used to set the clock signal value.
|
|
154
|
+
One of :class:`.Immediate`, :class:`.Deposit`, or :class:`.Force`.
|
|
155
|
+
|
|
156
|
+
.. versionadded:: 2.0
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def __init__(
|
|
160
|
+
self,
|
|
161
|
+
signal: LogicObject,
|
|
162
|
+
period: Union[float, Fraction, Decimal],
|
|
163
|
+
unit: TimeUnit = "step",
|
|
164
|
+
impl: Union[Impl, None] = None,
|
|
165
|
+
*,
|
|
166
|
+
units: None = None,
|
|
167
|
+
set_action: Union[Type[Immediate], Type[Deposit], Type[Force], None] = None,
|
|
168
|
+
period_high: Union[float, Fraction, Decimal, None] = None,
|
|
169
|
+
) -> None:
|
|
170
|
+
self._signal = signal
|
|
171
|
+
|
|
172
|
+
if units is not None:
|
|
173
|
+
warnings.warn(
|
|
174
|
+
"The 'units' argument has been renamed to 'unit'.",
|
|
175
|
+
DeprecationWarning,
|
|
176
|
+
stacklevel=2,
|
|
177
|
+
)
|
|
178
|
+
unit = units
|
|
179
|
+
self._unit: TimeUnit = unit
|
|
180
|
+
|
|
181
|
+
self._period = period
|
|
182
|
+
try:
|
|
183
|
+
self._period_steps = get_sim_steps(self._period, self._unit)
|
|
184
|
+
except ValueError as e:
|
|
185
|
+
raise ValueError(f"Bad `period`: {e}") from None
|
|
186
|
+
|
|
187
|
+
if set_action is None:
|
|
188
|
+
set_action = type(self).default_set_action
|
|
189
|
+
if set_action not in (Immediate, Deposit, Force):
|
|
190
|
+
raise TypeError(
|
|
191
|
+
"Invalid value for `set_action`. `set_action` must be one of Immediate, Deposit, or Force"
|
|
192
|
+
)
|
|
193
|
+
self._set_action = set_action
|
|
194
|
+
|
|
195
|
+
if impl is None:
|
|
196
|
+
self._impl = "gpi" if _trust_inertial else "py"
|
|
197
|
+
elif impl in _valid_impls:
|
|
198
|
+
self._impl = impl
|
|
199
|
+
else:
|
|
200
|
+
valid_impls_str = ", ".join([repr(i) for i in _valid_impls])
|
|
201
|
+
raise ValueError(
|
|
202
|
+
f"Invalid clock impl {impl!r}, must be one of: {valid_impls_str}"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
self._period_high: Union[float, Fraction, Decimal]
|
|
206
|
+
if period_high is not None:
|
|
207
|
+
if period_high >= self._period:
|
|
208
|
+
raise ValueError("`period_high` must be strictly less than `period`.")
|
|
209
|
+
self._period_high = period_high
|
|
210
|
+
try:
|
|
211
|
+
self._period_high_steps = get_sim_steps(self._period_high, self._unit)
|
|
212
|
+
except ValueError as e:
|
|
213
|
+
raise ValueError(f"Bad `period_high`: {e}") from None
|
|
214
|
+
else:
|
|
215
|
+
if self._period_steps % 2 != 0:
|
|
216
|
+
raise ValueError(
|
|
217
|
+
"Bad `period`: Must be divisible by 2 if `period_high` is not given."
|
|
218
|
+
)
|
|
219
|
+
self._period_high = period / 2
|
|
220
|
+
self._period_high_steps = self._period_steps // 2
|
|
221
|
+
|
|
222
|
+
self._task: Union[Task[None], None] = None
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def signal(self) -> LogicObject:
|
|
226
|
+
"""The clock signal being driven."""
|
|
227
|
+
return self._signal
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def period(self) -> Union[float, Fraction, Decimal]:
|
|
231
|
+
"""The clock period.
|
|
232
|
+
|
|
233
|
+
The unit is :attr:`unit`.
|
|
234
|
+
"""
|
|
235
|
+
return self._period
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def period_high(self) -> Union[float, Fraction, Decimal]:
|
|
239
|
+
"""The period of time when the clock is driven to ``1``.
|
|
240
|
+
|
|
241
|
+
The unit is :attr:`unit`.
|
|
242
|
+
|
|
243
|
+
.. versionadded:: 2.0
|
|
244
|
+
"""
|
|
245
|
+
return self._period_high
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def unit(self) -> TimeUnit:
|
|
249
|
+
"""The unit of the clock period.
|
|
250
|
+
|
|
251
|
+
.. versionadded:: 2.0
|
|
252
|
+
"""
|
|
253
|
+
return self._unit
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def impl(self) -> Impl:
|
|
257
|
+
"""The concrete implementation of the clock used.
|
|
258
|
+
|
|
259
|
+
``"gpi"`` if the clock is implemented in C in the GPI layer,
|
|
260
|
+
or ``"py"`` if the clock is implemented in Python using cocotb Tasks.
|
|
261
|
+
|
|
262
|
+
.. versionadded:: 2.0
|
|
263
|
+
"""
|
|
264
|
+
return self._impl
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def set_action(self) -> Union[Type[Immediate], Type[Deposit], Type[Force]]:
|
|
268
|
+
"""The value setting action used to set the clock signal value.
|
|
269
|
+
|
|
270
|
+
.. versionadded:: 2.0
|
|
271
|
+
"""
|
|
272
|
+
return self._set_action
|
|
273
|
+
|
|
274
|
+
def start(self, start_high: bool = True) -> Task[None]:
|
|
275
|
+
r"""Start driving the clock signal.
|
|
276
|
+
|
|
277
|
+
You can later stop the clock by calling :meth:`stop`.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
start_high: Whether to start the clock with a ``1``
|
|
281
|
+
for the first half of the period.
|
|
282
|
+
Default is ``True``.
|
|
283
|
+
|
|
284
|
+
.. versionadded:: 1.3
|
|
285
|
+
|
|
286
|
+
Raises:
|
|
287
|
+
RuntimeError: If attempting to start a clock that has already been started.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Object which can be passed to :func:`cocotb.start_soon` or ignored.
|
|
291
|
+
|
|
292
|
+
.. versionremoved:: 2.0
|
|
293
|
+
Removed ``cycles`` arguments for toggling for a finite amount of cycles.
|
|
294
|
+
Use :meth:`stop` to stop a clock from running.
|
|
295
|
+
|
|
296
|
+
.. versionchanged:: 2.0
|
|
297
|
+
Previously, this method returned a :term:`coroutine` which needed to be passed to :func:`cocotb.start_soon`.
|
|
298
|
+
Now the Clock object keeps track of its own driver Task, so this is no longer necessary.
|
|
299
|
+
Simply call ``clock.start()`` to start running the clock.
|
|
300
|
+
"""
|
|
301
|
+
if self._task is not None:
|
|
302
|
+
raise RuntimeError("Starting clock that has already been started.")
|
|
303
|
+
|
|
304
|
+
if self._impl == "gpi":
|
|
305
|
+
clkobj = clock_create(self._signal._handle)
|
|
306
|
+
set_action = {
|
|
307
|
+
Deposit: _GPISetAction.DEPOSIT,
|
|
308
|
+
Immediate: _GPISetAction.NO_DELAY,
|
|
309
|
+
Force: _GPISetAction.FORCE,
|
|
310
|
+
}[self._set_action]
|
|
311
|
+
clkobj.start(
|
|
312
|
+
self._period_steps,
|
|
313
|
+
self._period_high_steps,
|
|
314
|
+
start_high,
|
|
315
|
+
set_action.value,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
async def drive() -> None:
|
|
319
|
+
# The clock is meant to toggle forever, so awaiting this should
|
|
320
|
+
# never return by awaiting on Event that's never set.
|
|
321
|
+
e = Event()
|
|
322
|
+
try:
|
|
323
|
+
await e.wait()
|
|
324
|
+
finally:
|
|
325
|
+
clkobj.stop()
|
|
326
|
+
|
|
327
|
+
else:
|
|
328
|
+
|
|
329
|
+
async def drive() -> None:
|
|
330
|
+
timer_high = Timer(self._period_high_steps)
|
|
331
|
+
timer_low = Timer(self._period_steps - self._period_high_steps)
|
|
332
|
+
if start_high:
|
|
333
|
+
self._signal.set(self._set_action(1))
|
|
334
|
+
await timer_high
|
|
335
|
+
while True:
|
|
336
|
+
self._signal.set(self._set_action(0))
|
|
337
|
+
await timer_low
|
|
338
|
+
self._signal.set(self._set_action(1))
|
|
339
|
+
await timer_high
|
|
340
|
+
|
|
341
|
+
self._task = cocotb.start_soon(drive())
|
|
342
|
+
return self._task
|
|
343
|
+
|
|
344
|
+
def stop(self) -> None:
|
|
345
|
+
"""Stop driving the clock signal.
|
|
346
|
+
|
|
347
|
+
You can later start the clock again by calling :meth:`start`.
|
|
348
|
+
|
|
349
|
+
Raises:
|
|
350
|
+
RuntimeError: If attempting to stop a clock that has never been started.
|
|
351
|
+
|
|
352
|
+
.. versionadded:: 2.0
|
|
353
|
+
"""
|
|
354
|
+
if self._task is None:
|
|
355
|
+
raise RuntimeError("Stopping a clock that was never started.")
|
|
356
|
+
self._task.cancel()
|
|
357
|
+
self._task = None
|
|
358
|
+
|
|
359
|
+
async def cycles(
|
|
360
|
+
self,
|
|
361
|
+
num_cycles: int,
|
|
362
|
+
edge_type: Union[
|
|
363
|
+
Type[RisingEdge], Type[FallingEdge], Type[ValueChange]
|
|
364
|
+
] = RisingEdge,
|
|
365
|
+
) -> None:
|
|
366
|
+
"""Wait for a number of clock cycles.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
num_cycles: The number of clock cycles to wait.
|
|
370
|
+
edge_type:
|
|
371
|
+
The edge of the clock to wait on.
|
|
372
|
+
Must be one of :class:`.RisingEdge`, :class:`.FallingEdge`, or :class:`.ValueChange`.
|
|
373
|
+
|
|
374
|
+
Raises:
|
|
375
|
+
ValueError: if *num_cycles* is negative.
|
|
376
|
+
"""
|
|
377
|
+
if num_cycles == 0:
|
|
378
|
+
await NullTrigger()
|
|
379
|
+
return
|
|
380
|
+
elif num_cycles < 0:
|
|
381
|
+
raise ValueError("`num_cycles` cannot be negative")
|
|
382
|
+
|
|
383
|
+
# Synchronize first.
|
|
384
|
+
await edge_type(self._signal)
|
|
385
|
+
num_cycles -= 1
|
|
386
|
+
|
|
387
|
+
# Use Timer as an optimization if we are waiting long enough.
|
|
388
|
+
if num_cycles >= 2:
|
|
389
|
+
# NOTE: num_cycles must end 1 higher than expected because all edge_types occur
|
|
390
|
+
# strictly after beginning of time steps, so the last edge_type will jump within
|
|
391
|
+
# the same time step.
|
|
392
|
+
if edge_type is ValueChange:
|
|
393
|
+
# Make cycles_skipped the nearest even number so division by 2 doesn't cause issues.
|
|
394
|
+
cycles_skipped = (num_cycles // 2) * 2
|
|
395
|
+
await Timer(self._period_steps * cycles_skipped / 2, "step")
|
|
396
|
+
num_cycles = num_cycles - cycles_skipped + 1
|
|
397
|
+
else:
|
|
398
|
+
await Timer(self._period_steps * num_cycles, "step")
|
|
399
|
+
num_cycles = 1
|
|
400
|
+
|
|
401
|
+
# Run N edge_type trigger.
|
|
402
|
+
for _ in range(num_cycles):
|
|
403
|
+
await edge_type(self._signal)
|
|
404
|
+
|
|
405
|
+
def __repr__(self) -> str:
|
|
406
|
+
return self._repr
|
|
407
|
+
|
|
408
|
+
@cached_property
|
|
409
|
+
def _repr(self) -> str:
|
|
410
|
+
freq_mhz = 1 / get_time_from_sim_steps(
|
|
411
|
+
get_sim_steps(self._period, self._unit), "us"
|
|
412
|
+
)
|
|
413
|
+
return f"<{type(self).__qualname__}, {self._signal._path} @ {freq_mhz} MHz>"
|
|
414
|
+
|
|
415
|
+
@cached_property
|
|
416
|
+
def _log(self) -> Logger:
|
|
417
|
+
return logging.getLogger(
|
|
418
|
+
f"cocotb.{type(self).__qualname__}.{self._signal._name}"
|
|
419
|
+
)
|
cocotb/debug.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
debug: bool = bool(os.getenv("COCOTB_SCHEDULER_DEBUG"))
|
|
8
|
+
"""Global flag to enable additional debugging functionality.
|
|
9
|
+
|
|
10
|
+
Defaults to ``True`` if the :envvar:`COCOTB_SCHEDULER_DEBUG` environment variable is set,
|
|
11
|
+
but can be programmatically set by the user afterwards.
|
|
12
|
+
|
|
13
|
+
The ``"cocotb"`` logger should have its logging level set to :data:`logging.DEBUG`
|
|
14
|
+
to see additional debugging information in the test log.
|
|
15
|
+
This can be accomplished by setting the :envvar:`COCOTB_LOG_LEVEL` environment variable
|
|
16
|
+
to ``DEBUG``,
|
|
17
|
+
or using the following code.
|
|
18
|
+
|
|
19
|
+
.. code-block:: python
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
logging.getLogger("cocotb").setLevel(logging.DEBUG)
|
|
23
|
+
|
|
24
|
+
"""
|