cocotb 2.0.0b1__cp313-cp313-win32.whl → 2.0.0rc2__cp313-cp313-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cocotb might be problematic. Click here for more details.
- cocotb/_ANSI.py +47 -54
- cocotb/__init__.py +12 -2
- cocotb/_base_triggers.py +11 -9
- cocotb/_bridge.py +8 -9
- cocotb/_extended_awaitables.py +1 -1
- cocotb/_gpi_triggers.py +4 -1
- cocotb/_init.py +17 -11
- cocotb/_py_compat.py +24 -10
- cocotb/_scheduler.py +25 -31
- cocotb/_test.py +6 -3
- cocotb/_test_factory.py +4 -1
- cocotb/_utils.py +1 -23
- cocotb/_version.py +1 -1
- cocotb/clock.py +98 -16
- cocotb/debug.py +24 -0
- cocotb/handle.py +62 -32
- cocotb/libs/cocotb.dll +0 -0
- cocotb/libs/cocotb.exp +0 -0
- cocotb/libs/cocotb.lib +0 -0
- cocotb/libs/cocotbfli_modelsim.dll +0 -0
- cocotb/libs/cocotbfli_modelsim.exp +0 -0
- cocotb/libs/cocotbfli_modelsim.lib +0 -0
- cocotb/libs/cocotbutils.dll +0 -0
- cocotb/libs/cocotbutils.exp +0 -0
- cocotb/libs/cocotbutils.lib +0 -0
- cocotb/libs/cocotbvhpi_aldec.dll +0 -0
- cocotb/libs/cocotbvhpi_aldec.exp +0 -0
- cocotb/libs/cocotbvhpi_aldec.lib +0 -0
- cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
- cocotb/libs/cocotbvpi_aldec.dll +0 -0
- cocotb/libs/cocotbvpi_aldec.exp +0 -0
- cocotb/libs/cocotbvpi_aldec.lib +0 -0
- cocotb/libs/cocotbvpi_ghdl.dll +0 -0
- cocotb/libs/cocotbvpi_ghdl.exp +0 -0
- cocotb/libs/cocotbvpi_ghdl.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.exp +0 -0
- cocotb/libs/cocotbvpi_icarus.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.vpl +0 -0
- cocotb/libs/cocotbvpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvpi_modelsim.lib +0 -0
- cocotb/libs/embed.dll +0 -0
- cocotb/libs/embed.exp +0 -0
- cocotb/libs/embed.lib +0 -0
- cocotb/libs/gpi.dll +0 -0
- cocotb/libs/gpi.exp +0 -0
- cocotb/libs/gpi.lib +0 -0
- cocotb/libs/gpilog.dll +0 -0
- cocotb/libs/gpilog.exp +0 -0
- cocotb/libs/gpilog.lib +0 -0
- cocotb/libs/pygpilog.dll +0 -0
- cocotb/libs/pygpilog.exp +0 -0
- cocotb/libs/pygpilog.lib +0 -0
- cocotb/logging.py +243 -117
- cocotb/regression.py +43 -35
- cocotb/share/def/aldec.exp +0 -0
- cocotb/share/def/aldec.lib +0 -0
- cocotb/share/def/ghdl.exp +0 -0
- cocotb/share/def/ghdl.lib +0 -0
- cocotb/share/def/icarus.exp +0 -0
- cocotb/share/def/icarus.lib +0 -0
- cocotb/share/def/modelsim.exp +0 -0
- cocotb/share/def/modelsim.lib +0 -0
- cocotb/share/include/cocotb_utils.h +3 -3
- cocotb/share/include/embed.h +2 -2
- cocotb/share/include/gpi.h +258 -109
- cocotb/share/include/py_gpi_logging.h +3 -3
- cocotb/share/lib/verilator/verilator.cpp +23 -15
- cocotb/simtime.py +230 -0
- cocotb/simulator.cp313-win32.exp +0 -0
- cocotb/simulator.cp313-win32.lib +0 -0
- cocotb/simulator.cp313-win32.pyd +0 -0
- cocotb/task.py +54 -11
- cocotb/types/__init__.py +4 -1
- cocotb/types/_array.py +33 -2
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +96 -59
- cocotb/types/_logic_array.py +56 -22
- cocotb/types/_range.py +12 -5
- cocotb/utils.py +9 -129
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/METADATA +1 -1
- cocotb-2.0.0rc2.dist-info/RECORD +146 -0
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/licenses/LICENSE +1 -0
- cocotb_tools/config.py +5 -5
- cocotb_tools/makefiles/Makefile.inc +3 -9
- cocotb_tools/makefiles/Makefile.sim +9 -1
- cocotb_tools/makefiles/simulators/Makefile.vcs +1 -1
- cocotb_tools/runner.py +94 -59
- pygpi/entry.py +5 -6
- cocotb-2.0.0b1.dist-info/RECORD +0 -143
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +0 -0
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/entry_points.txt +0 -0
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +0 -0
cocotb/simtime.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
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
|
+
"""Tools for dealing with simulated time."""
|
|
8
|
+
|
|
9
|
+
import warnings
|
|
10
|
+
from decimal import Decimal
|
|
11
|
+
from fractions import Fraction
|
|
12
|
+
from functools import lru_cache
|
|
13
|
+
from math import ceil, floor
|
|
14
|
+
from typing import Union, cast, overload
|
|
15
|
+
|
|
16
|
+
from cocotb import simulator
|
|
17
|
+
from cocotb._py_compat import Literal, TypeAlias
|
|
18
|
+
from cocotb._typing import RoundMode, TimeUnit
|
|
19
|
+
|
|
20
|
+
__all__ = (
|
|
21
|
+
"convert",
|
|
22
|
+
"get_sim_time",
|
|
23
|
+
"time_precision",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Steps: TypeAlias = Literal["step"]
|
|
28
|
+
TimeUnitWithoutSteps: TypeAlias = Literal["fs", "ps", "ns", "us", "ms", "sec"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@overload
|
|
32
|
+
def convert(
|
|
33
|
+
value: Union[float, Fraction, Decimal],
|
|
34
|
+
unit: TimeUnit,
|
|
35
|
+
*,
|
|
36
|
+
to: Steps,
|
|
37
|
+
round_mode: RoundMode = "error",
|
|
38
|
+
) -> int: ...
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@overload
|
|
42
|
+
def convert(
|
|
43
|
+
value: Union[float, Fraction, Decimal],
|
|
44
|
+
unit: TimeUnit,
|
|
45
|
+
*,
|
|
46
|
+
to: TimeUnitWithoutSteps,
|
|
47
|
+
round_mode: RoundMode = "error",
|
|
48
|
+
) -> float: ...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def convert(
|
|
52
|
+
value: Union[float, Decimal, Fraction],
|
|
53
|
+
unit: TimeUnit,
|
|
54
|
+
*,
|
|
55
|
+
to: TimeUnit,
|
|
56
|
+
round_mode: RoundMode = "error",
|
|
57
|
+
) -> float:
|
|
58
|
+
"""Convert time values from one unit to another unit.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
value: The time value.
|
|
62
|
+
|
|
63
|
+
unit: The unit of *value* (one of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``).
|
|
64
|
+
|
|
65
|
+
to: The unit to convert *value* to (one of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``).
|
|
66
|
+
|
|
67
|
+
round_mode:
|
|
68
|
+
How to handle non-integral step values (one of ``'error'``, ``'round'``, ``'ceil'``, ``'floor'``).
|
|
69
|
+
|
|
70
|
+
When *round_mode* is ``"error"``, a :exc:`ValueError` is thrown if the value cannot
|
|
71
|
+
be accurately represented in terms of simulator time steps.
|
|
72
|
+
When *round_mode* is ``"round"``, ``"ceil"``, or ``"floor"``, the corresponding
|
|
73
|
+
rounding function from the standard library will be used to round to a simulator
|
|
74
|
+
time step.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The value scaled by the difference in units.
|
|
78
|
+
|
|
79
|
+
.. versionadded:: 2.0
|
|
80
|
+
"""
|
|
81
|
+
if unit == "step":
|
|
82
|
+
steps = cast("int", value)
|
|
83
|
+
else:
|
|
84
|
+
steps = _get_sim_steps(value, unit, round_mode=round_mode)
|
|
85
|
+
if to == "step":
|
|
86
|
+
return steps
|
|
87
|
+
else:
|
|
88
|
+
return _get_time_from_sim_steps(steps, to)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_sim_time(unit: TimeUnit = "step", *, units: None = None) -> float:
|
|
92
|
+
"""Retrieve the simulation time from the simulator.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
unit: String specifying the unit of the result
|
|
96
|
+
(one of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``).
|
|
97
|
+
``'step'`` will return the raw simulation time.
|
|
98
|
+
|
|
99
|
+
.. versionchanged:: 2.0
|
|
100
|
+
Passing ``None`` as the *unit* argument was removed, use ``'step'`` instead.
|
|
101
|
+
|
|
102
|
+
.. versionchanged:: 2.0
|
|
103
|
+
Renamed from ``units``.
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
ValueError: If *unit* is not a valid unit.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
The simulation time in the specified unit.
|
|
110
|
+
|
|
111
|
+
.. versionchanged:: 1.6
|
|
112
|
+
Support ``'step'`` as the the *unit* argument to mean "simulator time step".
|
|
113
|
+
|
|
114
|
+
.. versionchanged:: 2.0
|
|
115
|
+
Moved from :mod:`cocotb.utils` to :mod:`cocotb.simtime`.
|
|
116
|
+
"""
|
|
117
|
+
if units is not None:
|
|
118
|
+
warnings.warn(
|
|
119
|
+
"The 'units' argument has been renamed to 'unit'.",
|
|
120
|
+
DeprecationWarning,
|
|
121
|
+
stacklevel=2,
|
|
122
|
+
)
|
|
123
|
+
unit = units
|
|
124
|
+
timeh, timel = simulator.get_sim_time()
|
|
125
|
+
steps = timeh << 32 | timel
|
|
126
|
+
return _get_time_from_sim_steps(steps, unit) if unit != "step" else steps
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@overload
|
|
130
|
+
def _ldexp10(frac: float, exp: int) -> float: ...
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@overload
|
|
134
|
+
def _ldexp10(frac: Fraction, exp: int) -> Fraction: ...
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@overload
|
|
138
|
+
def _ldexp10(frac: Decimal, exp: int) -> Decimal: ...
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _ldexp10(
|
|
142
|
+
frac: Union[float, Fraction, Decimal], exp: int
|
|
143
|
+
) -> Union[float, Fraction, Decimal]:
|
|
144
|
+
"""Like :func:`math.ldexp`, but base 10."""
|
|
145
|
+
# using * or / separately prevents rounding errors if `frac` is a
|
|
146
|
+
# high-precision type
|
|
147
|
+
if exp > 0:
|
|
148
|
+
return frac * (10**exp)
|
|
149
|
+
else:
|
|
150
|
+
return frac / (10**-exp)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _get_time_from_sim_steps(
|
|
154
|
+
steps: int,
|
|
155
|
+
unit: TimeUnit,
|
|
156
|
+
) -> float:
|
|
157
|
+
if unit == "step":
|
|
158
|
+
return steps
|
|
159
|
+
return _ldexp10(steps, time_precision - _get_log_time_scale(unit))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _get_sim_steps(
|
|
163
|
+
time: Union[float, Fraction, Decimal],
|
|
164
|
+
unit: TimeUnit = "step",
|
|
165
|
+
*,
|
|
166
|
+
round_mode: RoundMode = "error",
|
|
167
|
+
) -> int:
|
|
168
|
+
result: Union[float, Fraction, Decimal]
|
|
169
|
+
if unit != "step":
|
|
170
|
+
result = _ldexp10(time, _get_log_time_scale(unit) - time_precision)
|
|
171
|
+
else:
|
|
172
|
+
result = time
|
|
173
|
+
|
|
174
|
+
if round_mode == "error":
|
|
175
|
+
result_rounded = floor(result)
|
|
176
|
+
if result_rounded != result:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Unable to accurately represent {time}({unit}) with the simulator precision of 1e{time_precision}"
|
|
179
|
+
)
|
|
180
|
+
elif round_mode == "ceil":
|
|
181
|
+
result_rounded = ceil(result)
|
|
182
|
+
elif round_mode == "round":
|
|
183
|
+
result_rounded = round(result)
|
|
184
|
+
elif round_mode == "floor":
|
|
185
|
+
result_rounded = floor(result)
|
|
186
|
+
else:
|
|
187
|
+
raise ValueError(f"Invalid round_mode specifier: {round_mode}")
|
|
188
|
+
|
|
189
|
+
return result_rounded
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@lru_cache(maxsize=None)
|
|
193
|
+
def _get_log_time_scale(unit: TimeUnitWithoutSteps) -> int:
|
|
194
|
+
"""Retrieve the ``log10()`` of the scale factor for a given time unit.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
unit: String specifying the unit
|
|
198
|
+
(one of ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``).
|
|
199
|
+
|
|
200
|
+
.. versionchanged:: 2.0
|
|
201
|
+
Renamed from ``units``.
|
|
202
|
+
|
|
203
|
+
Raises:
|
|
204
|
+
ValueError: If *unit* is not a valid unit.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
The ``log10()`` of the scale factor for the time unit.
|
|
208
|
+
"""
|
|
209
|
+
scale = {"fs": -15, "ps": -12, "ns": -9, "us": -6, "ms": -3, "sec": 0}
|
|
210
|
+
|
|
211
|
+
unit_lwr = unit.lower()
|
|
212
|
+
if unit_lwr not in scale:
|
|
213
|
+
raise ValueError(f"Invalid unit ({unit}) provided")
|
|
214
|
+
else:
|
|
215
|
+
return scale[unit_lwr]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
time_precision: int = _get_log_time_scale("fs")
|
|
219
|
+
"""The precision of time in the current simulation.
|
|
220
|
+
|
|
221
|
+
The value is seconds in powers of tens,
|
|
222
|
+
i.e. ``-15`` is ``10**-15`` seconds or 1 femtosecond.
|
|
223
|
+
|
|
224
|
+
.. versionadded:: 2.0
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _init() -> None:
|
|
229
|
+
global time_precision
|
|
230
|
+
time_precision = simulator.get_precision()
|
cocotb/simulator.cp313-win32.exp
CHANGED
|
Binary file
|
cocotb/simulator.cp313-win32.lib
CHANGED
|
Binary file
|
cocotb/simulator.cp313-win32.pyd
CHANGED
|
Binary file
|
cocotb/task.py
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
import collections.abc
|
|
5
5
|
import inspect
|
|
6
6
|
import logging
|
|
7
|
-
import os
|
|
8
7
|
import traceback
|
|
9
8
|
from asyncio import CancelledError, InvalidStateError
|
|
10
9
|
from bdb import BdbQuit
|
|
11
10
|
from enum import auto
|
|
12
|
-
from types import
|
|
11
|
+
from types import SimpleNamespace
|
|
13
12
|
from typing import (
|
|
13
|
+
TYPE_CHECKING,
|
|
14
14
|
Callable,
|
|
15
15
|
Coroutine,
|
|
16
16
|
Generator,
|
|
@@ -30,12 +30,8 @@ from cocotb._outcomes import Error, Outcome, Value
|
|
|
30
30
|
from cocotb._py_compat import Self, cached_property
|
|
31
31
|
from cocotb._utils import DocEnum, extract_coro_stack, remove_traceback_frames
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# Sadly the Python standard logging module is very slow so it's better not to
|
|
37
|
-
# make any calls by testing a boolean flag first
|
|
38
|
-
_debug = "COCOTB_SCHEDULER_DEBUG" in os.environ
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from types import CoroutineType
|
|
39
35
|
|
|
40
36
|
|
|
41
37
|
__all__ = (
|
|
@@ -51,6 +47,9 @@ __all__ = (
|
|
|
51
47
|
bridge.__module__ = __name__
|
|
52
48
|
resume.__module__ = __name__
|
|
53
49
|
|
|
50
|
+
#: Task result type
|
|
51
|
+
ResultType = TypeVar("ResultType")
|
|
52
|
+
|
|
54
53
|
|
|
55
54
|
class _TaskState(DocEnum):
|
|
56
55
|
"""State of a Task."""
|
|
@@ -83,6 +82,7 @@ class Task(Generic[ResultType]):
|
|
|
83
82
|
def __init__(
|
|
84
83
|
self, inst: Coroutine[Trigger, None, ResultType], *, name: Optional[str] = None
|
|
85
84
|
) -> None:
|
|
85
|
+
self._native_coroutine: bool
|
|
86
86
|
if inspect.iscoroutinefunction(inst):
|
|
87
87
|
raise TypeError(
|
|
88
88
|
f"Coroutine function {inst} should be called prior to being scheduled."
|
|
@@ -92,7 +92,11 @@ class Task(Generic[ResultType]):
|
|
|
92
92
|
f"{inst.__qualname__} is an async generator, not a coroutine. "
|
|
93
93
|
"You likely used the yield keyword instead of await."
|
|
94
94
|
)
|
|
95
|
-
elif
|
|
95
|
+
elif inspect.iscoroutine(inst):
|
|
96
|
+
self._native_coroutine = True
|
|
97
|
+
elif isinstance(inst, collections.abc.Coroutine):
|
|
98
|
+
self._native_coroutine = False
|
|
99
|
+
else:
|
|
96
100
|
raise TypeError(f"{inst} isn't a valid coroutine!")
|
|
97
101
|
|
|
98
102
|
self._coro = inst
|
|
@@ -102,11 +106,40 @@ class Task(Generic[ResultType]):
|
|
|
102
106
|
self._done_callbacks: List[Callable[[Task[ResultType]], None]] = []
|
|
103
107
|
self._cancelled_msg: Union[str, None] = None
|
|
104
108
|
self._must_cancel: bool = False
|
|
109
|
+
self._locals = SimpleNamespace()
|
|
105
110
|
|
|
106
111
|
self._task_id = self._id_count
|
|
107
112
|
type(self)._id_count += 1
|
|
108
113
|
self._name = f"Task {self._task_id}" if name is None else name
|
|
109
114
|
|
|
115
|
+
@property
|
|
116
|
+
def locals(self) -> SimpleNamespace:
|
|
117
|
+
'''Task-local variables.
|
|
118
|
+
|
|
119
|
+
A modifiable namespace where any user-defined variables can be added.
|
|
120
|
+
Use :func:`~cocotb.task.current_task` to access the current Task's locals.
|
|
121
|
+
|
|
122
|
+
.. code-block:: python
|
|
123
|
+
|
|
124
|
+
def get_rng() -> random.Random:
|
|
125
|
+
"""Get the random number generator for the current Task."""
|
|
126
|
+
try:
|
|
127
|
+
return current_task().locals.rng
|
|
128
|
+
except AttributeError:
|
|
129
|
+
rng = random.Random()
|
|
130
|
+
current_task().locals.rng = rng
|
|
131
|
+
return rng
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Use Task-local RNG to drive a signal
|
|
135
|
+
rng = get_rng()
|
|
136
|
+
for _ in range(10):
|
|
137
|
+
await drive(rng.randint(0, 100))
|
|
138
|
+
|
|
139
|
+
.. versionadded:: 2.0
|
|
140
|
+
'''
|
|
141
|
+
return self._locals
|
|
142
|
+
|
|
110
143
|
def get_name(self) -> str:
|
|
111
144
|
"""Return the name of the :class:`!Task`.
|
|
112
145
|
|
|
@@ -132,7 +165,12 @@ class Task(Generic[ResultType]):
|
|
|
132
165
|
|
|
133
166
|
@cached_property
|
|
134
167
|
def _log(self) -> logging.Logger:
|
|
135
|
-
|
|
168
|
+
coro_name: str
|
|
169
|
+
if self._native_coroutine:
|
|
170
|
+
coro_name = self._coro.__qualname__
|
|
171
|
+
else:
|
|
172
|
+
coro_name = type(self._coro).__qualname__
|
|
173
|
+
return logging.getLogger(f"cocotb.{self._name}.{coro_name}")
|
|
136
174
|
|
|
137
175
|
def __str__(self) -> str:
|
|
138
176
|
# TODO Do we really need this?
|
|
@@ -146,6 +184,11 @@ class Task(Generic[ResultType]):
|
|
|
146
184
|
Raises:
|
|
147
185
|
TypeError: If :attr:`_coro` is not a native Python coroutine object.
|
|
148
186
|
"""
|
|
187
|
+
if not self._native_coroutine:
|
|
188
|
+
raise TypeError(
|
|
189
|
+
"Task._get_coro_stack() can only be called on native Python coroutines."
|
|
190
|
+
)
|
|
191
|
+
|
|
149
192
|
coro_stack = extract_coro_stack(
|
|
150
193
|
cast("CoroutineType[Trigger, None, ResultType]", self._coro)
|
|
151
194
|
)
|
|
@@ -157,7 +200,7 @@ class Task(Generic[ResultType]):
|
|
|
157
200
|
return coro_stack
|
|
158
201
|
|
|
159
202
|
def __repr__(self) -> str:
|
|
160
|
-
if
|
|
203
|
+
if self._native_coroutine:
|
|
161
204
|
coro_stack = self._get_coro_stack()
|
|
162
205
|
try:
|
|
163
206
|
coro_name = coro_stack[-1].name
|
cocotb/types/__init__.py
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
4
|
from ._abstract_array import AbstractArray, AbstractMutableArray
|
|
5
5
|
from ._array import Array
|
|
6
|
-
from .
|
|
6
|
+
from ._indexing import IndexingChangedWarning
|
|
7
|
+
from ._logic import Bit, Logic
|
|
7
8
|
from ._logic_array import LogicArray
|
|
8
9
|
from ._range import Range
|
|
9
10
|
|
|
@@ -17,6 +18,8 @@ __all__ = (
|
|
|
17
18
|
"AbstractArray",
|
|
18
19
|
"AbstractMutableArray",
|
|
19
20
|
"Array",
|
|
21
|
+
"Bit",
|
|
22
|
+
"IndexingChangedWarning",
|
|
20
23
|
"Logic",
|
|
21
24
|
"LogicArray",
|
|
22
25
|
"Range",
|
cocotb/types/_array.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# Copyright cocotb contributors
|
|
2
2
|
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
3
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
-
|
|
4
|
+
import copy
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any, Dict, Iterable, Iterator, List, TypeVar, Union, cast, overload
|
|
5
7
|
|
|
6
8
|
from cocotb.types._abstract_array import AbstractMutableArray
|
|
9
|
+
from cocotb.types._indexing import IndexingChangedWarning
|
|
7
10
|
from cocotb.types._range import Range
|
|
8
11
|
|
|
9
12
|
T = TypeVar("T")
|
|
@@ -131,9 +134,12 @@ class Array(AbstractMutableArray[T]):
|
|
|
131
134
|
TypeError: When invalid argument types are used.
|
|
132
135
|
"""
|
|
133
136
|
|
|
137
|
+
__slots__ = ("_value", "_range", "_warn_indexing")
|
|
138
|
+
|
|
134
139
|
def __init__(
|
|
135
140
|
self, value: Iterable[T], range: Union[Range, int, None] = None
|
|
136
141
|
) -> None:
|
|
142
|
+
self._warn_indexing = False
|
|
137
143
|
self._value = list(value)
|
|
138
144
|
if range is None:
|
|
139
145
|
self._range = Range(0, "to", len(self._value) - 1)
|
|
@@ -153,8 +159,11 @@ class Array(AbstractMutableArray[T]):
|
|
|
153
159
|
)
|
|
154
160
|
|
|
155
161
|
@classmethod
|
|
156
|
-
def _from_handle(
|
|
162
|
+
def _from_handle(
|
|
163
|
+
cls, value: List[T], range: Range, warn_indexing: bool
|
|
164
|
+
) -> "Array[T]":
|
|
157
165
|
self = cls.__new__(cls)
|
|
166
|
+
self._warn_indexing = warn_indexing
|
|
158
167
|
self._value = value
|
|
159
168
|
self._range = range
|
|
160
169
|
return self
|
|
@@ -202,9 +211,23 @@ class Array(AbstractMutableArray[T]):
|
|
|
202
211
|
|
|
203
212
|
def __getitem__(self, item: Union[int, slice]) -> Union[T, "Array[T]"]:
|
|
204
213
|
if isinstance(item, int):
|
|
214
|
+
if self._warn_indexing:
|
|
215
|
+
warnings.warn(
|
|
216
|
+
f"Update index {item} to {self.range[item]}",
|
|
217
|
+
IndexingChangedWarning,
|
|
218
|
+
stacklevel=2,
|
|
219
|
+
)
|
|
205
220
|
idx = self._translate_index(item)
|
|
206
221
|
return self._value[idx]
|
|
207
222
|
elif isinstance(item, slice):
|
|
223
|
+
if self._warn_indexing:
|
|
224
|
+
start = item.start if item.start is not None else 0
|
|
225
|
+
stop = item.stop if item.stop is not None else len(self) - 1
|
|
226
|
+
warnings.warn(
|
|
227
|
+
f"Update slice {start}:{stop} to {self.range[start]}:{self.range[stop]}",
|
|
228
|
+
IndexingChangedWarning,
|
|
229
|
+
stacklevel=2,
|
|
230
|
+
)
|
|
208
231
|
start = item.start if item.start is not None else self.left
|
|
209
232
|
stop = item.stop if item.stop is not None else self.right
|
|
210
233
|
if item.step is not None:
|
|
@@ -262,3 +285,11 @@ class Array(AbstractMutableArray[T]):
|
|
|
262
285
|
return self._range.index(item)
|
|
263
286
|
except ValueError:
|
|
264
287
|
raise IndexError(f"index {item} out of range") from None
|
|
288
|
+
|
|
289
|
+
def __copy__(self) -> "Array":
|
|
290
|
+
return Array(self._value, self._range)
|
|
291
|
+
|
|
292
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> "Array":
|
|
293
|
+
return Array(
|
|
294
|
+
copy.deepcopy(self._value, memo=memo), copy.deepcopy(self._range, memo=memo)
|
|
295
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
from cocotb.types._range import Range
|
|
8
|
+
|
|
9
|
+
do_indexing_changed_warning = os.environ.get("COCOTB_INDEXING_CHANGED_WARNING")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def indexing_changed(range: Range) -> bool:
|
|
13
|
+
return not (range.left == 0 and range.direction == "to")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class IndexingChangedWarning(UserWarning):
|
|
17
|
+
"""Warning issued when a value is indexed in a way that is different between cocotb 1.x and 2.x."""
|