cocotb 2.0.0b1__cp311-cp311-win_amd64.whl → 2.0.0rc2__cp311-cp311-win_amd64.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.cp311-win_amd64.exp +0 -0
  73. cocotb/simulator.cp311-win_amd64.lib +0 -0
  74. cocotb/simulator.cp311-win_amd64.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/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()
Binary file
Binary file
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 CoroutineType
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
- #: Task result type
34
- ResultType = TypeVar("ResultType")
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 not isinstance(inst, collections.abc.Coroutine):
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
- return logging.getLogger(f"cocotb.{self._name}.{self._coro.__qualname__}")
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 inspect.iscoroutine(self._coro):
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 ._logic import Logic
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
- from typing import Iterable, Iterator, List, TypeVar, Union, cast, overload
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(cls, value: List[T], range: Range) -> "Array[T]":
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."""