cocotb 1.9.2__cp312-cp312-win32.whl → 2.0.0rc2__cp312-cp312-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 (161) hide show
  1. cocotb/_ANSI.py +65 -0
  2. cocotb/__init__.py +81 -327
  3. cocotb/_base_triggers.py +515 -0
  4. cocotb/_bridge.py +186 -0
  5. cocotb/_decorators.py +515 -0
  6. cocotb/_deprecation.py +3 -3
  7. cocotb/_exceptions.py +7 -0
  8. cocotb/_extended_awaitables.py +419 -0
  9. cocotb/_gpi_triggers.py +385 -0
  10. cocotb/_init.py +301 -0
  11. cocotb/_outcomes.py +54 -0
  12. cocotb/_profiling.py +46 -0
  13. cocotb/_py_compat.py +114 -29
  14. cocotb/_scheduler.py +448 -0
  15. cocotb/_test.py +248 -0
  16. cocotb/_test_factory.py +312 -0
  17. cocotb/_test_functions.py +42 -0
  18. cocotb/_typing.py +7 -0
  19. cocotb/_utils.py +274 -0
  20. cocotb/_version.py +3 -7
  21. cocotb/_xunit_reporter.py +66 -0
  22. cocotb/clock.py +353 -108
  23. cocotb/debug.py +24 -0
  24. cocotb/handle.py +1370 -793
  25. cocotb/libs/cocotb.dll +0 -0
  26. cocotb/libs/cocotb.exp +0 -0
  27. cocotb/libs/cocotb.lib +0 -0
  28. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  29. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  30. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  31. cocotb/libs/cocotbutils.dll +0 -0
  32. cocotb/libs/cocotbutils.exp +0 -0
  33. cocotb/libs/cocotbutils.lib +0 -0
  34. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  35. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  36. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  37. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  38. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  39. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  40. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  41. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  42. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  43. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  44. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  45. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  46. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  47. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  48. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  49. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  50. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  51. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  52. cocotb/libs/embed.dll +0 -0
  53. cocotb/libs/embed.exp +0 -0
  54. cocotb/libs/embed.lib +0 -0
  55. cocotb/libs/gpi.dll +0 -0
  56. cocotb/libs/gpi.exp +0 -0
  57. cocotb/libs/gpi.lib +0 -0
  58. cocotb/libs/gpilog.dll +0 -0
  59. cocotb/libs/gpilog.exp +0 -0
  60. cocotb/libs/gpilog.lib +0 -0
  61. cocotb/libs/pygpilog.dll +0 -0
  62. cocotb/libs/pygpilog.exp +0 -0
  63. cocotb/libs/pygpilog.lib +0 -0
  64. cocotb/logging.py +424 -0
  65. cocotb/queue.py +103 -57
  66. cocotb/regression.py +680 -717
  67. cocotb/result.py +17 -188
  68. cocotb/share/def/aldec.exp +0 -0
  69. cocotb/share/def/aldec.lib +0 -0
  70. cocotb/share/def/ghdl.exp +0 -0
  71. cocotb/share/def/ghdl.lib +0 -0
  72. cocotb/share/def/icarus.exp +0 -0
  73. cocotb/share/def/icarus.lib +0 -0
  74. cocotb/share/def/modelsim.def +1 -0
  75. cocotb/share/def/modelsim.exp +0 -0
  76. cocotb/share/def/modelsim.lib +0 -0
  77. cocotb/share/include/cocotb_utils.h +9 -32
  78. cocotb/share/include/embed.h +7 -30
  79. cocotb/share/include/gpi.h +331 -137
  80. cocotb/share/include/gpi_logging.h +221 -142
  81. cocotb/share/include/py_gpi_logging.h +8 -5
  82. cocotb/share/include/vpi_user_ext.h +4 -26
  83. cocotb/share/lib/verilator/verilator.cpp +80 -67
  84. cocotb/simtime.py +230 -0
  85. cocotb/simulator.cp312-win32.exp +0 -0
  86. cocotb/simulator.cp312-win32.lib +0 -0
  87. cocotb/simulator.cp312-win32.pyd +0 -0
  88. cocotb/simulator.pyi +107 -0
  89. cocotb/task.py +478 -213
  90. cocotb/triggers.py +55 -1092
  91. cocotb/types/__init__.py +28 -47
  92. cocotb/types/_abstract_array.py +151 -0
  93. cocotb/types/_array.py +295 -0
  94. cocotb/types/_indexing.py +17 -0
  95. cocotb/types/_logic.py +333 -0
  96. cocotb/types/_logic_array.py +868 -0
  97. cocotb/types/{range.py → _range.py} +47 -48
  98. cocotb/types/_resolve.py +76 -0
  99. cocotb/utils.py +58 -646
  100. cocotb-2.0.0rc2.dist-info/METADATA +60 -0
  101. cocotb-2.0.0rc2.dist-info/RECORD +146 -0
  102. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +1 -1
  103. cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
  104. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info/licenses}/LICENSE +1 -0
  105. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +1 -0
  106. cocotb_tools/__init__.py +0 -0
  107. cocotb_tools/_coverage.py +33 -0
  108. cocotb_tools/_vendor/__init__.py +3 -0
  109. cocotb_tools/check_results.py +65 -0
  110. cocotb_tools/combine_results.py +152 -0
  111. cocotb_tools/config.py +241 -0
  112. {cocotb → cocotb_tools}/ipython_support.py +29 -22
  113. cocotb_tools/makefiles/Makefile.deprecations +27 -0
  114. {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +77 -55
  115. {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +16 -33
  116. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
  117. cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
  118. cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
  119. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
  120. cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
  121. cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
  122. cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
  123. cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
  124. cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
  125. cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
  126. cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
  127. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
  128. cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
  129. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
  130. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
  131. cocotb_tools/py.typed +0 -0
  132. cocotb_tools/runner.py +1868 -0
  133. cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
  134. pygpi/entry.py +34 -18
  135. pygpi/py.typed +0 -0
  136. cocotb/ANSI.py +0 -92
  137. cocotb/binary.py +0 -858
  138. cocotb/config.py +0 -289
  139. cocotb/decorators.py +0 -332
  140. cocotb/log.py +0 -303
  141. cocotb/memdebug.py +0 -35
  142. cocotb/outcomes.py +0 -56
  143. cocotb/runner.py +0 -1400
  144. cocotb/scheduler.py +0 -1099
  145. cocotb/share/makefiles/Makefile.deprecations +0 -12
  146. cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
  147. cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
  148. cocotb/share/makefiles/simulators/Makefile.ius +0 -125
  149. cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
  150. cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
  151. cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
  152. cocotb/types/array.py +0 -309
  153. cocotb/types/logic.py +0 -292
  154. cocotb/types/logic_array.py +0 -298
  155. cocotb/wavedrom.py +0 -199
  156. cocotb/xunit_reporter.py +0 -80
  157. cocotb-1.9.2.dist-info/METADATA +0 -168
  158. cocotb-1.9.2.dist-info/RECORD +0 -121
  159. cocotb-1.9.2.dist-info/entry_points.txt +0 -2
  160. /cocotb/{_vendor/__init__.py → py.typed} +0 -0
  161. {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
cocotb/handle.py CHANGED
@@ -1,48 +1,71 @@
1
- #!/usr/bin/env python
2
-
1
+ # Copyright cocotb contributors
3
2
  # Copyright (c) 2013 Potential Ventures Ltd
4
3
  # Copyright (c) 2013 SolarFlare Communications Inc
5
- # All rights reserved.
6
- #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
9
- # * Redistributions of source code must retain the above copyright
10
- # notice, this list of conditions and the following disclaimer.
11
- # * Redistributions in binary form must reproduce the above copyright
12
- # notice, this list of conditions and the following disclaimer in the
13
- # documentation and/or other materials provided with the distribution.
14
- # * Neither the name of Potential Ventures Ltd,
15
- # SolarFlare Communications Inc nor the
16
- # names of its contributors may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- # DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
23
- # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
-
30
- # -*- coding: utf-8 -*-
31
-
32
- import ctypes
4
+ # Licensed under the Revised BSD License, see LICENSE for details.
5
+ # SPDX-License-Identifier: BSD-3-Clause
6
+
33
7
  import enum
34
- import warnings
8
+ import logging
9
+ import os
10
+ import re
11
+ from abc import ABC, abstractmethod
35
12
  from functools import lru_cache
36
- from typing import Optional
13
+ from logging import Logger
14
+ from typing import (
15
+ Any,
16
+ Callable,
17
+ Dict,
18
+ Generic,
19
+ Iterable,
20
+ Iterator,
21
+ NoReturn,
22
+ Optional,
23
+ Sequence,
24
+ Tuple,
25
+ Type,
26
+ TypeVar,
27
+ Union,
28
+ cast,
29
+ )
37
30
 
38
31
  import cocotb
39
32
  from cocotb import simulator
40
- from cocotb.binary import BinaryRepresentation, BinaryValue
41
- from cocotb.log import SimLog
42
- from cocotb.types import Logic, LogicArray
43
-
44
- # Only issue a warning for each deprecated attribute access
45
- _deprecation_warned = set()
33
+ from cocotb._base_triggers import Event
34
+ from cocotb._deprecation import deprecated
35
+ from cocotb._gpi_triggers import (
36
+ Edge,
37
+ FallingEdge,
38
+ ReadOnly,
39
+ ReadWrite,
40
+ RisingEdge,
41
+ ValueChange,
42
+ current_gpi_trigger,
43
+ )
44
+ from cocotb._py_compat import cached_property, insertion_ordered_dict
45
+ from cocotb._utils import DocIntEnum
46
+ from cocotb.task import Task
47
+ from cocotb.types import Array, Logic, LogicArray, Range
48
+ from cocotb.types._indexing import do_indexing_changed_warning, indexing_changed
49
+
50
+ __all__ = (
51
+ "ArrayObject",
52
+ "Deposit",
53
+ "EnumObject",
54
+ "Force",
55
+ "Freeze",
56
+ "GPIDiscovery",
57
+ "HierarchyArrayObject",
58
+ "HierarchyObject",
59
+ "Immediate",
60
+ "IntegerObject",
61
+ "LogicArrayObject",
62
+ "LogicObject",
63
+ "RealObject",
64
+ "Release",
65
+ "SimHandleBase",
66
+ "StringObject",
67
+ "ValueObjectBase",
68
+ )
46
69
 
47
70
 
48
71
  class _Limits(enum.IntEnum):
@@ -52,7 +75,7 @@ class _Limits(enum.IntEnum):
52
75
 
53
76
 
54
77
  @lru_cache(maxsize=None)
55
- def _value_limits(n_bits, limits):
78
+ def _value_limits(n_bits: int, limits: _Limits) -> Tuple[int, int]:
56
79
  """Calculate min/max for given number of bits and limits class"""
57
80
  if limits == _Limits.SIGNED_NBIT:
58
81
  min_val = -(2 ** (n_bits - 1))
@@ -67,58 +90,52 @@ def _value_limits(n_bits, limits):
67
90
  return min_val, max_val
68
91
 
69
92
 
70
- class SimHandleBase:
93
+ class SimHandleBase(ABC):
71
94
  """Base class for all simulation objects.
72
95
 
73
- We maintain a handle which we can use for GPI calls.
96
+ All simulation objects are hashable and equatable by identity.
97
+
98
+ .. code-block:: python
99
+
100
+ a = dut.clk
101
+ b = dut.clk
102
+ assert a == b
103
+
104
+ .. versionchanged:: 2.0
105
+ ``get_definition_name()`` and ``get_definition_file()`` were removed in favor of :meth:`_def_name` and :meth:`_def_file`, respectively.
74
106
  """
75
107
 
76
- # For backwards compatibility we support a mapping of old member names
77
- # which may alias with the simulator hierarchy. In these cases the
78
- # simulator result takes priority, only falling back to the python member
79
- # if there is no colliding object in the elaborated design.
80
- _compat_mapping = {
81
- "log": "_log",
82
- "fullname": "_fullname",
83
- "name": "_name",
84
- }
85
-
86
- def __init__(self, handle, path):
87
- """
88
- .. Constructor. This RST comment works around sphinx-doc/sphinx#6885
108
+ @abstractmethod
109
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
110
+ self._handle = handle
111
+ self._path: str = self._name if path is None else path
112
+ """The path to this handle, or its name if this is the root handle.
89
113
 
90
- Args:
91
- handle (int): The GPI handle to the simulator object.
92
- path (str): Path to this handle, ``None`` if root.
114
+ :meta public:
93
115
  """
94
- self._handle = handle
95
- self._len: Optional[int] = None
96
- """The "length" (the number of elements) of the underlying object. For vectors this is the number of bits."""
97
- self._sub_handles: dict = {}
98
- """Dictionary of this handle's children."""
99
- self._invalid_sub_handles: set = set()
100
- """Python :class:`set` of invalid queries, for caching purposes."""
101
- self._name: str = self._handle.get_name_string()
116
+
117
+ @cached_property
118
+ def _name(self) -> str:
102
119
  """The name of an object.
103
120
 
104
121
  :meta public:
105
122
  """
106
- self._type: str = self._handle.get_type_string()
123
+ return self._handle.get_name_string()
124
+
125
+ @cached_property
126
+ def _type(self) -> str:
107
127
  """The type of an object as a string.
108
128
 
109
129
  :meta public:
110
130
  """
111
- self._fullname: str = self._name + "(%s)" % self._type
112
- """The name of an object with its type appended in parentheses."""
113
- self._path: str = self._name if path is None else path
114
- """The path to this handle, or its name if this is the root handle.
131
+ return self._handle.get_type_string()
115
132
 
116
- :meta public:
117
- """
118
- self._log = SimLog("cocotb.%s" % self._name)
119
- """The logging object."""
120
- self._log.debug("Created")
121
- self._def_name: str = self._handle.get_definition_name()
133
+ @cached_property
134
+ def _log(self) -> Logger:
135
+ return logging.getLogger(f"cocotb.{self._name}")
136
+
137
+ @cached_property
138
+ def _def_name(self) -> str:
122
139
  """The name of a GPI object's definition.
123
140
 
124
141
  This is the value of ``vpiDefName`` for VPI, ``vhpiNameP`` for VHPI,
@@ -127,7 +144,10 @@ class SimHandleBase:
127
144
 
128
145
  :meta public:
129
146
  """
130
- self._def_file: str = self._handle.get_definition_file()
147
+ return self._handle.get_definition_name()
148
+
149
+ @cached_property
150
+ def _def_file(self) -> str:
131
151
  """The name of the file that sources the object's definition.
132
152
 
133
153
  This is the value of ``vpiDefFile`` for VPI, ``vhpiFileNameP`` for VHPI,
@@ -136,43 +156,17 @@ class SimHandleBase:
136
156
 
137
157
  :meta public:
138
158
  """
159
+ return self._handle.get_definition_file()
139
160
 
140
- def get_definition_name(self):
141
- return self._def_name
142
-
143
- def get_definition_file(self):
144
- return self._def_file
145
-
146
- def __hash__(self):
161
+ def __hash__(self) -> int:
147
162
  return hash(self._handle)
148
163
 
149
- def __len__(self):
150
- """Return the "length" (the number of elements) of the underlying object.
151
-
152
- For vectors this is the number of bits.
153
- """
154
- if self._len is None:
155
- self._len = self._handle.get_num_elems()
156
- return self._len
157
-
158
- def __eq__(self, other):
159
- """Compare equality of handles.
160
-
161
- Example usage::
162
-
163
- if clk == dut.clk:
164
- do_something()
165
- """
164
+ def __eq__(self, other: object) -> bool:
166
165
  if not isinstance(other, SimHandleBase):
167
166
  return NotImplemented
168
167
  return self._handle == other._handle
169
168
 
170
- def __ne__(self, other):
171
- if not isinstance(other, SimHandleBase):
172
- return NotImplemented
173
- return self._handle != other._handle
174
-
175
- def __repr__(self):
169
+ def __repr__(self) -> str:
176
170
  desc = self._path
177
171
  defname = self._def_name
178
172
  if defname:
@@ -182,78 +176,104 @@ class SimHandleBase:
182
176
  desc += " (at " + deffile + ")"
183
177
  return type(self).__qualname__ + "(" + desc + ")"
184
178
 
185
- def __str__(self):
186
- return self._path
187
-
188
- def __setattr__(self, name, value):
189
- if name in self._compat_mapping:
190
- if name not in _deprecation_warned:
191
- warnings.warn(
192
- "Use of attribute {!r} is deprecated, use {!r} instead".format(
193
- name, self._compat_mapping[name]
194
- ),
195
- DeprecationWarning,
196
- stacklevel=3,
197
- )
198
- _deprecation_warned.add(name)
199
- return setattr(self, self._compat_mapping[name], value)
200
- else:
201
- return object.__setattr__(self, name, value)
179
+ def __bool__(self) -> NoReturn:
180
+ raise TypeError(
181
+ "This object cannot be cast to bool or used in conditionals. Use `obj is not None` check in conditionals."
182
+ )
202
183
 
203
- def __getattr__(self, name):
204
- if name in self._compat_mapping:
205
- if name not in _deprecation_warned:
206
- warnings.warn(
207
- "Use of attribute {!r} is deprecated, use {!r} instead".format(
208
- name, self._compat_mapping[name]
209
- ),
210
- DeprecationWarning,
211
- stacklevel=3,
212
- )
213
- _deprecation_warned.add(name)
214
- return getattr(self, self._compat_mapping[name])
215
- else:
216
- return object.__getattribute__(self, name)
217
184
 
185
+ class _RangeableObjectMixin(SimHandleBase):
186
+ """Base class for simulation objects that have a range."""
187
+
188
+ @cached_property
189
+ def range(self) -> Range:
190
+ """Return a :class:`~cocotb.types.Range` over the indexes of the array/vector."""
191
+ left, right, direction = self._handle.get_range()
192
+ if direction == simulator.RANGE_NO_DIR:
193
+ raise RuntimeError("Expected range to have a direction but got none!")
194
+ return Range(left, "to" if direction == simulator.RANGE_UP else "downto", right)
195
+
196
+ @property
197
+ def left(self) -> int:
198
+ """Return the leftmost index in the array/vector."""
199
+ return self.range.left
200
+
201
+ @property
202
+ def direction(self) -> str:
203
+ """Return the direction (``"to"``/``"downto"``) of indexes in the array/vector."""
204
+ return self.range.direction
205
+
206
+ @property
207
+ def right(self) -> int:
208
+ """Return the rightmost index in the array/vector."""
209
+ return self.range.right
210
+
211
+ def __len__(self) -> int:
212
+ return len(self.range)
213
+
214
+
215
+ #: Type of keys (name or index) in HierarchyObjectBase.
216
+ KeyType = TypeVar("KeyType")
217
+
218
+ #: Subtype of :class:`SimHandleBase` returned when iterating or indexing a :class:`HierarchyArrayObject`.
219
+ HierarchyChildObjectT = TypeVar("HierarchyChildObjectT", bound=SimHandleBase)
218
220
 
219
- class RegionObject(SimHandleBase):
220
- """A region object, such as a scope or namespace.
221
221
 
222
- Region objects don't have values, they are effectively scopes or namespaces.
222
+ class GPIDiscovery(DocIntEnum):
223
+ """Simulator object discovery strategy."""
224
+
225
+ AUTO = (0, "Automatic discovery using all registered interfaces.")
226
+ NATIVE = (1, "Native discovery using only the parent's native interface.")
227
+
228
+
229
+ class _HierarchyObjectBase(SimHandleBase, Generic[KeyType]):
230
+ """Base class for hierarchical simulation objects.
231
+
232
+ Hierarchical objects don't have values, they are just scopes/namespaces of other objects.
233
+ This includes array-like hierarchical structures like "generate loops"
234
+ and named hierarchical structures like "generate blocks" or "module"/"entity" instantiations.
235
+
236
+ This base class defines logic to discover, cache, and inspect child objects.
237
+ It provides a :class:`dict`-like interface for doing so.
238
+
239
+ :meth:`_keys`, :meth:`_values`, and :meth:`_items` mimic their :class:`dict` counterparts.
240
+ You can also iterate over an object, which returns child objects, not keys like in :class:`dict`;
241
+ and can check the :func:`len`.
242
+
243
+ See :class:`HierarchyObject` and :class:`HierarchyArrayObject` for examples.
223
244
  """
224
245
 
225
- def __init__(self, handle, path):
226
- SimHandleBase.__init__(self, handle, path)
227
- self._discovered = False # True if this object has already been discovered
228
-
229
- def __iter__(self):
230
- """Iterate over all known objects in this layer of hierarchy."""
231
- if not self._discovered:
232
- self._discover_all()
233
-
234
- for name, handle in self._sub_handles.items():
235
- if isinstance(handle, list):
236
- self._log.debug("Found index list length %d", len(handle))
237
- for subindex, subhdl in enumerate(handle):
238
- if subhdl is None:
239
- self._log.warning(
240
- "Index %d doesn't exist in %s.%s",
241
- subindex,
242
- self._name,
243
- name,
244
- )
245
- continue
246
- self._log.debug(
247
- "Yielding index %d from %s (%s)", subindex, name, type(subhdl)
248
- )
249
- yield subhdl
250
- else:
251
- self._log.debug(
252
- "Yielding %s of type %s (%s)", name, type(handle), handle._path
253
- )
254
- yield handle
246
+ @abstractmethod
247
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
248
+ super().__init__(handle, path)
249
+ self._sub_handles: Dict[KeyType, SimHandleBase] = {}
250
+ self._discovered = False
251
+
252
+ def _keys(self) -> Iterable[KeyType]:
253
+ """Iterate over the keys (name or index) of the child objects.
254
+
255
+ :meta public:
256
+ """
257
+ self._discover_all()
258
+ return self._sub_handles.keys()
259
+
260
+ def _values(self) -> Iterable[SimHandleBase]:
261
+ """Iterate over the child objects.
262
+
263
+ :meta public:
264
+ """
265
+ self._discover_all()
266
+ return self._sub_handles.values()
267
+
268
+ def _items(self) -> Iterable[Tuple[KeyType, SimHandleBase]]:
269
+ """Iterate over ``(key, object)`` tuples of child objects.
270
+
271
+ :meta public:
272
+ """
273
+ self._discover_all()
274
+ return self._sub_handles.items()
255
275
 
256
- def _discover_all(self):
276
+ def _discover_all(self) -> None:
257
277
  """When iterating or performing IPython tab completion, we run through ahead of
258
278
  time and discover all possible children, populating the :any:`_sub_handles`
259
279
  mapping. Hierarchy can't change after elaboration so we only have to
@@ -261,914 +281,1471 @@ class RegionObject(SimHandleBase):
261
281
  """
262
282
  if self._discovered:
263
283
  return
264
- self._log.debug("Discovering all on %s", self._name)
284
+
265
285
  for thing in self._handle.iterate(simulator.OBJECTS):
266
286
  name = thing.get_name_string()
267
- path = self._child_path(name)
268
- try:
269
- hdl = SimHandle(thing, path)
270
- except NotImplementedError as e:
271
- self._log.debug("%s", e)
272
- continue
273
287
 
288
+ # translate HDL name into a consistent key name
274
289
  try:
275
290
  key = self._sub_handle_key(name)
276
291
  except ValueError:
277
- self._log.debug(
292
+ self._log.exception(
278
293
  "Unable to translate handle >%s< to a valid _sub_handle key",
279
- hdl._name,
294
+ name,
280
295
  )
281
296
  continue
282
297
 
298
+ # compute a full path using the key name
299
+ path = self._child_path(key)
300
+
301
+ # attempt to create the child object
302
+ try:
303
+ hdl = _make_sim_object(thing, path)
304
+ except NotImplementedError:
305
+ self._log.exception(
306
+ "Unable to construct a SimHandle object for %s", path
307
+ )
308
+ continue
309
+
310
+ # add to cache
283
311
  self._sub_handles[key] = hdl
284
312
 
285
313
  self._discovered = True
286
314
 
287
- def _child_path(self, name) -> str:
288
- """Return a string of the path of the child :any:`SimHandle` for a given *name*."""
289
- return self._path + "." + name
315
+ def _get(
316
+ self, key: KeyType, discovery_method: GPIDiscovery = GPIDiscovery.AUTO
317
+ ) -> Union[SimHandleBase, None]:
318
+ """Query the simulator for an object with the specified *key*.
290
319
 
291
- def _sub_handle_key(self, name):
292
- """Translate the handle name to a key to use in :any:`_sub_handles` dictionary."""
293
- return name.split(".")[-1]
320
+ Like Python's native dictionary ``get``-function, this returns ``None`` if the object
321
+ is not found instead of raising an :exc:`AttributeError`.
294
322
 
295
- def __dir__(self):
296
- """Permits IPython tab completion to work."""
297
- self._discover_all()
298
- return super().__dir__() + [str(k) for k in self._sub_handles]
323
+ Generally, use the ``handle[child_name]`` syntax instead, unless you have to change the
324
+ *discovery_method* or want to check for optional signals.
299
325
 
326
+ :meta public:
300
327
 
301
- class HierarchyObject(RegionObject):
302
- """Hierarchy objects are namespace/scope objects."""
328
+ Args:
329
+ key: The child object by name.
330
+ discovery_method: Optional selection of discovery strategy. :data:`~cocotb.handle.GPIDiscovery.AUTO` by default.
303
331
 
304
- def __get_sub_handle_by_name(self, name):
332
+ Returns:
333
+ The child object, or ``None`` if not found.
334
+ """
335
+ # try to use cached value
305
336
  try:
306
- return self._sub_handles[name]
337
+ return self._sub_handles[key]
307
338
  except KeyError:
308
339
  pass
309
340
 
310
- # Cache to avoid a call to the simulator if we already know the name is
311
- # invalid. Unclear if we care, but we had this before.
312
- if name in self._invalid_sub_handles:
341
+ # try to get value from GPI
342
+ new_handle = self._get_handle_by_key(key, discovery_method)
343
+ if new_handle is None:
313
344
  return None
314
345
 
315
- new_handle = self._handle.get_handle_by_name(name)
316
-
317
- if not new_handle:
318
- self._invalid_sub_handles.add(name)
319
- return None
346
+ # if successful, construct and cache
347
+ sub_handle = _make_sim_object(new_handle, self._child_path(key))
348
+ self._sub_handles[key] = sub_handle
320
349
 
321
- sub_handle = SimHandle(new_handle, self._child_path(name))
322
- self._sub_handles[name] = sub_handle
323
350
  return sub_handle
324
351
 
325
- def __setattr__(self, name, value):
326
- """Provide transparent access to signals via the hierarchy.
352
+ @abstractmethod
353
+ def _get_handle_by_key(
354
+ self, key: KeyType, discovery_method: GPIDiscovery
355
+ ) -> Union[simulator.gpi_sim_hdl, None]:
356
+ """Get child object by key from the simulator.
327
357
 
328
- Slightly hacky version of operator overloading in Python.
358
+ Args:
359
+ key: The key of the child object.
360
+ discovery_method: How to discover the object using the GPI.
329
361
 
330
- Raise an :exc:`AttributeError` if users attempt to create new members which
331
- don't exist in the design.
362
+ Returns:
363
+ A raw simulator handle for the child object at the given key, or ``None``.
332
364
  """
333
365
 
334
- # private attributes pass through directly
335
- if name.startswith("_"):
336
- return SimHandleBase.__setattr__(self, name, value)
337
-
338
- # then try handles
339
- sub = self.__get_sub_handle_by_name(name)
340
- if sub is not None:
341
- warnings.warn(
342
- "Setting values on handles using the ``dut.handle = value`` syntax is deprecated. "
343
- "Instead use the ``handle.value = value`` syntax",
344
- DeprecationWarning,
345
- stacklevel=2,
346
- )
347
- sub.value = value
348
- return
366
+ @abstractmethod
367
+ def _child_path(self, key: KeyType) -> str:
368
+ """Compute the path string of a child object at the given key.
349
369
 
350
- # compat behavior
351
- if name in self._compat_mapping:
352
- return SimHandleBase.__setattr__(self, name, value)
370
+ Args:
371
+ key: The key of the child object.
372
+
373
+ Returns:
374
+ A path string of the child object at the a given key.
375
+ """
376
+
377
+ @abstractmethod
378
+ def _sub_handle_key(self, name: str) -> KeyType:
379
+ """Translate a discovered child object name into a key.
380
+
381
+ Args:
382
+ name: The GPI name of the child object.
353
383
 
354
- raise AttributeError(f"{self._name} contains no object named {name}")
384
+ Returns:
385
+ A unique key for the child object.
355
386
 
356
- def __getattr__(self, name):
357
- """Query the simulator for an object with the specified name
358
- and cache the result to build a tree of objects.
387
+ Raises:
388
+ ValueError: if unable to translate handle to a valid _sub_handle key.
359
389
  """
390
+
391
+ def __iter__(self) -> Iterator[SimHandleBase]:
392
+ return iter(self._values())
393
+
394
+ def __len__(self) -> int:
395
+ self._discover_all()
396
+ return len(self._sub_handles)
397
+
398
+ def __dir__(self) -> Iterable[str]:
399
+ """Permits IPython tab completion and debuggers to work."""
400
+ self._discover_all()
401
+ return set(super().__dir__()) | {str(k) for k in self._keys()}
402
+
403
+
404
+ class HierarchyObject(_HierarchyObjectBase[str]):
405
+ r"""A simulation object that is a name-indexed collection of hierarchical simulation objects.
406
+
407
+ Inherits from :class:`SimHandleBase`.
408
+
409
+ This class is used for named hierarchical structures, such as "generate blocks" or "module"/"entity" instantiations.
410
+
411
+ Children under this structure are found by using the name of the child with either the attribute syntax or index syntax.
412
+ For example, if in your :envvar:`COCOTB_TOPLEVEL` entity/module you have a signal/net named ``count``, you could do either of the following.
413
+
414
+ .. code-block:: python
415
+
416
+ dut.count # attribute syntax
417
+ dut["count"] # index syntax
418
+
419
+ Attribute syntax is usually shorter and easier to read, and is more common.
420
+ However, it has limitations:
421
+
422
+ - the name cannot start with a number
423
+ - the name cannot start with a ``_`` character
424
+ - the name can only contain ASCII letters, numbers, and the ``_`` character.
425
+
426
+ Any possible name of an object is supported with the index syntax,
427
+ but it can be more verbose.
428
+
429
+ Accessing a non-existent child with attribute syntax results in an :class:`AttributeError`,
430
+ and accessing a non-existent child with index syntax results in a :class:`KeyError`.
431
+
432
+ .. note::
433
+ If you need to access signals/nets that start with ``_``,
434
+ or use escaped identifier (Verilog) or extended identifier (VHDL) characters,
435
+ you have to use the index syntax.
436
+ Accessing escaped/extended identifiers requires enclosing the name
437
+ with leading and trailing double backslashes (``\\``).
438
+
439
+ .. code-block:: python
440
+
441
+ dut["_underscore_signal"]
442
+ dut["\\%extended !ID\\"]
443
+
444
+ Iteration yields all child objects in no particular order.
445
+ The :func:`len` function can be used to find the number of children.
446
+
447
+ .. code-block:: python
448
+
449
+ # discover all children in 'some_module'
450
+ total = 0
451
+ for handle in dut.some_module:
452
+ cocotb.log("Found %r", handle._path)
453
+ total += 1
454
+
455
+ # make sure we found them all
456
+ assert len(dut.some_module) == total
457
+ """
458
+
459
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
460
+ super().__init__(handle, path)
461
+
462
+ def __setattr__(self, name: str, value: object) -> None:
463
+ # private attributes pass through directly
360
464
  if name.startswith("_"):
361
- return SimHandleBase.__getattr__(self, name)
465
+ return object.__setattr__(self, name, value)
362
466
 
363
- handle = self.__get_sub_handle_by_name(name)
364
- if handle is not None:
365
- return handle
467
+ try:
468
+ getattr(self, name)
469
+ except AttributeError:
470
+ raise AttributeError(
471
+ f"Cannot set attribute {name!r} on simulation object {self._path}. No such object exists."
472
+ ) from None
473
+ else:
474
+ raise AttributeError(
475
+ f"Cannot set attribute {name!r} on simulation object {self._path}. Did you forget to add `.value`?"
476
+ )
366
477
 
367
- if name in self._compat_mapping:
368
- return SimHandleBase.__getattr__(self, name)
478
+ def __getattr__(self, name: str) -> SimHandleBase:
479
+ if name.startswith("_"):
480
+ return object.__getattribute__(self, name)
481
+
482
+ handle = self._get(name)
483
+ if handle is None:
484
+ raise AttributeError(f"{self._path} contains no child object named {name}")
485
+ return handle
369
486
 
370
- raise AttributeError(f"{self._name} contains no object named {name}")
487
+ def __getitem__(self, key: str) -> SimHandleBase:
488
+ handle = self._get(key)
489
+ if handle is None:
490
+ raise KeyError(f"{self._path} contains no child object named {key}")
491
+ return handle
371
492
 
372
- def _id(self, name, extended: bool = True):
373
- """Query the simulator for an object with the specified *name*,
374
- and cache the result to build a tree of objects.
493
+ @deprecated(
494
+ "Use `handle[child_name]` syntax instead. If extended identifiers are needed simply add a '\\' character before and after the name."
495
+ )
496
+ def _id(self, name: str, extended: bool = True) -> SimHandleBase:
497
+ """Query the simulator for an object with the specified *name*.
375
498
 
376
499
  If *extended* is ``True``, run the query only for VHDL extended identifiers.
377
500
  For Verilog, only ``extended=False`` is supported.
378
501
 
379
502
  :meta public:
503
+
504
+ Args:
505
+ name: The child object by name.
506
+ extended: If ``True``, treat the *name* as an extended identifier.
507
+
508
+ Returns:
509
+ The child object.
510
+
511
+ Raises:
512
+ AttributeError: If the child object is not found.
513
+
514
+ .. deprecated:: 2.0
515
+ Use ``handle[child_name]`` syntax instead.
516
+ If extended identifiers are needed simply add a ``\\`` character before and after the name.
517
+
380
518
  """
381
519
  if extended:
382
520
  name = "\\" + name + "\\"
383
521
 
384
- handle = self.__get_sub_handle_by_name(name)
385
- if handle is not None:
386
- return handle
522
+ handle = self._get(name)
523
+ if handle is None:
524
+ raise AttributeError(f"{self._path} contains no child object named {name}")
525
+ return handle
526
+
527
+ def _child_path(self, key: str) -> str:
528
+ delimiter = "::" if self._type == "GPI_PACKAGE" else "."
529
+ return f"{self._path}{delimiter}{key}"
530
+
531
+ def _sub_handle_key(self, name: str) -> str:
532
+ return name
533
+
534
+ def _get_handle_by_key(
535
+ self, key: str, discovery_method: GPIDiscovery
536
+ ) -> Union[simulator.gpi_sim_hdl, None]:
537
+ return self._handle.get_handle_by_name(key, discovery_method)
538
+
539
+
540
+ class HierarchyArrayObject(
541
+ _HierarchyObjectBase[int], _RangeableObjectMixin, Generic[HierarchyChildObjectT]
542
+ ):
543
+ """A simulation object that is an array of hierarchical simulation objects.
544
+
545
+ Inherits from :class:`SimHandleBase`.
546
+
547
+ This class is used for array-like hierarchical structures like "generate loops".
548
+
549
+ Children of this object are found by supplying a numerical index using index syntax.
550
+ For example, if you have a design with a generate loop ``gen_pipe_stages`` from the range ``0`` to ``7``:
551
+
552
+ .. code-block:: python
553
+
554
+ block_0 = dut.gen_pipe_stages[0]
555
+ block_7 = dut.gen_pipe_stages[7]
387
556
 
388
- raise AttributeError(f"{self._name} contains no object named {name}")
557
+ Accessing a non-existent child results in an :class:`IndexError`.
389
558
 
559
+ Iteration yields all child objects in order.
390
560
 
391
- class HierarchyArrayObject(RegionObject):
392
- """Hierarchy Arrays are containers of Hierarchy Objects."""
561
+ .. code-block:: python
393
562
 
394
- def _sub_handle_key(self, name):
395
- """Translate the handle name to a key to use in :any:`_sub_handles` dictionary."""
563
+ # set all 'reg's in each pipe stage to 0
564
+ for pipe_stage in dut.gen_pipe_stages:
565
+ pipe_stage.reg.value = 0
566
+
567
+ Use the :meth:`range` property if you want to iterate over the indexes.
568
+ The :func:`len` function can be used to find the number of elements.
569
+
570
+ .. code-block:: python
571
+
572
+ # set all 'reg's in each pipe stage to 0
573
+ for idx in dut.gen_pipe_stages.range:
574
+ dut.gen_pipe_stages[idx].reg.value = 0
575
+
576
+ # make sure we have all the pipe stages
577
+ assert len(dut.gen_pipe_stage) == len(dut.gen_pipe_stages.range)
578
+ """
579
+
580
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
581
+ super().__init__(handle, path)
582
+
583
+ def _sub_handle_key(self, name: str) -> int:
396
584
  # This is slightly hacky, but we need to extract the index from the name
397
585
  # See also GEN_IDX_SEP_* in VhpiImpl.h for the VHPI separators.
398
586
  #
399
587
  # FLI and VHPI: _name(X) where X is the index
400
588
  # VHPI(ALDEC): _name__X where X is the index
401
589
  # VPI: _name[X] where X is the index
402
- import re
403
-
404
- result = re.match(rf"{self._name}__(?P<index>\d+)$", name)
590
+ result = re.match(rf"{re.escape(self._name)}__(?P<index>\d+)$", name)
405
591
  if not result:
406
- result = re.match(rf"{self._name}\((?P<index>\d+)\)$", name, re.IGNORECASE)
592
+ result = re.match(
593
+ rf"{re.escape(self._name)}\((?P<index>\d+)\)$", name, re.IGNORECASE
594
+ )
407
595
  if not result:
408
- result = re.match(rf"{self._name}\[(?P<index>\d+)\]$", name)
596
+ result = re.match(rf"{re.escape(self._name)}\[(?P<index>\d+)\]$", name)
409
597
 
410
598
  if result:
411
599
  return int(result.group("index"))
412
600
  else:
413
601
  raise ValueError(f"Unable to match an index pattern: {name}")
414
602
 
415
- def __len__(self):
416
- """Return the "length" of the generate block."""
417
- if self._len is None:
418
- if not self._discovered:
419
- self._discover_all()
603
+ def _child_path(self, key: int) -> str:
604
+ return f"{self._path}[{key}]"
420
605
 
421
- self._len = len(self._sub_handles)
422
- return self._len
606
+ def _get_handle_by_key(
607
+ self, key: int, discovery_method: GPIDiscovery
608
+ ) -> Union[simulator.gpi_sim_hdl, None]:
609
+ if discovery_method is not GPIDiscovery.AUTO:
610
+ raise NotImplementedError(
611
+ f"Only GPIDiscovery.AUTO is supported for {type(self).__qualname__} right now"
612
+ )
613
+ return self._handle.get_handle_by_index(key)
423
614
 
424
- def __getitem__(self, index):
425
- if isinstance(index, slice):
426
- raise IndexError("Slice indexing is not supported")
427
- if index in self._sub_handles:
428
- return self._sub_handles[index]
429
- new_handle = self._handle.get_handle_by_index(index)
430
- if not new_handle:
431
- raise IndexError("%s contains no object at index %d" % (self._name, index))
432
- path = self._path + "[" + str(index) + "]"
433
- self._sub_handles[index] = SimHandle(new_handle, path)
434
- return self._sub_handles[index]
615
+ def __getitem__(self, key: int) -> HierarchyChildObjectT:
616
+ if isinstance(key, slice):
617
+ raise TypeError("Slice indexing is not supported")
618
+
619
+ handle = self._get(key)
620
+ if handle is None:
621
+ raise IndexError(f"{self._path} contains no child object at index {key}")
622
+ return cast("HierarchyChildObjectT", handle)
623
+
624
+ # ideally `__len__` could be implemented in terms of `range`, but `range` doesn't work universally.
435
625
 
436
- def _child_path(self, name):
437
- """Return a string of the path of the child :any:`SimHandle` for a given name."""
438
- index = self._sub_handle_key(name)
439
- return self._path + "[" + str(index) + "]"
626
+ def __iter__(self) -> Iterator[HierarchyChildObjectT]:
627
+ # must use `sorted(self._keys())` instead of the range because `range` doesn't work universally.
628
+ for i in sorted(self._keys()):
629
+ yield self[i]
440
630
 
441
- def __setitem__(self, index, value):
442
- raise TypeError("Not permissible to set %s at index %d" % (self._name, index))
443
631
 
632
+ class _GPISetAction(enum.Enum):
633
+ DEPOSIT = 0
634
+ FORCE = 1
635
+ RELEASE = 2
636
+ NO_DELAY = 3
637
+ OLD_IMMEDIATE = 0
444
638
 
445
- class _AssignmentResult:
639
+
640
+ _ValueT = TypeVar("_ValueT")
641
+
642
+
643
+ class Deposit(Generic[_ValueT]):
644
+ r""":term:`Inertially deposit <inertial deposit>` the given value on a simulator object.
645
+
646
+ If another :term:`deposit` comes after this deposit, the newer deposit overwrites the old value.
647
+ If an HDL process is :term:`driving` the signal/net/register where a deposit from cocotb is made,
648
+ the deposited value will be overwritten at the end of the next delta cycle,
649
+ essentially causing a single delta cycle "glitch" in the waveform.
650
+
651
+ .. note::
652
+ VHDL applies writes according to their definition.
653
+ ``signal`` writes are set inertially, regardless of using this class;
654
+ while ``variable`` writes are set immediately, regardless of using this class.
446
655
  """
447
- An object that exists solely to provide an error message if the caller
448
- is not aware of cocotb's meaning of ``<=``.
656
+
657
+ def __init__(self, value: _ValueT) -> None:
658
+ self.value = value
659
+
660
+
661
+ class Force(Generic[_ValueT]):
662
+ r""":term:`Force <force>` the given value on a simulator object immediately.
663
+
664
+ Further :term:`deposits <deposit>` from cocotb or :term:`drives <driving>` from HDL processes
665
+ do not cause the value to change until the handle is :term:`released <release>` by cocotb or HDL code.
666
+ Further :term:`forces <force>` will overwrite the value and leave the value forced.
667
+
668
+ .. note::
669
+ VHDL applies writes according to their definition.
670
+ ``signal`` writes are set inertially, regardless of using this class;
671
+ while ``variable`` writes are set immediately, regardless of using this class.
672
+
673
+ .. note::
674
+ Verilog :class:`!Force`\ s are always immediate.
675
+ This also means that if there are multiple cocotb Tasks or multiple ``always`` blocks writing to the same object,
676
+ the resulting value is non-deterministic.
677
+
678
+ .. note::
679
+ Issuing a :class:`!Force` and :class:`Release` in the same evaluation cycle in VHDL will result in the :class:`!Force` "winning".
449
680
  """
450
681
 
451
- def __init__(self, signal, value):
452
- self._signal = signal
453
- self._value = value
682
+ def __init__(self, value: _ValueT) -> None:
683
+ self.value = value
454
684
 
455
- def __bool__(self):
456
- raise TypeError(
457
- "Attempted to use `{0._signal!r} <= {0._value!r}` (a cocotb "
458
- "delayed write) as if it were a numeric comparison. To perform "
459
- "comparison, use `{0._signal!r}.value <= {0._value!r}` instead.".format(
460
- self
461
- )
462
- )
463
685
 
686
+ class Freeze:
687
+ r""":term:`Force <force>` the simulator object with its current value.
464
688
 
465
- class NonHierarchyObject(SimHandleBase):
466
- """Common base class for all non-hierarchy objects."""
689
+ Useful if you have done a :term:`deposit` and later decide to lock the value from changing.
690
+ Does not change the current value of the simulation object.
691
+ See :class:`Force` for information on behavior after this write completes.
467
692
 
468
- def __iter__(self):
469
- return iter(())
693
+ .. note::
694
+ VHDL applies writes according to their definition.
695
+ ``signal`` writes are set inertially, regardless of using this class;
696
+ while ``variable`` writes are set immediately, regardless of using this class.
470
697
 
471
- @property
472
- def value(self):
473
- """The value of this simulation object.
698
+ .. note::
699
+ Verilog :class:`Force`\ s are always immediate.
700
+ This also means that if there are multiple cocotb Tasks or multiple ``always`` blocks writing to the same object,
701
+ the resulting value is non-deterministic.
474
702
 
475
- .. note::
476
- When setting this property, the value is stored by the :class:`~cocotb.scheduler.Scheduler`
477
- and all stored values are written at the same time at the end of the current simulator time step.
703
+ .. note::
704
+ Issuing a :class:`!Force` and :class:`Release` in the same evaluation cycle in VHDL will result in the :class:`!Force` "winning".
705
+ """
478
706
 
479
- Use :meth:`setimmediatevalue` to set the value immediately.
480
- """
481
- raise TypeError(
482
- "Not permissible to get values of object {} of type {}".format(
483
- self._name, type(self)
484
- )
485
- )
486
707
 
487
- @value.setter
488
- def value(self, value):
489
- self._set_value(value, cocotb.scheduler._schedule_write)
708
+ class Release:
709
+ r""":term:`Release <release>` a :term:`forced <force>` simulation object.
490
710
 
491
- def setimmediatevalue(self, value):
492
- """Assign a value to this simulation object immediately."""
711
+ Does not change the current value of the simulation object.
712
+ See :class:`Deposit` for information on behavior after this write completes.
493
713
 
494
- def _call_now(handle, f, *args):
495
- f(*args)
714
+ .. note::
715
+ VHDL applies writes according to their definition.
716
+ ``signal`` writes are set inertially, regardless of using this class,
717
+ while ``variable`` writes are set immediately, regardless of using this class.
496
718
 
497
- self._set_value(value, _call_now)
719
+ .. note::
720
+ Verilog :class:`!Release`\ s are always immediate.
721
+ This also means that if there are multiple cocotb Tasks or multiple ``always`` blocks writing to the same object,
722
+ the resulting value is non-deterministic.
498
723
 
499
- def _set_value(self, value, call_sim):
500
- """This should be overriden in subclasses.
724
+ .. note::
725
+ Issuing a :class:`Force` and :class:`!Release` in the same evaluation cycle in VHDL will result in the :class:`!Force` "winning".
501
726
 
502
- This is used to implement both the setter for :attr:`value`, and the
503
- :meth:`setimmediatevalue` method.
727
+ .. note::
728
+ Releasing a ``reg`` or ``logic`` in Verilog will leave the current value.
729
+ Releasing a ``wire`` in Verilog will cause the value to be recomputed from the wire's drivers current values.
730
+ Releasing a ``signal`` in VHDL will cause the value to be recomputed from the signal's drivers current value.
731
+ Unconnected ``in`` ports and unconnected internal signals have no drivers and their value after :class:`!Release` will be ``U`` in VHDL and ``X`` in Verilog.
732
+ """
504
733
 
505
- ``call_sim(handle, f, *args)`` should be used to schedule simulator writes,
506
- rather than performing them directly as ``f(*args)``.
507
- """
508
- raise TypeError(
509
- "Not permissible to set values on object {} of type {}".format(
510
- self._name, type(self)
511
- )
512
- )
513
734
 
514
- def __le__(self, value):
515
- """Overload less-than-or-equal-to operator to provide an HDL-like shortcut.
735
+ class Immediate(Generic[_ValueT]):
736
+ """:term:`Deposit <no-delay deposit>` a value on a simulator object without delay.
516
737
 
517
- Example:
518
- >>> module.signal <= 2
519
- """
520
- warnings.warn(
521
- "Setting values on handles using the ``handle <= value`` syntax is deprecated. "
522
- "Instead use the ``handle.value = value`` syntax",
523
- DeprecationWarning,
524
- stacklevel=2,
525
- )
738
+ The value of the signal will be changed immediately
739
+ and should be able to be read back immediately following the write.
740
+ Otherwise, behaves like :class:`Deposit`.
741
+
742
+ .. note::
743
+ VHDL applies writes according to their definition.
744
+ ``signal`` writes are set inertially, regardless of using this class;
745
+ while ``variable`` writes are set immediately, regardless of using this class.
746
+
747
+ .. note::
748
+ In Verilog, because these writes are immediate,
749
+ if there are multiple cocotb Tasks or multiple ``always`` blocks writing to the same object,
750
+ the resulting value is non-deterministic.
751
+ """
752
+
753
+ def __init__(self, value: _ValueT) -> None:
526
754
  self.value = value
527
- return _AssignmentResult(self, value)
528
755
 
529
- def __eq__(self, other):
530
- """Equality comparator for non-hierarchy objects
531
756
 
532
- If ``other`` is not a :class:`SimHandleBase` instance the comparison
533
- uses the comparison method of the ``other`` object against our
534
- ``.value``.
535
- """
536
- if isinstance(other, SimHandleBase):
537
- return SimHandleBase.__eq__(self, other)
538
- return self.value == other
757
+ class _OldImmediate(Generic[_ValueT]):
758
+ def __init__(self, value: _ValueT) -> None:
759
+ self.value = value
760
+
761
+
762
+ _trust_inertial = bool(int(os.environ.get("COCOTB_TRUST_INERTIAL_WRITES", "0")))
763
+
764
+ # A dictionary of pending (write_func, args), keyed by handle.
765
+ # Writes are applied oldest to newest (least recently used).
766
+ # Only the last scheduled write to a particular handle in a timestep is performed.
767
+ _write_calls: "dict[ValueObjectBase[Any, Any], Tuple[Callable[[int, Any], None], _GPISetAction, Any]]" = insertion_ordered_dict()
768
+
769
+ _write_task: Union[Task[None], None] = None
770
+
771
+ _writes_pending = Event()
772
+
773
+
774
+ async def _do_writes() -> None:
775
+ """An internal task that schedules a ReadWrite to force writes to occur."""
776
+ while True:
777
+ await _writes_pending.wait()
778
+ await ReadWrite()
779
+
780
+
781
+ def _start_write_scheduler() -> None:
782
+ global _write_task
783
+ if _write_task is None:
784
+ _write_task = Task(_do_writes())
785
+ cocotb._scheduler_inst._schedule_task(_write_task)
786
+
787
+
788
+ def _stop_write_scheduler() -> None:
789
+ global _write_task
790
+ if _write_task is not None:
791
+ _write_task.cancel()
792
+ _write_task = None
793
+ _write_calls.clear()
794
+ _writes_pending.clear()
795
+
796
+
797
+ def _apply_scheduled_writes() -> None:
798
+ for func, action, value in _write_calls.values():
799
+ func(action.value, value)
800
+ _write_calls.clear()
801
+ _writes_pending.clear()
802
+
803
+
804
+ if _trust_inertial:
805
+
806
+ def _schedule_write(
807
+ handle: "ValueObjectBase[Any, Any]",
808
+ write_func: Callable[[int, _ValueT], None],
809
+ action: _GPISetAction,
810
+ value: _ValueT,
811
+ ) -> None:
812
+ # Trust the simulator and just write.
813
+ write_func(action.value, value)
814
+ else:
815
+
816
+ def _schedule_write(
817
+ handle: "ValueObjectBase[Any, Any]",
818
+ write_func: Callable[[int, _ValueT], None],
819
+ action: _GPISetAction,
820
+ value: _ValueT,
821
+ ) -> None:
822
+ if isinstance(current_gpi_trigger(), ReadWrite):
823
+ # If we are already in the ReadWrite phase, apply writes immediately as an optimization.
824
+ write_func(action.value, value)
825
+ elif action is _GPISetAction.DEPOSIT:
826
+ # Queue write for the beginning of the next ReadWrite phase because we can't trust the simulator. =(
827
+ if handle in _write_calls:
828
+ del _write_calls[handle]
829
+ _write_calls[handle] = (write_func, action, value)
830
+ _writes_pending.set()
831
+ else:
832
+ # If we are writing anything that isn't an inertial write, it must be applied immediately.
833
+ write_func(action.value, value)
834
+
835
+
836
+ #: Type returned by the :attr:`~ValueObjectBase.value` getter and returned by the :meth:`~ValueObjectBase.get` method.
837
+ ValueGetT = TypeVar("ValueGetT")
539
838
 
540
- def __ne__(self, other):
541
- if isinstance(other, SimHandleBase):
542
- return SimHandleBase.__ne__(self, other)
543
- return self.value != other
544
839
 
545
- # Re-define hash because we defined __eq__
546
- def __hash__(self):
547
- return SimHandleBase.__hash__(self)
840
+ #: Type accepted by the :attr:`~ValueObjectBase.value` setter and the :meth:`~ValueObjectBase.set` and :meth:`~ValueObjectBase.setimmediatevalue` methods.
841
+ ValueSetT = TypeVar("ValueSetT")
548
842
 
549
843
 
550
- class ConstantObject(NonHierarchyObject):
551
- """An object which has a value that can be read, but not set.
844
+ class ValueObjectBase(SimHandleBase, Generic[ValueGetT, ValueSetT]):
845
+ """Abstract base class for simulation objects that have a value.
552
846
 
553
- The value is cached in the class since it is fixed at elaboration
554
- time and won't change within a simulation.
847
+ Inherits from :class:`SimHandleBase`.
555
848
  """
556
849
 
557
- def __init__(self, handle, path, handle_type):
850
+ @property
851
+ def value(self) -> ValueGetT:
852
+ """Get or set the value of the simulation object.
853
+
854
+ :getter: Return the current value of the simulation object.
855
+
856
+ :setter:
857
+ Set the value of the simulation object.
858
+
859
+ See :class:`Deposit`, :class:`Force`, :class:`Freeze`, :class:`Release`, and :class:`Immediate`
860
+ for additional actions that can be taken when setting a value.
861
+ These are used like so:
862
+
863
+ .. code-block:: python
864
+
865
+ dut.handle.value = 1 # default Deposit action
866
+ dut.handle.value = Deposit(2)
867
+ dut.handle.value = Force(3)
868
+ dut.handle.value = Freeze()
869
+ dut.handle.value = Release()
870
+ dut.handle.value = Immediate(4)
558
871
  """
872
+ return self.get()
873
+
874
+ @value.setter
875
+ def value(self, value: ValueSetT) -> None:
876
+ self.set(value)
877
+
878
+ @abstractmethod
879
+ def get(self) -> ValueGetT:
880
+ """Return the current value of the simulation object."""
881
+
882
+ def set(
883
+ self,
884
+ value: Union[
885
+ ValueSetT,
886
+ Deposit[ValueSetT],
887
+ Force[ValueSetT],
888
+ Freeze,
889
+ Release,
890
+ Immediate[ValueSetT],
891
+ ],
892
+ ) -> None:
893
+ """Set the value of the simulation object.
894
+
895
+ See :class:`Deposit`, :class:`Force`, :class:`Freeze`, :class:`Release`, and :class:`Immediate`
896
+ for additional actions that can be taken when setting a value.
897
+
559
898
  Args:
560
- handle (int): The GPI handle to the simulator object.
561
- path (str): Path to this handle, ``None`` if root.
562
- handle_type: The type of the handle
563
- (``simulator.INTEGER``, ``simulator.ENUM``,
564
- ``simulator.REAL``, ``simulator.STRING``).
899
+ value: The value to set the simulation object to. This may include type conversion.
900
+
901
+ Raises:
902
+ TypeError: If the *value* is of a type that cannot be converted to a simulation value,
903
+ or if the simulation object is immutable.
904
+ ValueError: If the *value* is of the correct type, but the value fails to convert.
565
905
  """
566
- NonHierarchyObject.__init__(self, handle, path)
567
- if handle_type in [simulator.INTEGER, simulator.ENUM]:
568
- self._value = self._handle.get_signal_val_long()
569
- elif handle_type == simulator.REAL:
570
- self._value = self._handle.get_signal_val_real()
571
- elif handle_type == simulator.STRING:
572
- self._value = self._handle.get_signal_val_str()
906
+ if isinstance(current_gpi_trigger(), ReadOnly):
907
+ raise RuntimeError("Attempting settings a value during the ReadOnly phase.")
908
+ if self.is_const:
909
+ raise TypeError("Attempted setting an immutable object")
910
+ if isinstance(value, Deposit):
911
+ self._set_value(value.value, _GPISetAction.DEPOSIT)
912
+ elif isinstance(value, Force):
913
+ self._set_value(value.value, _GPISetAction.FORCE)
914
+ elif isinstance(value, Freeze):
915
+ # We assume that ValueSetT >= ValueGetT
916
+ self._set_value(cast("ValueSetT", self.get()), _GPISetAction.FORCE)
917
+ elif isinstance(value, Release):
918
+ # We assume that ValueSetT >= ValueGetT
919
+ self._set_value(cast("ValueSetT", self.get()), _GPISetAction.RELEASE)
920
+ elif isinstance(value, Immediate):
921
+ self._set_value(value.value, _GPISetAction.NO_DELAY)
922
+ elif isinstance(value, _OldImmediate):
923
+ self._set_value(value.value, _GPISetAction.OLD_IMMEDIATE)
573
924
  else:
574
- val = self._handle.get_signal_val_binstr()
575
- self._value = BinaryValue(n_bits=len(val))
576
- try:
577
- self._value.binstr = val
578
- except Exception:
579
- self._value = val
925
+ self._set_value(value, _GPISetAction.DEPOSIT)
926
+
927
+ @deprecated(
928
+ "Use `handle.set(Immediate(...))` or `handle.value = Immediate(...)` instead."
929
+ )
930
+ def setimmediatevalue(
931
+ self,
932
+ value: Union[
933
+ ValueSetT,
934
+ Deposit[ValueSetT],
935
+ Force[ValueSetT],
936
+ Freeze,
937
+ Release,
938
+ Immediate[ValueSetT],
939
+ ],
940
+ ) -> None:
941
+ r"""Set the value of the simulation object immediately.
942
+
943
+ See :class:`Deposit`, :class:`Force`, :class:`Freeze`, :class:`Release`, and :class:`Immediate`
944
+ for additional actions that can be taken when setting a value.
945
+
946
+ Passing :class:`Deposit`\ s and unwrapped values is equivalent to passing an :class:`Immediate` to :meth:`set`.
947
+
948
+ .. deprecated:: 2.0
949
+ Use ``handle.set(Immediate(...))`` or ``handle.value = Immediate(...)`` instead.
950
+ This could result in a change in behavior because prior to version 2.0 this function did not set values immediately.
951
+ """
952
+ if isinstance(value, Deposit):
953
+ value = _OldImmediate(value.value) # type: ignore
954
+ elif not isinstance(value, (Force, Freeze, Release, Immediate)):
955
+ value = _OldImmediate(value) # type: ignore
956
+ self.set(value)
957
+
958
+ @cached_property
959
+ def is_const(self) -> bool:
960
+ """``True`` if the simulator object is immutable, e.g. a Verilog parameter or VHDL constant or generic."""
961
+ return self._handle.get_const()
962
+
963
+ @abstractmethod
964
+ def _set_value(
965
+ self,
966
+ value: ValueSetT,
967
+ action: _GPISetAction,
968
+ ) -> None:
969
+ """Schedule a write of the given value to a simulator object.
970
+
971
+ Conversion from multiple Python types into a type understood by the simulator is expected.
972
+ This is used to implement the :attr:`value` property setter, :meth:`setimmediatevalue`, and :meth:`set`.
973
+ Implementations can assume that handle isn't :meth:`const <is_const>`
974
+ and the Scheduler is not in the :data:`ReadOnly <cocotb.SimPhase.READ_ONLY>` phase.
580
975
 
581
- def __int__(self):
582
- return int(self.value)
976
+ Args:
977
+ value: A value used to set the handle.
978
+ action: Whether to deposit, force, or release the value on the handle.
979
+ """
583
980
 
584
- def __float__(self):
585
- return float(self.value)
586
981
 
587
- @NonHierarchyObject.value.getter
588
- def value(self):
589
- """The value of this simulation object."""
590
- return self._value
982
+ #: Type of value of each element in an :class:`ArrayObject`.
983
+ ElemValueT = TypeVar("ElemValueT")
591
984
 
592
- def __str__(self):
593
- if isinstance(self.value, bytes):
594
- StringObject._emit_str_warning(self)
595
- return self.value.decode("ascii")
596
- else:
597
- ModifiableObject._emit_str_warning(self)
598
- return str(self.value)
985
+ #: Subtype of :class:`ValueObjectBase` returned when iterating or indexing a :class:`ArrayObject`.
986
+ ChildObjectT = TypeVar("ChildObjectT", bound=ValueObjectBase[Any, Any])
599
987
 
600
988
 
601
- class NonHierarchyIndexableObject(NonHierarchyObject):
602
- """A non-hierarchy indexable object.
989
+ class ArrayObject(
990
+ ValueObjectBase[Array[ElemValueT], Union[Array[ElemValueT], Sequence[ElemValueT]]],
991
+ _RangeableObjectMixin,
992
+ Generic[ElemValueT, ChildObjectT],
993
+ ):
994
+ """A simulation object that is an array of value-having simulation objects.
603
995
 
604
- Getting and setting the current value of an array is done
605
- by iterating through sub-handles in left-to-right order.
996
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
606
997
 
607
- Given an HDL array ``arr``:
998
+ With Verilog simulation objects, unpacked vectors are mapped to this type.
999
+ Packed vectors are typically mapped to :class:`LogicArrayObject`.
608
1000
 
609
- +--------------+---------------------+--------------------------------------------------------------+
610
- | Verilog | VHDL | ``arr.value`` is equivalent to |
611
- +==============+=====================+==============================================================+
612
- | ``arr[4:7]`` | ``arr(4 to 7)`` | ``[arr[4].value, arr[5].value, arr[6].value, arr[7].value]`` |
613
- +--------------+---------------------+--------------------------------------------------------------+
614
- | ``arr[7:4]`` | ``arr(7 downto 4)`` | ``[arr[7].value, arr[6].value, arr[5].value, arr[4].value]`` |
615
- +--------------+---------------------+--------------------------------------------------------------+
1001
+ With VHDL simulation objects, all arrayed objects that aren't ``std_(u)logic``,
1002
+ ``sfixed``, ``ufixed``, ``unsigned``, ``signed``, and ``string`` are mapped to this type.
616
1003
 
617
- When setting the signal as in ``arr.value = ...``, the same index equivalence as noted in the table holds.
1004
+ These objects can be iterated over to yield child objects:
618
1005
 
619
- .. warning::
620
- Assigning a value to a sub-handle:
1006
+ .. code-block:: python
621
1007
 
622
- - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as a list then updates index 0)
623
- - **Correct**: ``dut.some_array[0].value = 1``
624
- """
1008
+ for child in dut.array_object:
1009
+ print(child._path)
625
1010
 
626
- def __init__(self, handle, path):
627
- NonHierarchyObject.__init__(self, handle, path)
628
- self._range = self._handle.get_range()
629
-
630
- def __setitem__(self, index, value):
631
- """Provide transparent assignment to indexed array handles."""
632
- warnings.warn(
633
- "Setting values on handles using the ``dut.handle[i] = value`` syntax is deprecated. "
634
- "Instead use the ``handle[i].value = value`` syntax",
635
- DeprecationWarning,
636
- stacklevel=2,
637
- )
638
- self[index].value = value
1011
+ A particular child can be retrieved using its index:
639
1012
 
640
- def __getitem__(self, index):
641
- if isinstance(index, slice):
642
- raise IndexError("Slice indexing is not supported")
643
- if self._range is None:
644
- raise IndexError(
645
- "%s is not indexable. Unable to get object at index %d"
646
- % (self._fullname, index)
647
- )
648
- if index in self._sub_handles:
649
- return self._sub_handles[index]
650
- new_handle = self._handle.get_handle_by_index(index)
651
- if not new_handle:
652
- raise IndexError(
653
- "%s contains no object at index %d" % (self._fullname, index)
654
- )
655
- path = self._path + "[" + str(index) + "]"
656
- self._sub_handles[index] = SimHandle(new_handle, path)
657
- return self._sub_handles[index]
1013
+ .. code-block:: python
658
1014
 
659
- def __iter__(self):
660
- if self._range is None:
661
- return
1015
+ child = dut.array_object[0]
662
1016
 
663
- self._log.debug("Iterating with range [%d:%d]", self._range[0], self._range[1])
664
- for i in self._range_iter(self._range[0], self._range[1]):
665
- try:
666
- result = self[i]
667
- yield result
668
- except IndexError:
669
- continue
1017
+ # reversed iteration over children
1018
+ for child_idx in reversed(dut.array_object.range):
1019
+ dut.array_object[child_idx]
1020
+ """
670
1021
 
671
- def _range_iter(self, left, right):
672
- if left > right:
673
- while left >= right:
674
- yield left
675
- left = left - 1
676
- else:
677
- while left <= right:
678
- yield left
679
- left = left + 1
1022
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1023
+ super().__init__(handle, path)
1024
+ self._sub_handles: Dict[int, ChildObjectT] = {}
680
1025
 
681
- @NonHierarchyObject.value.getter
682
- def value(self) -> list:
683
- # Don't use self.__iter__, because it has an unwanted `except IndexError`
684
- return [self[i].value for i in self._range_iter(self._range[0], self._range[1])]
1026
+ def get(self) -> Array[ElemValueT]:
1027
+ """Return the current value as an :class:`~cocotb.types.Array`.
685
1028
 
686
- def _set_value(self, value, call_sim):
687
- """Assign value from a list of same length to an array in left-to-right order.
688
- Index 0 of the list maps to the left-most index in the array.
1029
+ Given the HDL array ``arr``, getting the value is equivalent to:
689
1030
 
690
- See the docstring for this class.
1031
+ +--------------+---------------------+--------------------------------------------------------------------------------------------------+
1032
+ | Verilog | VHDL | ``arr.get()`` is equivalent to |
1033
+ +==============+=====================+==================================================================================================+
1034
+ | ``arr[4:7]`` | ``arr(4 to 7)`` | ``Array([arr[4].value, arr[5].value, arr[6].value, arr[7].value], range=Range(4, 'to', 7))`` |
1035
+ +--------------+---------------------+--------------------------------------------------------------------------------------------------+
1036
+ | ``arr[7:4]`` | ``arr(7 downto 4)`` | ``Array([arr[7].value, arr[6].value, arr[5].value, arr[4].value], range=Range(7, 'downto', 4))`` |
1037
+ +--------------+---------------------+--------------------------------------------------------------------------------------------------+
691
1038
  """
692
- if type(value) is not list:
693
- raise TypeError(
694
- "Assigning non-list value to object {} of type {}".format(
695
- self._name, type(self)
696
- )
697
- )
698
- if len(value) != len(self):
699
- raise ValueError(
700
- "Assigning list of length %d to object %s of length %d"
701
- % (len(value), self._name, len(self))
702
- )
703
- for val_idx, self_idx in enumerate(
704
- self._range_iter(self._range[0], self._range[1])
705
- ):
706
- self[self_idx]._set_value(value[val_idx], call_sim)
1039
+ r = self.range
1040
+ return Array._from_handle(
1041
+ value=[self[i].value for i in r],
1042
+ range=r,
1043
+ warn_indexing=indexing_changed(r) if do_indexing_changed_warning else False,
1044
+ )
707
1045
 
1046
+ def set(
1047
+ self,
1048
+ value: Union[
1049
+ Union[Array[ElemValueT], Sequence[ElemValueT]],
1050
+ Deposit[Union[Array[ElemValueT], Sequence[ElemValueT]]],
1051
+ Force[Union[Array[ElemValueT], Sequence[ElemValueT]]],
1052
+ Freeze,
1053
+ Release,
1054
+ Immediate[Union[Array[ElemValueT], Sequence[ElemValueT]]],
1055
+ ],
1056
+ ) -> None:
1057
+ """Set the value using an :class:`.Array`-like value.
708
1058
 
709
- class NonConstantObject(NonHierarchyIndexableObject):
710
- """A non-constant object"""
1059
+ The simulation object is set, element-by-element, left-to-right, using the corresponding element of *value*.
1060
+ The indexes of *value* and the simulation object are not taken into account, only position.
711
1061
 
712
- # FIXME: what is the difference to ModifiableObject? Explain in docstring.
1062
+ .. warning::
1063
+ Assigning a value to a sub-handle:
713
1064
 
714
- def drivers(self):
715
- """An iterator for gathering all drivers for a signal.
1065
+ - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as an Array, updates index 0, then throws it away)
1066
+ - **Correct**: ``dut.some_array[0].value = 1``
716
1067
 
717
- This is currently only available for VPI.
718
- Also, only a few simulators implement this.
719
- """
720
- return self._handle.iterate(simulator.DRIVERS)
1068
+ Args:
1069
+ value: The value to set the signal to. This may include type conversion.
721
1070
 
722
- def loads(self):
723
- """An iterator for gathering all loads on a signal.
1071
+ Raises:
1072
+ TypeError: If *value* is of a type that can't be assigned to the simulation object.
724
1073
 
725
- This is currently only available for VPI.
726
- Also, only a few simulators implement this.
1074
+ .. warning::
1075
+ Exceptions from array element :meth:`.ValueObjectBase.set` calls will be propagated up,
1076
+ so the actual set of exceptions possible is greater than this list.
727
1077
  """
728
- return self._handle.iterate(simulator.LOADS)
1078
+ super().set(value)
729
1079
 
1080
+ def _set_value(
1081
+ self,
1082
+ value: Union[Array[ElemValueT], Sequence[ElemValueT]],
1083
+ action: _GPISetAction,
1084
+ ) -> None:
1085
+ if len(value) != len(self):
1086
+ raise ValueError(
1087
+ f"Assigning list of length {len(value)} to object {self._name} of length {len(self)}"
1088
+ )
1089
+ for elem, self_idx in zip(value, self.range):
1090
+ self[self_idx]._set_value(elem, action)
730
1091
 
731
- class _SetAction:
732
- """Base class representing the type of action used while write-accessing a handle."""
733
-
734
- pass
1092
+ def __getitem__(self, index: int) -> ChildObjectT:
1093
+ if isinstance(index, slice):
1094
+ raise TypeError("Slicing is not supported")
1095
+ if index in self._sub_handles:
1096
+ return self._sub_handles[index]
1097
+ new_handle = self._handle.get_handle_by_index(index)
1098
+ if not new_handle:
1099
+ raise IndexError(f"{self._path} contains no object at index {index}")
1100
+ path = self._path + "[" + str(index) + "]"
1101
+ self._sub_handles[index] = cast(
1102
+ "ChildObjectT", _make_sim_object(new_handle, path)
1103
+ )
1104
+ return self._sub_handles[index]
735
1105
 
1106
+ def __iter__(self) -> Iterable[ChildObjectT]:
1107
+ for i in self.range:
1108
+ yield self[i]
736
1109
 
737
- class _SetValueAction(_SetAction):
738
- __slots__ = ("value",)
739
- """Base class representing the type of action used while write-accessing a handle with a value."""
740
1110
 
741
- def __init__(self, value):
742
- self.value = value
1111
+ class _NonIndexableValueObjectBase(ValueObjectBase[ValueGetT, ValueSetT]):
1112
+ """ValueObject that is treated as a single object in the GPI.
743
1113
 
1114
+ NonArrayValueObjects support :meth:`value_change` triggers.
1115
+ """
744
1116
 
745
- class Deposit(_SetValueAction):
746
- """Action used for placing a value into a given handle."""
1117
+ @cached_property
1118
+ def value_change(self) -> ValueChange:
1119
+ """A trigger which fires whenever the value changes."""
1120
+ if self.is_const:
1121
+ raise TypeError("Can't get ValueChange on immutable signal.")
1122
+ return ValueChange._make(self)
747
1123
 
748
- def _as_gpi_args_for(self, hdl):
749
- return self.value, 0 # GPI_DEPOSIT
1124
+ @cached_property
1125
+ def _edge(self) -> Edge:
1126
+ if self.is_const:
1127
+ raise TypeError("Can't get Edge on immutable signal.")
1128
+ return Edge._make(self)
750
1129
 
751
1130
 
752
- class Force(_SetValueAction):
753
- """Action used to force a handle to a given value until a release is applied."""
1131
+ class LogicObject(_NonIndexableValueObjectBase[Logic, Union[Logic, int, str]]):
1132
+ """A scalar logic simulation object.
754
1133
 
755
- def _as_gpi_args_for(self, hdl):
756
- return self.value, 1 # GPI_FORCE
1134
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
757
1135
 
1136
+ Verilog data types that map to this object:
758
1137
 
759
- class Freeze(_SetAction):
760
- """Action used to make a handle keep its current value until a release is used."""
1138
+ * ``logic``
1139
+ * ``bit``
761
1140
 
762
- def _as_gpi_args_for(self, hdl):
763
- return hdl.value, 1 # GPI_FORCE
1141
+ VHDL types that map to this object:
764
1142
 
1143
+ * ``std_logic``
1144
+ * ``std_ulogic``
1145
+ * ``bit``
1146
+ """
765
1147
 
766
- class Release(_SetAction):
767
- """Action used to stop the effects of a previously applied force/freeze action."""
1148
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1149
+ super().__init__(handle, path)
768
1150
 
769
- def _as_gpi_args_for(self, hdl):
770
- return 0, 2 # GPI_RELEASE
1151
+ def _set_value(
1152
+ self,
1153
+ value: Union[Logic, int, str],
1154
+ action: _GPISetAction,
1155
+ ) -> None:
1156
+ value_: str
1157
+ if isinstance(value, (int, str)):
1158
+ value_ = str(Logic(value))
771
1159
 
1160
+ elif isinstance(value, LogicArray):
1161
+ if len(value) != 1:
1162
+ raise ValueError(
1163
+ f"Cannot assign value of length {len(value)} to handle of length 1"
1164
+ )
1165
+ value_ = str(value)
772
1166
 
773
- class ModifiableObject(NonConstantObject):
774
- """Base class for simulator objects whose values can be modified."""
1167
+ elif isinstance(value, Logic):
1168
+ value_ = str(value)
775
1169
 
776
- def _set_value(self, value, call_sim):
777
- """Set the value of the underlying simulation object to *value*.
1170
+ else:
1171
+ raise TypeError(
1172
+ f"Unsupported type for value assignment: {type(value)} ({value!r})"
1173
+ )
778
1174
 
779
- This operation will fail unless the handle refers to a modifiable
780
- object, e.g. net, signal or variable.
1175
+ _schedule_write(self, self._handle.set_signal_val_binstr, action, value_)
781
1176
 
782
- We determine the library call to make based on the type of the value
783
- because assigning integers less than 32 bits is faster.
1177
+ def get(self) -> Logic:
1178
+ """Return the current value of the simulation object as a :class:`.Logic`."""
1179
+ binstr = self._handle.get_signal_val_binstr()
1180
+ return Logic(binstr)
1181
+
1182
+ def set(
1183
+ self,
1184
+ value: Union[
1185
+ Union[Logic, int, str],
1186
+ Deposit[Union[Logic, int, str]],
1187
+ Force[Union[Logic, int, str]],
1188
+ Freeze,
1189
+ Release,
1190
+ Immediate[Union[Logic, int, str]],
1191
+ ],
1192
+ ) -> None:
1193
+ """Set the value of the simulation object using a :class:`.Logic`-like value.
784
1194
 
785
1195
  Args:
786
- value (cocotb.binary.BinaryValue, int):
787
- The value to drive onto the simulator object.
1196
+ value: The value to set the simulation object to.
788
1197
 
789
1198
  Raises:
790
- TypeError: If target has an unsupported type for value assignment.
1199
+ TypeError: If *value* is of a type that can't be assigned to the simulation object, or readily converted into a type that can.
1200
+ ValueError: If *value* would not fit in the bounds of the simulation object.
1201
+ """
1202
+ super().set(value)
1203
+
1204
+ @cached_property
1205
+ def rising_edge(self) -> RisingEdge:
1206
+ """A trigger which fires whenever the value changes to a ``1``."""
1207
+ if self.is_const:
1208
+ raise TypeError("Can't get RisingEdge on immutable signal")
1209
+ return RisingEdge._make(self)
1210
+
1211
+ @cached_property
1212
+ def falling_edge(self) -> FallingEdge:
1213
+ """A trigger which fires whenever the value changes to a ``0``."""
1214
+ if self.is_const:
1215
+ raise TypeError("Can't get FallingEdge on immutable signal")
1216
+ return FallingEdge._make(self)
1217
+
1218
+ def __len__(self) -> int:
1219
+ return 1
1220
+
1221
+ @deprecated(
1222
+ "`int(handle)` casts have been deprecated. Use `int(handle.value)` instead."
1223
+ )
1224
+ def __int__(self) -> int:
1225
+ return int(self.value)
791
1226
 
792
- OverflowError: If int value is out of the range that can be represented
793
- by the target. -2**(Nbits - 1) <= value <= 2**Nbits - 1
1227
+ @deprecated(
1228
+ "`str(handle)` casts have been deprecated. Use `str(handle.value)` instead."
1229
+ )
1230
+ def __str__(self) -> str:
1231
+ return str(self.value)
794
1232
 
795
- .. deprecated:: 1.5
796
- :class:`ctypes.Structure` objects are no longer accepted as values for assignment.
797
- Convert the struct object to a :class:`~cocotb.binary.BinaryValue` before assignment using
798
- ``BinaryValue(value=bytes(struct_obj), n_bits=len(signal))`` instead.
799
1233
 
800
- .. deprecated:: 1.5
801
- :class:`dict` objects are no longer accepted as values for assignment.
802
- Convert the dictionary to an integer before assignment using
803
- ``sum(v << (d['bits'] * i) for i, v in enumerate(d['values']))``.
804
- """
805
- value, set_action = self._check_for_set_action(value)
1234
+ class LogicArrayObject(
1235
+ _NonIndexableValueObjectBase[LogicArray, Union[LogicArray, Logic, int, str]],
1236
+ _RangeableObjectMixin,
1237
+ ):
1238
+ """A logic array simulation object.
1239
+
1240
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
1241
+
1242
+ Verilog types that map to this object:
1243
+
1244
+ * packed any-dimensional vectors of ``logic`` or ``bit``
1245
+ * packed any-dimensional vectors of packed structures
1246
+
1247
+ VHDL types that map to this object:
1248
+
1249
+ * ``std_logic_vector`` and ``std_ulogic_vector``
1250
+ * ``unsigned``
1251
+ * ``signed``
1252
+ * ``ufixed``
1253
+ * ``sfixed``
1254
+ * ``float``
1255
+ """
806
1256
 
1257
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1258
+ super().__init__(handle, path)
1259
+
1260
+ def _set_value(
1261
+ self,
1262
+ value: Union[LogicArray, Logic, int, str],
1263
+ action: _GPISetAction,
1264
+ ) -> None:
1265
+ value_: str
807
1266
  if isinstance(value, int):
808
1267
  min_val, max_val = _value_limits(len(self), _Limits.VECTOR_NBIT)
809
1268
  if min_val <= value <= max_val:
810
1269
  if len(self) <= 32:
811
- call_sim(self, self._handle.set_signal_val_int, set_action, value)
1270
+ _schedule_write(
1271
+ self, self._handle.set_signal_val_int, action, value
1272
+ )
812
1273
  return
813
1274
 
1275
+ # LogicArray used for checking
814
1276
  if value < 0:
815
- value = BinaryValue(
816
- value=value,
817
- n_bits=len(self),
818
- bigEndian=False,
819
- binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT,
1277
+ value_ = str(
1278
+ LogicArray.from_signed(
1279
+ value,
1280
+ Range(len(self) - 1, "downto", 0),
1281
+ )
820
1282
  )
821
1283
  else:
822
- value = BinaryValue(
823
- value=value,
824
- n_bits=len(self),
825
- bigEndian=False,
826
- binaryRepresentation=BinaryRepresentation.UNSIGNED,
1284
+ value_ = str(
1285
+ LogicArray.from_unsigned(
1286
+ value,
1287
+ Range(len(self) - 1, "downto", 0),
1288
+ )
827
1289
  )
828
1290
  else:
829
- raise OverflowError(
830
- "Int value ({!r}) out of range for assignment of {!r}-bit signal ({!r})".format(
831
- value, len(self), self._name
832
- )
833
- )
834
-
835
- if isinstance(value, ctypes.Structure):
836
- warnings.warn(
837
- "`ctypes.Structure` values are no longer accepted for value assignment. "
838
- "Use `BinaryValue(value=bytes(struct_obj), n_bits=len(signal))` instead",
839
- DeprecationWarning,
840
- stacklevel=3,
841
- )
842
- value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self))
843
-
844
- elif isinstance(value, dict):
845
- warnings.warn(
846
- "dict values are no longer accepted for value assignment. "
847
- "Use `sum(v << (d['bits'] * i) for i, v in enumerate(d['values']))` "
848
- "to convert the dict to an int before assignment.",
849
- DeprecationWarning,
850
- stacklevel=3,
851
- )
852
- # We're given a dictionary with a list of values and a bit size...
853
- num = 0
854
- vallist = list(value["values"])
855
- vallist.reverse()
856
- if len(vallist) * value["bits"] != len(self):
857
- raise TypeError(
858
- "Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long"
859
- % (
860
- len(value["values"]),
861
- value["bits"],
862
- len(value["values"]) * value["bits"],
863
- len(self),
864
- )
1291
+ raise ValueError(
1292
+ f"Int value ({value!r}) out of range for assignment of {len(self)!r}-bit signal ({self._name!r})"
865
1293
  )
866
1294
 
867
- for val in vallist:
868
- num = (num << value["bits"]) + val
869
- value = BinaryValue(value=num, n_bits=len(self), bigEndian=False)
1295
+ elif isinstance(value, str):
1296
+ # LogicArray used for checking
1297
+ value_ = str(LogicArray(value, self.range))
870
1298
 
871
1299
  elif isinstance(value, LogicArray):
872
1300
  if len(self) != len(value):
873
1301
  raise ValueError(
874
1302
  f"cannot assign value of length {len(value)} to handle of length {len(self)}"
875
1303
  )
876
- value = value.to_BinaryValue()
1304
+ value_ = str(value)
877
1305
 
878
1306
  elif isinstance(value, Logic):
879
1307
  if len(self) != 1:
880
1308
  raise ValueError(
881
1309
  f"cannot assign value of length 1 to handle of length {len(self)}"
882
1310
  )
883
- value = BinaryValue(str(value))
884
-
885
- elif isinstance(value, BinaryValue):
886
- if len(value) != len(self):
887
- raise ValueError(
888
- f"cannot assign value of length {len(value)} to handle of length {len(self)}"
889
- )
1311
+ value_ = str(value)
890
1312
 
891
1313
  else:
892
1314
  raise TypeError(
893
- "Unsupported type for value assignment: {} ({!r})".format(
894
- type(value), value
895
- )
1315
+ f"Unsupported type for value assignment: {type(value)} ({value!r})"
896
1316
  )
897
1317
 
898
- call_sim(self, self._handle.set_signal_val_binstr, set_action, value.binstr)
899
-
900
- def _check_for_set_action(self, value):
901
- if not isinstance(value, _SetAction):
902
- return value, 0 # GPI_DEPOSIT
903
- return value._as_gpi_args_for(self)
1318
+ _schedule_write(self, self._handle.set_signal_val_binstr, action, value_)
904
1319
 
905
- @NonConstantObject.value.getter
906
- def value(self) -> BinaryValue:
1320
+ def get(self) -> LogicArray:
1321
+ """Return the current value of the simulation object as a :class:`.LogicArray`."""
907
1322
  binstr = self._handle.get_signal_val_binstr()
908
- # Skip BinaryValue.assign() as we know we are using a binstr
909
- result = BinaryValue(n_bits=len(binstr))
910
- # Skip the permitted characters check as we trust the simulator
911
- result._set_trusted_binstr(binstr)
912
- return result
1323
+ return LogicArray._from_handle(
1324
+ value=binstr,
1325
+ warn_indexing=indexing_changed(self.range)
1326
+ if do_indexing_changed_warning
1327
+ else False,
1328
+ )
913
1329
 
914
- def __int__(self):
915
- return int(self.value)
1330
+ def set(
1331
+ self,
1332
+ value: Union[
1333
+ Union[LogicArray, Logic, int, str],
1334
+ Deposit[Union[LogicArray, Logic, int, str]],
1335
+ Force[Union[LogicArray, Logic, int, str]],
1336
+ Freeze,
1337
+ Release,
1338
+ Immediate[Union[LogicArray, Logic, int, str]],
1339
+ ],
1340
+ ) -> None:
1341
+ """Set the value of the simulation object using a :class:`.LogicArray`-like value.
916
1342
 
917
- def _emit_str_warning(self):
918
- warnings.warn(
919
- "`str({t})` is deprecated, and in future will return `{t}._path`. "
920
- "To get a string representation of the value, use `str({t}.value)`.".format(
921
- t=type(self).__qualname__
922
- ),
923
- FutureWarning,
924
- stacklevel=3,
925
- )
1343
+ Args:
1344
+ value: The value to set the simulation object to.
1345
+
1346
+ Raises:
1347
+ TypeError: If *value* is of a type that can't be assigned to the simulation object, or readily converted into a type that can.
1348
+ ValueError: If *value* would not fit in the bounds of the simulation object.
1349
+
1350
+ .. versionchanged:: 2.0
1351
+ Using :class:`ctypes.Structure` objects to set values was removed.
1352
+ Convert the struct object to a :class:`~cocotb.types.LogicArray` before assignment using
1353
+ ``LogicArray("".join(format(int(byte), "08b") for byte in bytes(struct_obj)))`` instead.
1354
+
1355
+ .. versionchanged:: 2.0
1356
+ Using :class:`dict` objects to set values was removed.
1357
+ Convert the dictionary to an integer before assignment using
1358
+ ``sum(v << (d['bits'] * i) for i, v in enumerate(d['values']))`` instead.
926
1359
 
927
- def __str__(self):
928
- self._emit_str_warning()
1360
+ .. versionchanged:: 2.0
1361
+ Supplying too large of an :class:`int` value results in raising a :exc:`ValueError` instead of an :exc:`OverflowError`.
1362
+ """
1363
+ super().set(value)
1364
+
1365
+ @deprecated(
1366
+ "`int(handle)` casts have been deprecated. Use `int(handle.value)` instead."
1367
+ )
1368
+ def __int__(self) -> int:
1369
+ return int(self.value)
1370
+
1371
+ @deprecated(
1372
+ "`str(handle)` casts have been deprecated. Use `str(handle.value)` instead."
1373
+ )
1374
+ def __str__(self) -> str:
929
1375
  return str(self.value)
930
1376
 
1377
+ def __len__(self) -> int:
1378
+ # can't use `range` to get length because `range` is for outer-most dimension only
1379
+ # and this object needs to support multi-dimensional packed arrays.
1380
+ return self._len
931
1381
 
932
- class RealObject(ModifiableObject):
933
- """Specific object handle for Real signals and variables."""
1382
+ @cached_property
1383
+ def _len(self) -> int:
1384
+ return self._handle.get_num_elems()
934
1385
 
935
- def _set_value(self, value, call_sim):
936
- """Set the value of the underlying simulation object to value.
1386
+ def __getitem__(self, _: object) -> NoReturn:
1387
+ raise TypeError(
1388
+ "Packed objects, either arrays or structs, cannot be indexed.\n"
1389
+ "Try instead reading the whole value and slicing: `t = handle.value; t[0:3]`.\n"
1390
+ "If you need to use an element in an Edge Trigger, consider making the array or struct unpacked.\n"
1391
+ "Alternatively, use `ValueChange` on the whole object and check the bit(s) you care about for changes afterwards."
1392
+ )
937
1393
 
938
- This operation will fail unless the handle refers to a modifiable
939
- object, e.g. net, signal or variable.
940
1394
 
941
- Args:
942
- value (float): The value to drive onto the simulator object.
1395
+ class RealObject(_NonIndexableValueObjectBase[float, float]):
1396
+ """A floating point simulation object.
943
1397
 
944
- Raises:
945
- TypeError: If target has an unsupported type for
946
- real value assignment.
947
- """
948
- value, set_action = self._check_for_set_action(value)
1398
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
949
1399
 
950
- try:
951
- value = float(value)
952
- except ValueError:
1400
+ This type is used when a ``real`` object in VHDL or ``float`` object in Verilog is seen.
1401
+ """
1402
+
1403
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1404
+ super().__init__(handle, path)
1405
+
1406
+ def _set_value(
1407
+ self,
1408
+ value: float,
1409
+ action: _GPISetAction,
1410
+ ) -> None:
1411
+ if not isinstance(value, (float, int)):
953
1412
  raise TypeError(
954
- "Unsupported type for real value assignment: {} ({!r})".format(
955
- type(value), value
956
- )
1413
+ f"Unsupported type for real value assignment: {type(value)} ({value!r})"
957
1414
  )
958
1415
 
959
- call_sim(self, self._handle.set_signal_val_real, set_action, value)
1416
+ _schedule_write(self, self._handle.set_signal_val_real, action, value)
960
1417
 
961
- @ModifiableObject.value.getter
962
- def value(self) -> float:
1418
+ def get(self) -> float:
1419
+ """Return the current value of the simulation object as a :class:`float`."""
963
1420
  return self._handle.get_signal_val_real()
964
1421
 
965
- def __float__(self):
966
- return float(self.value)
1422
+ def set(
1423
+ self,
1424
+ value: Union[
1425
+ float,
1426
+ Deposit[float],
1427
+ Force[float],
1428
+ Freeze,
1429
+ Release,
1430
+ Immediate[float],
1431
+ ],
1432
+ ) -> None:
1433
+ """Set the value of the simulation object using a :class:`float` value.
967
1434
 
1435
+ Args:
1436
+ value: The value to set the simulation object to.
968
1437
 
969
- class EnumObject(ModifiableObject):
970
- """Specific object handle for enumeration signals and variables."""
1438
+ Raises:
1439
+ TypeError: If *value* is any type other than :class:`float`.
1440
+ """
1441
+ super().set(value)
971
1442
 
972
- def _set_value(self, value, call_sim):
973
- """Set the value of the underlying simulation object to *value*.
1443
+ @deprecated(
1444
+ "`float(handle)` casts have been deprecated. Use `float(handle.value)` instead."
1445
+ )
1446
+ def __float__(self) -> float:
1447
+ return self.value
974
1448
 
975
- This operation will fail unless the handle refers to a modifiable
976
- object, e.g. net, signal or variable.
977
1449
 
978
- Args:
979
- value (int): The value to drive onto the simulator object.
1450
+ class EnumObject(_NonIndexableValueObjectBase[int, int]):
1451
+ """An enumeration simulation object.
980
1452
 
981
- Raises:
982
- TypeError: If target has an unsupported type for
983
- integer value assignment.
984
- """
985
- value, set_action = self._check_for_set_action(value)
1453
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
1454
+
1455
+ This type is used when an enumerated-type simulation object is seen that aren't a "logic" or similar type.
1456
+ The value of this object is represented with an :class:`int`.
1457
+
1458
+ For VHDL objects, the value being represented is the enumeration value at the integer index into the original ``type`` declaration,
1459
+ as if it were a 1-based array.
1460
+
1461
+ For Verilog objects, enumerations are little more than named integer values.
1462
+ There may be many enumeration values that a given :class:`int` value represents.
1463
+ """
1464
+
1465
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1466
+ super().__init__(handle, path)
986
1467
 
987
- if isinstance(value, BinaryValue):
988
- value = int(value)
989
- elif not isinstance(value, int):
1468
+ def _set_value(
1469
+ self,
1470
+ value: int,
1471
+ action: _GPISetAction,
1472
+ ) -> None:
1473
+ if not isinstance(value, int):
990
1474
  raise TypeError(
991
- "Unsupported type for enum value assignment: {} ({!r})".format(
992
- type(value), value
993
- )
1475
+ f"Unsupported type for enum value assignment: {type(value)} ({value!r})"
994
1476
  )
995
1477
 
996
1478
  min_val, max_val = _value_limits(32, _Limits.UNSIGNED_NBIT)
997
1479
  if min_val <= value <= max_val:
998
- call_sim(self, self._handle.set_signal_val_int, set_action, value)
1480
+ _schedule_write(self, self._handle.set_signal_val_int, action, value)
999
1481
  else:
1000
- raise OverflowError(
1001
- "Int value ({!r}) out of range for assignment of enum signal ({!r})".format(
1002
- value, self._name
1003
- )
1482
+ raise ValueError(
1483
+ f"Int value ({value!r}) out of range for assignment of enum signal ({self._name!r})"
1004
1484
  )
1005
1485
 
1006
- @ModifiableObject.value.getter
1007
- def value(self) -> int:
1008
- return self._handle.get_signal_val_long()
1486
+ def get(self) -> int:
1487
+ """Return the current value of the simulation object as an :class:`int`.
1009
1488
 
1489
+ See :class:`EnumObject` for details on what :class:`int` values correspond to which enumeration values.
1490
+ """
1491
+ return self._handle.get_signal_val_long()
1010
1492
 
1011
- class IntegerObject(ModifiableObject):
1012
- """Specific object handle for integer and enumeration signals and variables."""
1013
-
1014
- def _set_value(self, value, call_sim):
1015
- """Set the value of the underlying simulation object to *value*.
1016
-
1017
- This operation will fail unless the handle refers to a modifiable
1018
- object, e.g. net, signal or variable.
1493
+ def set(
1494
+ self,
1495
+ value: Union[
1496
+ int,
1497
+ Deposit[int],
1498
+ Force[int],
1499
+ Freeze,
1500
+ Release,
1501
+ Immediate[int],
1502
+ ],
1503
+ ) -> None:
1504
+ """Set the value of the simulation object using an :class:`int`.
1505
+
1506
+ See :class:`EnumObject` for details on what :class:`int` values correspond to which enumeration values.
1019
1507
 
1020
1508
  Args:
1021
- value (int): The value to drive onto the simulator object.
1509
+ value: The value to set the simulation object to.
1022
1510
 
1023
1511
  Raises:
1024
- TypeError: If target has an unsupported type for
1025
- integer value assignment.
1512
+ TypeError: If *value* is any type other than :class:`int`.
1513
+ ValueError: If *value* would not fit in a 32-bit signed integer.
1026
1514
 
1027
- OverflowError: If value is out of range for assignment
1028
- of 32-bit IntegerObject.
1515
+ .. versionchanged:: 2.0
1516
+ Supplying too large of a value results in raising a :exc:`ValueError` instead of an :exc:`OverflowError`.
1029
1517
  """
1030
- value, set_action = self._check_for_set_action(value)
1518
+ super().set(value)
1519
+
1520
+ @deprecated(
1521
+ "`int(handle)` casts have been deprecated. Use `int(handle.value)` instead."
1522
+ )
1523
+ def __int__(self) -> int:
1524
+ return int(self.value)
1525
+
1526
+
1527
+ class IntegerObject(_NonIndexableValueObjectBase[int, int]):
1528
+ """An integer simulation object.
1529
+
1530
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
1031
1531
 
1032
- if isinstance(value, BinaryValue):
1033
- value = int(value)
1034
- elif not isinstance(value, int):
1532
+ Verilog types that map to this object:
1533
+
1534
+ * ``byte``
1535
+ * ``shortint``
1536
+ * ``int``
1537
+ * ``longint``
1538
+
1539
+ This type should not be used for the 4-state integer types ``integer`` and ``time``.
1540
+
1541
+ VHDL types that map to this object:
1542
+
1543
+ * ``integer``
1544
+ * ``natural``
1545
+ * ``positive``
1546
+
1547
+ Objects that use this type are assumed to be two's complement 32-bit integers with 2-state (``0`` and ``1``) bits.
1548
+ """
1549
+
1550
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1551
+ super().__init__(handle, path)
1552
+
1553
+ def _set_value(
1554
+ self,
1555
+ value: int,
1556
+ action: _GPISetAction,
1557
+ ) -> None:
1558
+ if not isinstance(value, int):
1035
1559
  raise TypeError(
1036
- "Unsupported type for integer value assignment: {} ({!r})".format(
1037
- type(value), value
1038
- )
1560
+ f"Unsupported type for integer value assignment: {type(value)} ({value!r})"
1039
1561
  )
1040
1562
 
1041
1563
  min_val, max_val = _value_limits(32, _Limits.SIGNED_NBIT)
1042
1564
  if min_val <= value <= max_val:
1043
- call_sim(self, self._handle.set_signal_val_int, set_action, value)
1565
+ _schedule_write(self, self._handle.set_signal_val_int, action, value)
1044
1566
  else:
1045
- raise OverflowError(
1046
- "Int value ({!r}) out of range for assignment of integer signal ({!r})".format(
1047
- value, self._name
1048
- )
1567
+ raise ValueError(
1568
+ f"Int value ({value!r}) out of range for assignment of integer signal ({self._name!r})"
1049
1569
  )
1050
1570
 
1051
- @ModifiableObject.value.getter
1052
- def value(self) -> int:
1571
+ def get(self) -> int:
1572
+ """Return the current value of the simulation object as an :class:`int`."""
1053
1573
  return self._handle.get_signal_val_long()
1054
1574
 
1575
+ def set(
1576
+ self,
1577
+ value: Union[
1578
+ int,
1579
+ Deposit[int],
1580
+ Force[int],
1581
+ Freeze,
1582
+ Release,
1583
+ Immediate[int],
1584
+ ],
1585
+ ) -> None:
1586
+ """Set the the value of the simulation object using an :class:`int` value.
1055
1587
 
1056
- class StringObject(ModifiableObject):
1057
- """Specific object handle for String variables."""
1588
+ Args:
1589
+ value: The value to set the simulation object to.
1058
1590
 
1059
- def _set_value(self, value, call_sim):
1060
- """Set the value of the underlying simulation object to *value*.
1591
+ Raises:
1592
+ TypeError: If *value* is any type other than :class:`int`.
1593
+ ValueError: If *value* would not fit in a 32-bit signed integer.
1061
1594
 
1062
- This operation will fail unless the handle refers to a modifiable
1063
- object, e.g. net, signal or variable.
1595
+ .. versionchanged:: 2.0
1596
+ Supplying too large of a value results in raising a :exc:`ValueError` instead of an :exc:`OverflowError`.
1597
+ """
1598
+ super().set(value)
1064
1599
 
1065
- Args:
1066
- value (bytes): The value to drive onto the simulator object.
1600
+ @deprecated(
1601
+ "`int(handle)` casts have been deprecated. Use `int(handle.value)` instead."
1602
+ )
1603
+ def __int__(self) -> int:
1604
+ return self.value
1067
1605
 
1068
- Raises:
1069
- TypeError: If target has an unsupported type for
1070
- string value assignment.
1071
1606
 
1072
- .. versionchanged:: 1.4
1073
- Takes :class:`bytes` instead of :class:`str`.
1074
- Users are now expected to choose an encoding when using these objects.
1075
- As a convenience, when assigning :class:`str` values, ASCII encoding will be used as a safe default.
1607
+ class StringObject(
1608
+ _NonIndexableValueObjectBase[bytes, bytes],
1609
+ _RangeableObjectMixin,
1610
+ ):
1611
+ """A string simulation object.
1076
1612
 
1077
- """
1078
- value, set_action = self._check_for_set_action(value)
1079
-
1080
- if isinstance(value, str):
1081
- warnings.warn(
1082
- "Handles on string objects will soon not accept `str` objects. "
1083
- "Please use a bytes object by encoding the string as you see fit. "
1084
- "`str.encode('ascii')` is typically sufficient.",
1085
- DeprecationWarning,
1086
- stacklevel=2,
1087
- )
1088
- value = value.encode("ascii") # may throw UnicodeEncodeError
1613
+ Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
1614
+
1615
+ This type is used when a ``string`` (VHDL or Verilog) simulation object is seen.
1616
+ """
1089
1617
 
1090
- if not isinstance(value, bytes):
1618
+ def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
1619
+ super().__init__(handle, path)
1620
+
1621
+ def _set_value(
1622
+ self,
1623
+ value: bytes,
1624
+ action: _GPISetAction,
1625
+ ) -> None:
1626
+ if not isinstance(value, (bytes, bytearray)):
1091
1627
  raise TypeError(
1092
- "Unsupported type for string value assignment: {} ({!r})".format(
1093
- type(value), value
1094
- )
1628
+ f"Unsupported type for string value assignment: {type(value)} ({value!r})"
1095
1629
  )
1630
+ _schedule_write(self, self._handle.set_signal_val_str, action, value)
1096
1631
 
1097
- call_sim(self, self._handle.set_signal_val_str, set_action, value)
1098
-
1099
- @ModifiableObject.value.getter
1100
- def value(self) -> bytes:
1632
+ def get(self) -> bytes:
1633
+ """Return the current value of the simulation object as a :class:`bytes`."""
1101
1634
  return self._handle.get_signal_val_str()
1102
1635
 
1103
- def _emit_str_warning(self):
1104
- warnings.warn(
1105
- "`str({t})` is deprecated, and in future will return `{t}._path`. "
1106
- "To access the `bytes` value of this handle, use `{t}.value`.".format(
1107
- t=type(self).__qualname__
1108
- ),
1109
- FutureWarning,
1110
- stacklevel=3,
1111
- )
1636
+ def set(
1637
+ self,
1638
+ value: Union[
1639
+ bytes,
1640
+ Deposit[bytes],
1641
+ Force[bytes],
1642
+ Freeze,
1643
+ Release,
1644
+ Immediate[bytes],
1645
+ ],
1646
+ ) -> None:
1647
+ """Set the value of the simulation object with a :class:`bytes` or :class:`bytearray` value.
1648
+
1649
+ When *value*'s length is less than the simulation object's,
1650
+ the value is padded with NUL (``'\0'``) characters up to the appropriate length.
1651
+ When the value's length is greater than the simulation object's,
1652
+ the value is truncated without a NUL terminator to the appropriate length,
1653
+ without warning.
1654
+
1655
+ Strings in both Verilog and VHDL are byte arrays without any particular encoding.
1656
+ Encoding must be done to turn Python strings into byte arrays.
1657
+ Because `there are many encodings <https://docs.python.org/3/library/codecs.html#standard-encodings>`_,
1658
+ this step is left up to the user.
1659
+
1660
+ An example of how encoding and decoding could be accomplished using an ASCII string.
1661
+
1662
+ .. code-block:: python
1663
+
1664
+ # lowercase a string
1665
+ value = dut.string_handle.value.decode("ascii")
1666
+ value = value.lower()
1667
+ dut.string_handle.value = value.encode("ascii")
1112
1668
 
1113
- def __str__(self):
1114
- self._emit_str_warning()
1115
- return self.value.decode("ascii")
1669
+ Args:
1670
+ value: The value to set the simulation object to.
1116
1671
 
1672
+ Raises:
1673
+ TypeError: If *value* is any type other than :class:`bytes`.
1674
+
1675
+ .. versionchanged:: 1.4
1676
+ Takes :class:`bytes` instead of :class:`str`.
1677
+ Users are now expected to choose an encoding when using these objects.
1678
+ """
1679
+ super().set(value)
1117
1680
 
1118
- _handle2obj = {}
1681
+ @deprecated(
1682
+ '`str(handle)` casts have been deprecated. Use `handle.value.decode("ascii")` instead.'
1683
+ )
1684
+ def __str__(self) -> str:
1685
+ return self.value.decode("ascii")
1119
1686
 
1120
1687
 
1121
- def SimHandle(handle, path=None):
1688
+ _ConcreteHandleTypes = Union[
1689
+ HierarchyObject,
1690
+ HierarchyArrayObject[SimHandleBase],
1691
+ LogicObject,
1692
+ LogicArrayObject,
1693
+ ArrayObject[Any, ValueObjectBase[Any, Any]],
1694
+ RealObject,
1695
+ IntegerObject,
1696
+ EnumObject,
1697
+ StringObject,
1698
+ ]
1699
+
1700
+
1701
+ _handle2obj: Dict[
1702
+ simulator.gpi_sim_hdl,
1703
+ _ConcreteHandleTypes,
1704
+ ] = {}
1705
+
1706
+ _type2cls: Dict[int, Type[_ConcreteHandleTypes]] = {
1707
+ simulator.MODULE: HierarchyObject,
1708
+ simulator.STRUCTURE: HierarchyObject,
1709
+ simulator.PACKED_STRUCTURE: LogicArrayObject,
1710
+ simulator.LOGIC: LogicObject,
1711
+ simulator.LOGIC_ARRAY: LogicArrayObject,
1712
+ simulator.NETARRAY: ArrayObject[Any, ValueObjectBase[Any, Any]],
1713
+ simulator.REAL: RealObject,
1714
+ simulator.INTEGER: IntegerObject,
1715
+ simulator.ENUM: EnumObject,
1716
+ simulator.STRING: StringObject,
1717
+ simulator.GENARRAY: HierarchyArrayObject[SimHandleBase],
1718
+ simulator.PACKAGE: HierarchyObject,
1719
+ }
1720
+
1721
+
1722
+ def _make_sim_object(
1723
+ handle: simulator.gpi_sim_hdl, path: Optional[str] = None
1724
+ ) -> SimHandleBase:
1122
1725
  """Factory function to create the correct type of `SimHandle` object.
1123
1726
 
1124
1727
  Args:
1125
- handle (int): The GPI handle to the simulator object.
1126
- path (str): Path to this handle, ``None`` if root.
1728
+ handle: The GPI handle to the simulator object.
1729
+ path: Path to this handle.
1127
1730
 
1128
1731
  Returns:
1129
- The `SimHandle` object.
1732
+ An appropriate :class:`SimHandleBase` object.
1130
1733
 
1131
1734
  Raises:
1132
1735
  NotImplementedError: If no matching object for GPI type could be found.
1133
1736
  """
1134
- _type2cls = {
1135
- simulator.MODULE: HierarchyObject,
1136
- simulator.STRUCTURE: HierarchyObject,
1137
- simulator.REG: ModifiableObject,
1138
- simulator.NET: ModifiableObject,
1139
- simulator.NETARRAY: NonHierarchyIndexableObject,
1140
- simulator.REAL: RealObject,
1141
- simulator.INTEGER: IntegerObject,
1142
- simulator.ENUM: EnumObject,
1143
- simulator.STRING: StringObject,
1144
- simulator.GENARRAY: HierarchyArrayObject,
1145
- }
1146
1737
 
1147
1738
  # Enforce singletons since it's possible to retrieve handles avoiding
1148
1739
  # the hierarchy by getting driver/load information
1149
- global _handle2obj
1150
1740
  try:
1151
1741
  return _handle2obj[handle]
1152
1742
  except KeyError:
1153
1743
  pass
1154
1744
 
1155
1745
  t = handle.get_type()
1156
-
1157
- # Special case for constants
1158
- if handle.get_const() and t not in [
1159
- simulator.MODULE,
1160
- simulator.STRUCTURE,
1161
- simulator.NETARRAY,
1162
- simulator.GENARRAY,
1163
- ]:
1164
- obj = ConstantObject(handle, path, t)
1165
- _handle2obj[handle] = obj
1166
- return obj
1167
-
1168
1746
  if t not in _type2cls:
1169
1747
  raise NotImplementedError(
1170
- "Couldn't find a matching object for GPI type %s(%d) (path=%s)"
1171
- % (handle.get_type_string(), t, path)
1748
+ f"Couldn't find a matching object for GPI type {handle.get_type_string()}({t}) (path={path})"
1172
1749
  )
1173
1750
  obj = _type2cls[t](handle, path)
1174
1751
  _handle2obj[handle] = obj