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/types/_logic.py
CHANGED
|
@@ -4,13 +4,10 @@
|
|
|
4
4
|
from functools import lru_cache
|
|
5
5
|
from typing import (
|
|
6
6
|
Dict,
|
|
7
|
-
Set,
|
|
8
|
-
Type,
|
|
9
7
|
Union,
|
|
10
8
|
)
|
|
11
9
|
|
|
12
|
-
from cocotb.
|
|
13
|
-
from cocotb._py_compat import TypeAlias
|
|
10
|
+
from cocotb._py_compat import Self, TypeAlias
|
|
14
11
|
from cocotb.types._resolve import RESOLVE_X, ResolverLiteral, get_str_resolver
|
|
15
12
|
|
|
16
13
|
LogicLiteralT: TypeAlias = Union[str, int, bool]
|
|
@@ -56,21 +53,16 @@ _literal_repr: Dict[LogicLiteralT, int] = {
|
|
|
56
53
|
"-": _D,
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
_str_literals: Set[str] = {k for k in _literal_repr if isinstance(k, str)}
|
|
60
|
-
|
|
61
56
|
|
|
62
57
|
class Logic:
|
|
63
|
-
r"""
|
|
64
|
-
Model of a 9-value (``U``, ``X``, ``0``, ``1``, ``Z``, ``W``, ``L``, ``H``, ``-``) datatype commonly seen in VHDL.
|
|
65
|
-
|
|
66
|
-
.. currentmodule:: cocotb.types
|
|
58
|
+
r"""9-state digital signal value type.
|
|
67
59
|
|
|
68
|
-
This is modeled after VHDL's ``std_ulogic`` type.
|
|
69
|
-
|
|
60
|
+
This type is modeled after VHDL's ``std_ulogic`` type.
|
|
61
|
+
It can represent the values (``U``, ``X``, ``0``, ``1``, ``Z``, ``W``, ``L``, ``H``, ``-``).
|
|
62
|
+
(System)Verilog's 4-state ``logic`` type is a subset which only utilizes the ``X``, ``0``, ``1``, and ``Z`` values.
|
|
70
63
|
|
|
71
|
-
:class
|
|
72
|
-
|
|
73
|
-
``"U"``, ``"X"``, ``"0"``, ``"1"``, ``"Z"``, ``"W"``, ``"L"``, ``"H"``, ``"-"``, ``0``, ``1``, ``True``, and ``False``.
|
|
64
|
+
:class:`!Logic` can be converted to and from :class:`int`, :class:`str`, :class:`bool` and :class:`Bit`.
|
|
65
|
+
String literals include ``"U"``, ``"X"``, ``"0"``, ``"1"``, ``"Z"``, ``"W"``, ``"L"``, ``"H"``, ``"-"``, and their lowercase values.
|
|
74
66
|
|
|
75
67
|
.. code-block:: pycon3
|
|
76
68
|
|
|
@@ -92,8 +84,6 @@ class Logic:
|
|
|
92
84
|
|
|
93
85
|
The :class:`int` and :class:`bool` conversions will raise :exc:`ValueError` if the value is not ``0``, ``1``, ``L``, or ``H``.
|
|
94
86
|
|
|
95
|
-
:class:`Logic` values are immutable and therefore hashable and can be placed in :class:`set`\ s and used as keys in :class:`dict`\ s.
|
|
96
|
-
|
|
97
87
|
:class:`Logic` supports the common logic operations ``&``, ``|``, ``^``, and ``~``.
|
|
98
88
|
|
|
99
89
|
.. code-block:: pycon3
|
|
@@ -107,53 +97,54 @@ class Logic:
|
|
|
107
97
|
(Logic('0'), Logic('1'))
|
|
108
98
|
|
|
109
99
|
Args:
|
|
110
|
-
value: value to construct into a :class
|
|
100
|
+
value: value to construct into a :class:`!Logic`.
|
|
111
101
|
|
|
112
102
|
Raises:
|
|
113
|
-
ValueError: If the value if of the correct type, but cannot be constructed into a :class
|
|
114
|
-
TypeError: If the value is of a type that can't be constructed into a :class
|
|
103
|
+
ValueError: If the value if of the correct type, but cannot be constructed into a :class:`!Logic`.
|
|
104
|
+
TypeError: If the value is of a type that can't be constructed into a :class:`!Logic`.
|
|
115
105
|
"""
|
|
116
106
|
|
|
107
|
+
_values = {_U, _X, _0, _1, _Z, _W, _L, _H, _D}
|
|
108
|
+
|
|
117
109
|
_repr: int
|
|
118
110
|
|
|
111
|
+
__slots__ = ("_repr",)
|
|
112
|
+
|
|
119
113
|
@classmethod
|
|
120
114
|
@lru_cache(maxsize=None)
|
|
121
|
-
def _singleton(cls
|
|
115
|
+
def _singleton(cls, _repr: int) -> Self:
|
|
122
116
|
"""Return the Logic object associated with the repr, enforcing singleton."""
|
|
123
117
|
self = object.__new__(cls)
|
|
124
118
|
self._repr = _repr
|
|
125
119
|
return self
|
|
126
120
|
|
|
127
|
-
@classmethod
|
|
128
|
-
@lru_cache(maxsize=None)
|
|
129
|
-
def _map_literal(
|
|
130
|
-
cls: Type["Logic"],
|
|
131
|
-
value: LogicLiteralT,
|
|
132
|
-
) -> "Logic":
|
|
133
|
-
"""Convert and cache all literals."""
|
|
134
|
-
try:
|
|
135
|
-
_repr = _literal_repr[value]
|
|
136
|
-
except KeyError:
|
|
137
|
-
raise ValueError(
|
|
138
|
-
f"{value!r} is not convertible to a {cls.__qualname__}"
|
|
139
|
-
) from None
|
|
140
|
-
obj = cls._singleton(_repr)
|
|
141
|
-
return obj
|
|
142
|
-
|
|
143
121
|
def __new__(
|
|
144
|
-
cls
|
|
122
|
+
cls,
|
|
145
123
|
value: LogicConstructibleT,
|
|
146
|
-
) ->
|
|
124
|
+
) -> Self:
|
|
147
125
|
if isinstance(value, Logic):
|
|
148
|
-
|
|
126
|
+
_repr = value._repr
|
|
149
127
|
elif isinstance(value, (str, int)):
|
|
150
|
-
|
|
151
|
-
|
|
128
|
+
try:
|
|
129
|
+
_repr = _literal_repr[value]
|
|
130
|
+
except KeyError:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
f"{value!r} is not convertible to {cls.__qualname__}"
|
|
133
|
+
) from None
|
|
134
|
+
else:
|
|
135
|
+
raise TypeError(
|
|
136
|
+
f"Expected str, bool, or int, not {type(value).__qualname__}"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if _repr not in cls._values:
|
|
140
|
+
raise ValueError(f"{value!r} is not a valid {cls.__qualname__}")
|
|
152
141
|
|
|
153
|
-
|
|
154
|
-
|
|
142
|
+
return cls._singleton(_repr)
|
|
143
|
+
|
|
144
|
+
def __and__(self, other: Self) -> Self:
|
|
145
|
+
if not isinstance(other, type(self)):
|
|
155
146
|
return NotImplemented
|
|
156
|
-
return
|
|
147
|
+
return type(self)(
|
|
157
148
|
(
|
|
158
149
|
# -----------------------------------------------------
|
|
159
150
|
# U X 0 1 Z W L H - | |
|
|
@@ -170,10 +161,13 @@ class Logic:
|
|
|
170
161
|
)[self._repr][other._repr]
|
|
171
162
|
)
|
|
172
163
|
|
|
173
|
-
def
|
|
174
|
-
|
|
164
|
+
def __rand__(self, other: Self) -> Self:
|
|
165
|
+
return self & other
|
|
166
|
+
|
|
167
|
+
def __or__(self, other: Self) -> Self:
|
|
168
|
+
if not isinstance(other, type(self)):
|
|
175
169
|
return NotImplemented
|
|
176
|
-
return
|
|
170
|
+
return type(self)(
|
|
177
171
|
(
|
|
178
172
|
# -----------------------------------------------------
|
|
179
173
|
# U X 0 1 Z W L H - | |
|
|
@@ -190,10 +184,13 @@ class Logic:
|
|
|
190
184
|
)[self._repr][other._repr]
|
|
191
185
|
)
|
|
192
186
|
|
|
193
|
-
def
|
|
194
|
-
|
|
187
|
+
def __ror__(self, other: Self) -> Self:
|
|
188
|
+
return self | other
|
|
189
|
+
|
|
190
|
+
def __xor__(self, other: Self) -> Self:
|
|
191
|
+
if not isinstance(other, type(self)):
|
|
195
192
|
return NotImplemented
|
|
196
|
-
return
|
|
193
|
+
return type(self)(
|
|
197
194
|
(
|
|
198
195
|
# -----------------------------------------------------
|
|
199
196
|
# U X 0 1 Z W L H - | |
|
|
@@ -210,12 +207,15 @@ class Logic:
|
|
|
210
207
|
)[self._repr][other._repr]
|
|
211
208
|
)
|
|
212
209
|
|
|
213
|
-
def
|
|
214
|
-
return
|
|
210
|
+
def __rxor__(self, other: Self) -> Self:
|
|
211
|
+
return self ^ other
|
|
212
|
+
|
|
213
|
+
def __invert__(self) -> Self:
|
|
214
|
+
return type(self)(("U", "X", "1", "0", "X", "X", "1", "0", "X")[self._repr])
|
|
215
215
|
|
|
216
216
|
def __eq__(self, other: object) -> bool:
|
|
217
217
|
if isinstance(other, Logic):
|
|
218
|
-
return self
|
|
218
|
+
return self._repr == other._repr
|
|
219
219
|
elif isinstance(other, (int, str, bool)):
|
|
220
220
|
try:
|
|
221
221
|
other = Logic(other)
|
|
@@ -225,6 +225,8 @@ class Logic:
|
|
|
225
225
|
else:
|
|
226
226
|
return NotImplemented
|
|
227
227
|
|
|
228
|
+
__hash__: None # type: ignore[assignment]
|
|
229
|
+
|
|
228
230
|
def __repr__(self) -> str:
|
|
229
231
|
return f"{type(self).__qualname__}({str(self)!r})"
|
|
230
232
|
|
|
@@ -260,12 +262,13 @@ class Logic:
|
|
|
260
262
|
def __index__(self) -> int:
|
|
261
263
|
return int(self)
|
|
262
264
|
|
|
263
|
-
def resolve(self, resolver: ResolverLiteral) ->
|
|
264
|
-
"""
|
|
265
|
+
def resolve(self, resolver: ResolverLiteral) -> Self:
|
|
266
|
+
"""Resolve non-``0``/``1`` values to ``0``/``1``.
|
|
265
267
|
|
|
266
268
|
The possible values of the *resolver* argument are:
|
|
267
269
|
|
|
268
|
-
* ``"weak"``:
|
|
270
|
+
* ``"weak"``:
|
|
271
|
+
Weak values are resolved to their strong-valued equivalents.
|
|
269
272
|
|
|
270
273
|
* ``"zeros"``:
|
|
271
274
|
``L`` and ``H`` are resolved to ``0`` and ``1``, respectively.
|
|
@@ -289,8 +292,42 @@ class Logic:
|
|
|
289
292
|
ValueError: Invalid *resolver* value.
|
|
290
293
|
TypeError: Unsupported *value* type.
|
|
291
294
|
"""
|
|
292
|
-
return
|
|
295
|
+
return type(self)(get_str_resolver(resolver)(str(self)))
|
|
293
296
|
|
|
294
|
-
@deprecated('`len(logic)` is deprecated. A Logic\'s "length" is always 1.')
|
|
295
297
|
def __len__(self) -> int:
|
|
296
298
|
return 1
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def is_resolvable(self) -> bool:
|
|
302
|
+
"""``True`` if value is ``0``, ``1``, ``L``, ``H``.
|
|
303
|
+
|
|
304
|
+
.. versionadded:: 2.0
|
|
305
|
+
"""
|
|
306
|
+
return (False, False, True, True, False, False, True, True, False)[self._repr]
|
|
307
|
+
|
|
308
|
+
def __copy__(self) -> "Logic":
|
|
309
|
+
return self
|
|
310
|
+
|
|
311
|
+
def __deepcopy__(self, memo: Dict[int, object]) -> "Logic":
|
|
312
|
+
return self
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class Bit(Logic):
|
|
316
|
+
"""2-state digital signal value type.
|
|
317
|
+
|
|
318
|
+
This is modeled after (System)Verilog's and VHDL's ``bit`` type.
|
|
319
|
+
It can represent only the values ``0`` and ``1``.
|
|
320
|
+
It can be converted to and from :class:`int`, :class:`str`, :class:`bool`, or :class:`Logic` values.
|
|
321
|
+
|
|
322
|
+
As a subtype of :class:`!Logic`, it supports all of the same operations
|
|
323
|
+
and can be used in operations interchangeably with :class:`!Logic`.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
value: value to construct into a :class:`!Bit`.
|
|
327
|
+
|
|
328
|
+
Raises:
|
|
329
|
+
ValueError: If the value if of the correct type, but cannot be constructed into a :class:`!Bit`.
|
|
330
|
+
TypeError: If the value is of a type that can't be constructed into a :class:`!Bit`.
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
_values = {_0, _1}
|
cocotb/types/_logic_array.py
CHANGED
|
@@ -1,8 +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
|
+
import copy
|
|
5
|
+
import warnings
|
|
4
6
|
from math import ceil
|
|
5
7
|
from typing import (
|
|
8
|
+
Any,
|
|
9
|
+
Dict,
|
|
6
10
|
Iterable,
|
|
7
11
|
Iterator,
|
|
8
12
|
List,
|
|
@@ -14,29 +18,29 @@ from typing import (
|
|
|
14
18
|
from cocotb._deprecation import deprecated
|
|
15
19
|
from cocotb._py_compat import Literal, TypeAlias
|
|
16
20
|
from cocotb.types._abstract_array import AbstractMutableArray
|
|
17
|
-
from cocotb.types.
|
|
21
|
+
from cocotb.types._indexing import IndexingChangedWarning
|
|
22
|
+
from cocotb.types._logic import Logic, LogicConstructibleT
|
|
18
23
|
from cocotb.types._range import Range
|
|
19
24
|
from cocotb.types._resolve import RESOLVE_X, ResolverLiteral, get_str_resolver
|
|
20
25
|
|
|
21
26
|
_resolve_lh_table = str.maketrans({"L": "0", "H": "1"})
|
|
27
|
+
_str_literals = frozenset("uUxX01zZwWlLhH-")
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
ByteOrder: TypeAlias = Literal["big", "little"]
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
class LogicArray(AbstractMutableArray[Logic]):
|
|
28
|
-
r"""Fixed-sized, arbitrarily-indexed, Array of
|
|
34
|
+
r"""Fixed-sized, arbitrarily-indexed, :class:`.Array` of :class:`.Logic`\ s.
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
An :class:`Array`, where all elements are enforced to be :class:`Logic`.
|
|
36
|
+
An :class:`!Array`, where all elements are enforced to be :class:`!Logic`.
|
|
33
37
|
This allows the additional of bit-wise logical operators, conversions to integers and bytes, and ``X`` testing and mapping.
|
|
34
38
|
|
|
35
39
|
:class:`!LogicArray`\ s can be constructed from an iterable of :class:`!Logic`\ s,
|
|
36
40
|
or values constructible into :class:`!Logic`, like :class:`bool`, :class:`str`, or :class:`int`.
|
|
37
41
|
Alternatively, they can be constructed from :class:`!str` or :class:`!int` literals.
|
|
38
42
|
|
|
39
|
-
Like :class
|
|
43
|
+
Like :class:`!Array`, if *range* is not given, the range ``Range(len(value)-1, "downto", 0)`` is used;
|
|
40
44
|
and if an :class:`int` is passed for *range*, the range ``Range(range-1, "downto", 0)`` is used.
|
|
41
45
|
|
|
42
46
|
.. code-block:: pycon3
|
|
@@ -74,7 +78,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
74
78
|
>>> LogicArray.from_bytes(b"1n", byteorder="little")
|
|
75
79
|
LogicArray('0110111000110001', Range(15, 'downto', 0))
|
|
76
80
|
|
|
77
|
-
:class:`!LogicArray`\ s support the same :class:`list`-like operations as :class
|
|
81
|
+
:class:`!LogicArray`\ s support the same :class:`list`-like operations as :class:`!Array`;
|
|
78
82
|
however, it enforces the condition that all elements must be a :class:`!Logic`.
|
|
79
83
|
|
|
80
84
|
.. code-block:: pycon3
|
|
@@ -92,7 +96,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
92
96
|
>>> list(array) # is an iterable
|
|
93
97
|
[Logic('1'), Logic('0'), Logic('1'), Logic('0')]
|
|
94
98
|
|
|
95
|
-
When setting an element or slice, the *value* is first constructed into a :class
|
|
99
|
+
When setting an element or slice, the *value* is first constructed into a :class:`!Logic`.
|
|
96
100
|
|
|
97
101
|
.. code-block:: pycon3
|
|
98
102
|
|
|
@@ -147,7 +151,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
147
151
|
These operations assume the value is entirely ``0``, ``1``, ``L``, or ``H``, and will raise an exception otherwise.
|
|
148
152
|
|
|
149
153
|
You can also convert :class:`!LogicArray`\ s to hexadecimal or binary strings using
|
|
150
|
-
the built-ins :func:`hex
|
|
154
|
+
the built-ins :func:`hex` and :func:`bin`, respectively.
|
|
151
155
|
|
|
152
156
|
.. code-block:: pycon3
|
|
153
157
|
|
|
@@ -159,8 +163,8 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
159
163
|
'0b1111010'
|
|
160
164
|
|
|
161
165
|
.. warning::
|
|
162
|
-
Using :func:`hex` or :func:`bin` first turns the LogicArray into an :class:`int`.
|
|
163
|
-
This means the exact length of the LogicArray is lost.
|
|
166
|
+
Using :func:`hex` or :func:`bin` first turns the :class:`!LogicArray` into an :class:`int`.
|
|
167
|
+
This means the exact length of the :class:`!LogicArray` is lost.
|
|
164
168
|
It also means that these expressions will raise an exception if the value is not entirely ``0``, ``1``, ``L``, or ``H``.
|
|
165
169
|
|
|
166
170
|
:class:`!LogicArray`\ s also support element-wise logical operations: ``&``, ``|``,
|
|
@@ -179,12 +183,12 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
179
183
|
LogicArray('1110', Range(3, 'downto', 0))
|
|
180
184
|
|
|
181
185
|
Args:
|
|
182
|
-
value: Initial value for the LogicArray
|
|
183
|
-
range: The indexing scheme of the LogicArray
|
|
186
|
+
value: Initial value for the :class:`!LogicArray`.
|
|
187
|
+
range: The indexing scheme of the :class:`!LogicArray`.
|
|
184
188
|
|
|
185
189
|
Raises:
|
|
186
190
|
TypeError: When invalid argument types are used.
|
|
187
|
-
ValueError: When *value* will not fit in a LogicArray of the given *range*.
|
|
191
|
+
ValueError: When *value* will not fit in a :class:`!LogicArray` of the given *range*.
|
|
188
192
|
"""
|
|
189
193
|
|
|
190
194
|
# These three attribute contain the current value of the array in one or more of
|
|
@@ -196,6 +200,15 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
196
200
|
_value_as_int: Union[int, None]
|
|
197
201
|
_value_as_str: Union[str, None]
|
|
198
202
|
_range: Range
|
|
203
|
+
_warn_indexing: bool
|
|
204
|
+
|
|
205
|
+
__slots__ = (
|
|
206
|
+
"_value_as_array",
|
|
207
|
+
"_value_as_int",
|
|
208
|
+
"_value_as_str",
|
|
209
|
+
"_range",
|
|
210
|
+
"_warn_indexing",
|
|
211
|
+
)
|
|
199
212
|
|
|
200
213
|
def __init__(
|
|
201
214
|
self,
|
|
@@ -205,6 +218,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
205
218
|
self._value_as_array = None
|
|
206
219
|
self._value_as_int = None
|
|
207
220
|
self._value_as_str = None
|
|
221
|
+
self._warn_indexing = False
|
|
208
222
|
|
|
209
223
|
if isinstance(range, int):
|
|
210
224
|
range = Range(range - 1, "downto", 0)
|
|
@@ -299,7 +313,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
299
313
|
|
|
300
314
|
Args:
|
|
301
315
|
value: The integer to convert.
|
|
302
|
-
range: Indexing scheme for the LogicArray
|
|
316
|
+
range: Indexing scheme for the :class:`!LogicArray`.
|
|
303
317
|
|
|
304
318
|
Returns:
|
|
305
319
|
A :class:`!LogicArray` equivalent to the *value*.
|
|
@@ -325,7 +339,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
325
339
|
|
|
326
340
|
Args:
|
|
327
341
|
value: The integer to convert.
|
|
328
|
-
range: Indexing scheme for the LogicArray
|
|
342
|
+
range: Indexing scheme for the :class:`!LogicArray`.
|
|
329
343
|
|
|
330
344
|
Returns:
|
|
331
345
|
A :class:`!LogicArray` equivalent to the *value*.
|
|
@@ -371,7 +385,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
371
385
|
|
|
372
386
|
Args:
|
|
373
387
|
value: The bytes to convert.
|
|
374
|
-
range: Indexing scheme for the LogicArray
|
|
388
|
+
range: Indexing scheme for the :class:`!LogicArray`.
|
|
375
389
|
byteorder: The endianness used to construct the intermediate integer, either ``"big"`` or ``"little"``.
|
|
376
390
|
|
|
377
391
|
Returns:
|
|
@@ -393,7 +407,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
393
407
|
return LogicArray(value_as_int, range)
|
|
394
408
|
|
|
395
409
|
@classmethod
|
|
396
|
-
def _from_handle(cls, value: str) -> "LogicArray":
|
|
410
|
+
def _from_handle(cls, value: str, warn_indexing: bool) -> "LogicArray":
|
|
397
411
|
# Used by cocotb.handle classes to make LogicArray from values gotten from the
|
|
398
412
|
# simulator which we expect to be well-formed.
|
|
399
413
|
# Values are required to be uppercase.
|
|
@@ -402,6 +416,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
402
416
|
self._value_as_int = None
|
|
403
417
|
self._value_as_str = value
|
|
404
418
|
self._range = Range(len(value) - 1, "downto", 0)
|
|
419
|
+
self._warn_indexing = warn_indexing
|
|
405
420
|
return self
|
|
406
421
|
|
|
407
422
|
@property
|
|
@@ -514,9 +529,7 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
514
529
|
@property
|
|
515
530
|
def is_resolvable(self) -> bool:
|
|
516
531
|
"""``True`` if all elements are ``0``, ``1``, ``L``, ``H``."""
|
|
517
|
-
return all(
|
|
518
|
-
bit in (Logic("0"), Logic("1"), Logic("L"), Logic("H")) for bit in self
|
|
519
|
-
)
|
|
532
|
+
return all(bit.is_resolvable for bit in self)
|
|
520
533
|
|
|
521
534
|
@property
|
|
522
535
|
@deprecated(
|
|
@@ -677,9 +690,23 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
677
690
|
def __getitem__(self, item: Union[int, slice]) -> Union[Logic, "LogicArray"]:
|
|
678
691
|
array = self._get_array()
|
|
679
692
|
if isinstance(item, int):
|
|
693
|
+
if self._warn_indexing:
|
|
694
|
+
warnings.warn(
|
|
695
|
+
f"Update index {item} to {self.range[item]}",
|
|
696
|
+
IndexingChangedWarning,
|
|
697
|
+
stacklevel=2,
|
|
698
|
+
)
|
|
680
699
|
idx = self._translate_index(item)
|
|
681
700
|
return array[idx]
|
|
682
701
|
elif isinstance(item, slice):
|
|
702
|
+
if self._warn_indexing:
|
|
703
|
+
start = item.start if item.start is not None else 0
|
|
704
|
+
stop = item.stop if item.stop is not None else len(self) - 1
|
|
705
|
+
warnings.warn(
|
|
706
|
+
f"Update slice {start}:{stop} to {self.range[start]}:{self.range[stop]}",
|
|
707
|
+
IndexingChangedWarning,
|
|
708
|
+
stacklevel=2,
|
|
709
|
+
)
|
|
683
710
|
start = item.start if item.start is not None else self.left
|
|
684
711
|
stop = item.stop if item.stop is not None else self.right
|
|
685
712
|
if item.step is not None:
|
|
@@ -807,7 +834,8 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
807
834
|
|
|
808
835
|
The possible values of the *resolver* argument are:
|
|
809
836
|
|
|
810
|
-
* ``"weak"``:
|
|
837
|
+
* ``"weak"``:
|
|
838
|
+
Weak values are resolved to their strong-valued equivalents.
|
|
811
839
|
|
|
812
840
|
* ``"zeros"``:
|
|
813
841
|
``L`` and ``H`` are resolved to ``0`` and ``1``, respectively.
|
|
@@ -832,3 +860,9 @@ class LogicArray(AbstractMutableArray[Logic]):
|
|
|
832
860
|
TypeError: Unsupported *value* type.
|
|
833
861
|
"""
|
|
834
862
|
return LogicArray(get_str_resolver(resolver)(str(self)), self.range)
|
|
863
|
+
|
|
864
|
+
def __copy__(self) -> "LogicArray":
|
|
865
|
+
raise NotImplementedError("`copy.copy` on LogicArray is not supported")
|
|
866
|
+
|
|
867
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> "LogicArray":
|
|
868
|
+
return LogicArray(self._get_str(), copy.deepcopy(self._range, memo=memo))
|
cocotb/types/_range.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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
|
+
import copy
|
|
4
5
|
from functools import lru_cache
|
|
5
|
-
from typing import Iterator, Sequence, Union, overload
|
|
6
|
+
from typing import Any, Dict, Iterator, Sequence, Union, overload
|
|
6
7
|
|
|
7
8
|
from cocotb._utils import cached_method
|
|
8
9
|
|
|
@@ -40,7 +41,7 @@ class Range(Sequence[int]):
|
|
|
40
41
|
|
|
41
42
|
:class:`Range` supports "null" ranges as seen in VHDL.
|
|
42
43
|
"null" ranges occur when a left bound cannot reach a right bound with the given direction.
|
|
43
|
-
They have a length of 0
|
|
44
|
+
They have a length of ``0``, but the :attr:`left`, :attr:`right`, and :attr:`direction` values remain as given.
|
|
44
45
|
|
|
45
46
|
.. code-block:: pycon3
|
|
46
47
|
|
|
@@ -61,9 +62,9 @@ class Range(Sequence[int]):
|
|
|
61
62
|
The typical use case of this type is in conjunction with :class:`~cocotb.types.Array`.
|
|
62
63
|
|
|
63
64
|
Args:
|
|
64
|
-
left:
|
|
65
|
-
direction: ``'to'`` if values are ascending, ``'downto'`` if descending
|
|
66
|
-
right:
|
|
65
|
+
left: Leftmost bound of range.
|
|
66
|
+
direction: ``'to'`` if values are ascending, ``'downto'`` if descending.
|
|
67
|
+
right: Rightmost bound of range (inclusive).
|
|
67
68
|
"""
|
|
68
69
|
|
|
69
70
|
@overload
|
|
@@ -165,6 +166,12 @@ class Range(Sequence[int]):
|
|
|
165
166
|
|
|
166
167
|
index = cached_method(Sequence.index)
|
|
167
168
|
|
|
169
|
+
def __copy__(self) -> "Range":
|
|
170
|
+
return Range.from_range(self._range)
|
|
171
|
+
|
|
172
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> "Range":
|
|
173
|
+
return Range.from_range(copy.deepcopy(self._range, memo=memo))
|
|
174
|
+
|
|
168
175
|
|
|
169
176
|
def _guess_step(left: int, right: int) -> int:
|
|
170
177
|
if left <= right:
|
cocotb/utils.py
CHANGED
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
5
5
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
6
6
|
|
|
7
|
-
"""
|
|
7
|
+
"""Tools for dealing with simulated time."""
|
|
8
8
|
|
|
9
9
|
import warnings
|
|
10
10
|
from decimal import Decimal
|
|
11
11
|
from fractions import Fraction
|
|
12
|
-
from
|
|
13
|
-
from math import ceil, floor
|
|
14
|
-
from typing import Union, overload
|
|
12
|
+
from typing import Union
|
|
15
13
|
|
|
16
|
-
from cocotb import simulator
|
|
17
|
-
from cocotb._py_compat import Literal, TypeAlias
|
|
18
14
|
from cocotb._typing import RoundMode, TimeUnit
|
|
15
|
+
from cocotb.simtime import (
|
|
16
|
+
_get_sim_steps,
|
|
17
|
+
_get_time_from_sim_steps,
|
|
18
|
+
get_sim_time,
|
|
19
|
+
)
|
|
19
20
|
|
|
20
21
|
__all__ = (
|
|
21
22
|
"get_sim_steps",
|
|
@@ -24,74 +25,6 @@ __all__ = (
|
|
|
24
25
|
)
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def _get_simulator_precision() -> int:
|
|
28
|
-
# cache and replace this function
|
|
29
|
-
precision = simulator.get_precision()
|
|
30
|
-
global _get_simulator_precision
|
|
31
|
-
_get_simulator_precision = precision.__int__
|
|
32
|
-
return _get_simulator_precision()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# Simulator helper functions
|
|
36
|
-
def get_sim_time(unit: TimeUnit = "step", *, units: None = None) -> float:
|
|
37
|
-
"""Retrieve the simulation time from the simulator.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
unit: String specifying the unit of the result
|
|
41
|
-
(one of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``).
|
|
42
|
-
``'step'`` will return the raw simulation time.
|
|
43
|
-
|
|
44
|
-
.. versionchanged:: 2.0
|
|
45
|
-
Passing ``None`` as the *unit* argument was removed, use ``'step'`` instead.
|
|
46
|
-
|
|
47
|
-
.. versionchanged:: 2.0
|
|
48
|
-
Renamed from ``units``.
|
|
49
|
-
|
|
50
|
-
Raises:
|
|
51
|
-
ValueError: If *unit* is not a valid unit.
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
The simulation time in the specified unit.
|
|
55
|
-
|
|
56
|
-
.. versionchanged:: 1.6
|
|
57
|
-
Support ``'step'`` as the the *unit* argument to mean "simulator time step".
|
|
58
|
-
"""
|
|
59
|
-
if units is not None:
|
|
60
|
-
warnings.warn(
|
|
61
|
-
"The 'units' argument has been renamed to 'unit'.",
|
|
62
|
-
DeprecationWarning,
|
|
63
|
-
stacklevel=2,
|
|
64
|
-
)
|
|
65
|
-
unit = units
|
|
66
|
-
timeh, timel = simulator.get_sim_time()
|
|
67
|
-
steps = timeh << 32 | timel
|
|
68
|
-
return get_time_from_sim_steps(steps, unit) if unit != "step" else steps
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@overload
|
|
72
|
-
def _ldexp10(frac: float, exp: int) -> float: ...
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@overload
|
|
76
|
-
def _ldexp10(frac: Fraction, exp: int) -> Fraction: ...
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@overload
|
|
80
|
-
def _ldexp10(frac: Decimal, exp: int) -> Decimal: ...
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def _ldexp10(
|
|
84
|
-
frac: Union[float, Fraction, Decimal], exp: int
|
|
85
|
-
) -> Union[float, Fraction, Decimal]:
|
|
86
|
-
"""Like :func:`math.ldexp`, but base 10."""
|
|
87
|
-
# using * or / separately prevents rounding errors if `frac` is a
|
|
88
|
-
# high-precision type
|
|
89
|
-
if exp > 0:
|
|
90
|
-
return frac * (10**exp)
|
|
91
|
-
else:
|
|
92
|
-
return frac / (10**-exp)
|
|
93
|
-
|
|
94
|
-
|
|
95
28
|
def get_time_from_sim_steps(
|
|
96
29
|
steps: int,
|
|
97
30
|
unit: Union[TimeUnit, None] = None,
|
|
@@ -124,9 +57,7 @@ def get_time_from_sim_steps(
|
|
|
124
57
|
unit = units
|
|
125
58
|
if unit is None:
|
|
126
59
|
raise TypeError("Missing required argument 'unit'")
|
|
127
|
-
|
|
128
|
-
return steps
|
|
129
|
-
return _ldexp10(steps, _get_simulator_precision() - _get_log_time_scale(unit))
|
|
60
|
+
return _get_time_from_sim_steps(steps, unit)
|
|
130
61
|
|
|
131
62
|
|
|
132
63
|
def get_sim_steps(
|
|
@@ -176,55 +107,4 @@ def get_sim_steps(
|
|
|
176
107
|
stacklevel=2,
|
|
177
108
|
)
|
|
178
109
|
unit = units
|
|
179
|
-
|
|
180
|
-
if unit != "step":
|
|
181
|
-
result = _ldexp10(time, _get_log_time_scale(unit) - _get_simulator_precision())
|
|
182
|
-
else:
|
|
183
|
-
result = time
|
|
184
|
-
|
|
185
|
-
if round_mode == "error":
|
|
186
|
-
result_rounded = floor(result)
|
|
187
|
-
if result_rounded != result:
|
|
188
|
-
precision = _get_simulator_precision()
|
|
189
|
-
raise ValueError(
|
|
190
|
-
f"Unable to accurately represent {time}({unit}) with the simulator precision of 1e{precision}"
|
|
191
|
-
)
|
|
192
|
-
elif round_mode == "ceil":
|
|
193
|
-
result_rounded = ceil(result)
|
|
194
|
-
elif round_mode == "round":
|
|
195
|
-
result_rounded = round(result)
|
|
196
|
-
elif round_mode == "floor":
|
|
197
|
-
result_rounded = floor(result)
|
|
198
|
-
else:
|
|
199
|
-
raise ValueError(f"Invalid round_mode specifier: {round_mode}")
|
|
200
|
-
|
|
201
|
-
return result_rounded
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
TimeUnitWithoutSteps: TypeAlias = Literal["fs", "ps", "ns", "us", "ms", "sec"]
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
@lru_cache(maxsize=None)
|
|
208
|
-
def _get_log_time_scale(unit: TimeUnitWithoutSteps) -> int:
|
|
209
|
-
"""Retrieves the ``log10()`` of the scale factor for a given time unit.
|
|
210
|
-
|
|
211
|
-
Args:
|
|
212
|
-
unit: String specifying the unit
|
|
213
|
-
(one of ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``).
|
|
214
|
-
|
|
215
|
-
.. versionchanged:: 2.0
|
|
216
|
-
Renamed from ``units``.
|
|
217
|
-
|
|
218
|
-
Raises:
|
|
219
|
-
ValueError: If *unit* is not a valid unit.
|
|
220
|
-
|
|
221
|
-
Returns:
|
|
222
|
-
The ``log10()`` of the scale factor for the time unit.
|
|
223
|
-
"""
|
|
224
|
-
scale = {"fs": -15, "ps": -12, "ns": -9, "us": -6, "ms": -3, "sec": 0}
|
|
225
|
-
|
|
226
|
-
unit_lwr = unit.lower()
|
|
227
|
-
if unit_lwr not in scale:
|
|
228
|
-
raise ValueError(f"Invalid unit ({unit}) provided")
|
|
229
|
-
else:
|
|
230
|
-
return scale[unit_lwr]
|
|
110
|
+
return _get_sim_steps(time, unit, round_mode=round_mode)
|