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.

Files changed (95) hide show
  1. cocotb/_ANSI.py +47 -54
  2. cocotb/__init__.py +12 -2
  3. cocotb/_base_triggers.py +11 -9
  4. cocotb/_bridge.py +8 -9
  5. cocotb/_extended_awaitables.py +1 -1
  6. cocotb/_gpi_triggers.py +4 -1
  7. cocotb/_init.py +17 -11
  8. cocotb/_py_compat.py +24 -10
  9. cocotb/_scheduler.py +25 -31
  10. cocotb/_test.py +6 -3
  11. cocotb/_test_factory.py +4 -1
  12. cocotb/_utils.py +1 -23
  13. cocotb/_version.py +1 -1
  14. cocotb/clock.py +98 -16
  15. cocotb/debug.py +24 -0
  16. cocotb/handle.py +62 -32
  17. cocotb/libs/cocotb.dll +0 -0
  18. cocotb/libs/cocotb.exp +0 -0
  19. cocotb/libs/cocotb.lib +0 -0
  20. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  21. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  22. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  23. cocotb/libs/cocotbutils.dll +0 -0
  24. cocotb/libs/cocotbutils.exp +0 -0
  25. cocotb/libs/cocotbutils.lib +0 -0
  26. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  27. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  28. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  29. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  30. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  31. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  32. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  33. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  34. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  35. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  36. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  37. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  38. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  39. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  40. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  41. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  42. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  43. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  44. cocotb/libs/embed.dll +0 -0
  45. cocotb/libs/embed.exp +0 -0
  46. cocotb/libs/embed.lib +0 -0
  47. cocotb/libs/gpi.dll +0 -0
  48. cocotb/libs/gpi.exp +0 -0
  49. cocotb/libs/gpi.lib +0 -0
  50. cocotb/libs/gpilog.dll +0 -0
  51. cocotb/libs/gpilog.exp +0 -0
  52. cocotb/libs/gpilog.lib +0 -0
  53. cocotb/libs/pygpilog.dll +0 -0
  54. cocotb/libs/pygpilog.exp +0 -0
  55. cocotb/libs/pygpilog.lib +0 -0
  56. cocotb/logging.py +243 -117
  57. cocotb/regression.py +43 -35
  58. cocotb/share/def/aldec.exp +0 -0
  59. cocotb/share/def/aldec.lib +0 -0
  60. cocotb/share/def/ghdl.exp +0 -0
  61. cocotb/share/def/ghdl.lib +0 -0
  62. cocotb/share/def/icarus.exp +0 -0
  63. cocotb/share/def/icarus.lib +0 -0
  64. cocotb/share/def/modelsim.exp +0 -0
  65. cocotb/share/def/modelsim.lib +0 -0
  66. cocotb/share/include/cocotb_utils.h +3 -3
  67. cocotb/share/include/embed.h +2 -2
  68. cocotb/share/include/gpi.h +258 -109
  69. cocotb/share/include/py_gpi_logging.h +3 -3
  70. cocotb/share/lib/verilator/verilator.cpp +23 -15
  71. cocotb/simtime.py +230 -0
  72. cocotb/simulator.cp313-win32.exp +0 -0
  73. cocotb/simulator.cp313-win32.lib +0 -0
  74. cocotb/simulator.cp313-win32.pyd +0 -0
  75. cocotb/task.py +54 -11
  76. cocotb/types/__init__.py +4 -1
  77. cocotb/types/_array.py +33 -2
  78. cocotb/types/_indexing.py +17 -0
  79. cocotb/types/_logic.py +96 -59
  80. cocotb/types/_logic_array.py +56 -22
  81. cocotb/types/_range.py +12 -5
  82. cocotb/utils.py +9 -129
  83. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/METADATA +1 -1
  84. cocotb-2.0.0rc2.dist-info/RECORD +146 -0
  85. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/licenses/LICENSE +1 -0
  86. cocotb_tools/config.py +5 -5
  87. cocotb_tools/makefiles/Makefile.inc +3 -9
  88. cocotb_tools/makefiles/Makefile.sim +9 -1
  89. cocotb_tools/makefiles/simulators/Makefile.vcs +1 -1
  90. cocotb_tools/runner.py +94 -59
  91. pygpi/entry.py +5 -6
  92. cocotb-2.0.0b1.dist-info/RECORD +0 -143
  93. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +0 -0
  94. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/entry_points.txt +0 -0
  95. {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._deprecation import deprecated
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
- (System)Verilog's 4-value ``logic`` type only utilizes ``X``, ``0``, ``1``, and ``Z`` values.
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:`Logic` can be converted to and from :class:`int`, :class:`str`, and :class:`bool`.
72
- The list of values convertible to :class:`Logic` includes
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:`Logic`.
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:`Logic`.
114
- TypeError: If the value is of a type that can't be constructed into a :class:`Logic`.
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: Type["Logic"], _repr: int) -> "Logic":
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: Type["Logic"],
122
+ cls,
145
123
  value: LogicConstructibleT,
146
- ) -> "Logic":
124
+ ) -> Self:
147
125
  if isinstance(value, Logic):
148
- return value
126
+ _repr = value._repr
149
127
  elif isinstance(value, (str, int)):
150
- return cls._map_literal(value)
151
- raise TypeError(f"Expected str, bool, or int, not {type(value).__qualname__}")
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
- def __and__(self, other: "Logic") -> "Logic":
154
- if not isinstance(other, Logic):
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 Logic(
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 __or__(self: "Logic", other: "Logic") -> "Logic":
174
- if not isinstance(other, Logic):
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 Logic(
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 __xor__(self: "Logic", other: "Logic") -> "Logic":
194
- if not isinstance(other, Logic):
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 Logic(
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 __invert__(self: "Logic") -> "Logic":
214
- return Logic(("U", "X", "1", "0", "X", "X", "1", "0", "X")[self._repr])
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 is other
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) -> "Logic":
264
- """Resolves non-0/1 values to 0/1.
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"``: Weak values are resolved to their strong-valued equivalents.
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 Logic(get_str_resolver(resolver)(str(self)))
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}
@@ -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._logic import Logic, LogicConstructibleT, _str_literals
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 Logics.
34
+ r"""Fixed-sized, arbitrarily-indexed, :class:`.Array` of :class:`.Logic`\ s.
29
35
 
30
- .. currentmodule:: cocotb.types
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:`Array`, if *range* is not given, the range ``Range(len(value)-1, "downto", 0)`` is used;
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:`Array`;
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:`Logic`.
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:` and :func:`bin`, respectively.
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"``: Weak values are resolved to their strong-valued equivalents.
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, but the :attr:`left`, :attr:`right`, and :attr:`direction` values remain as given.
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: leftmost bound of range
65
- direction: ``'to'`` if values are ascending, ``'downto'`` if descending
66
- right: rightmost bound of range (inclusive)
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
- """Utility functions for dealing with simulation time."""
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 functools import lru_cache
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
- if unit == "step":
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
- result: Union[float, Fraction, Decimal]
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)