cocotb 1.9.2__cp313-cp313-win32.whl → 2.0.0rc2__cp313-cp313-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cocotb might be problematic. Click here for more details.
- cocotb/_ANSI.py +65 -0
- cocotb/__init__.py +81 -327
- cocotb/_base_triggers.py +515 -0
- cocotb/_bridge.py +186 -0
- cocotb/_decorators.py +515 -0
- cocotb/_deprecation.py +3 -3
- cocotb/_exceptions.py +7 -0
- cocotb/_extended_awaitables.py +419 -0
- cocotb/_gpi_triggers.py +385 -0
- cocotb/_init.py +301 -0
- cocotb/_outcomes.py +54 -0
- cocotb/_profiling.py +46 -0
- cocotb/_py_compat.py +114 -29
- cocotb/_scheduler.py +448 -0
- cocotb/_test.py +248 -0
- cocotb/_test_factory.py +312 -0
- cocotb/_test_functions.py +42 -0
- cocotb/_typing.py +7 -0
- cocotb/_utils.py +274 -0
- cocotb/_version.py +3 -7
- cocotb/_xunit_reporter.py +66 -0
- cocotb/clock.py +353 -108
- cocotb/debug.py +24 -0
- cocotb/handle.py +1370 -793
- cocotb/libs/cocotb.dll +0 -0
- cocotb/libs/cocotb.exp +0 -0
- cocotb/libs/cocotb.lib +0 -0
- cocotb/libs/cocotbfli_modelsim.dll +0 -0
- cocotb/libs/cocotbfli_modelsim.exp +0 -0
- cocotb/libs/cocotbfli_modelsim.lib +0 -0
- cocotb/libs/cocotbutils.dll +0 -0
- cocotb/libs/cocotbutils.exp +0 -0
- cocotb/libs/cocotbutils.lib +0 -0
- cocotb/libs/cocotbvhpi_aldec.dll +0 -0
- cocotb/libs/cocotbvhpi_aldec.exp +0 -0
- cocotb/libs/cocotbvhpi_aldec.lib +0 -0
- cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
- cocotb/libs/cocotbvpi_aldec.dll +0 -0
- cocotb/libs/cocotbvpi_aldec.exp +0 -0
- cocotb/libs/cocotbvpi_aldec.lib +0 -0
- cocotb/libs/cocotbvpi_ghdl.dll +0 -0
- cocotb/libs/cocotbvpi_ghdl.exp +0 -0
- cocotb/libs/cocotbvpi_ghdl.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.exp +0 -0
- cocotb/libs/cocotbvpi_icarus.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.vpl +0 -0
- cocotb/libs/cocotbvpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvpi_modelsim.lib +0 -0
- cocotb/libs/embed.dll +0 -0
- cocotb/libs/embed.exp +0 -0
- cocotb/libs/embed.lib +0 -0
- cocotb/libs/gpi.dll +0 -0
- cocotb/libs/gpi.exp +0 -0
- cocotb/libs/gpi.lib +0 -0
- cocotb/libs/gpilog.dll +0 -0
- cocotb/libs/gpilog.exp +0 -0
- cocotb/libs/gpilog.lib +0 -0
- cocotb/libs/pygpilog.dll +0 -0
- cocotb/libs/pygpilog.exp +0 -0
- cocotb/libs/pygpilog.lib +0 -0
- cocotb/logging.py +424 -0
- cocotb/queue.py +103 -57
- cocotb/regression.py +680 -717
- cocotb/result.py +17 -188
- cocotb/share/def/aldec.exp +0 -0
- cocotb/share/def/aldec.lib +0 -0
- cocotb/share/def/ghdl.exp +0 -0
- cocotb/share/def/ghdl.lib +0 -0
- cocotb/share/def/icarus.exp +0 -0
- cocotb/share/def/icarus.lib +0 -0
- cocotb/share/def/modelsim.def +1 -0
- cocotb/share/def/modelsim.exp +0 -0
- cocotb/share/def/modelsim.lib +0 -0
- cocotb/share/include/cocotb_utils.h +9 -32
- cocotb/share/include/embed.h +7 -30
- cocotb/share/include/gpi.h +331 -137
- cocotb/share/include/gpi_logging.h +221 -142
- cocotb/share/include/py_gpi_logging.h +8 -5
- cocotb/share/include/vpi_user_ext.h +4 -26
- cocotb/share/lib/verilator/verilator.cpp +80 -67
- cocotb/simtime.py +230 -0
- cocotb/simulator.cp313-win32.exp +0 -0
- cocotb/simulator.cp313-win32.lib +0 -0
- cocotb/simulator.cp313-win32.pyd +0 -0
- cocotb/simulator.pyi +107 -0
- cocotb/task.py +478 -213
- cocotb/triggers.py +55 -1092
- cocotb/types/__init__.py +28 -47
- cocotb/types/_abstract_array.py +151 -0
- cocotb/types/_array.py +295 -0
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +333 -0
- cocotb/types/_logic_array.py +868 -0
- cocotb/types/{range.py → _range.py} +47 -48
- cocotb/types/_resolve.py +76 -0
- cocotb/utils.py +58 -646
- cocotb-2.0.0rc2.dist-info/METADATA +60 -0
- cocotb-2.0.0rc2.dist-info/RECORD +146 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +1 -1
- cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info/licenses}/LICENSE +1 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +1 -0
- cocotb_tools/__init__.py +0 -0
- cocotb_tools/_coverage.py +33 -0
- cocotb_tools/_vendor/__init__.py +3 -0
- cocotb_tools/check_results.py +65 -0
- cocotb_tools/combine_results.py +152 -0
- cocotb_tools/config.py +241 -0
- {cocotb → cocotb_tools}/ipython_support.py +29 -22
- cocotb_tools/makefiles/Makefile.deprecations +27 -0
- {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +77 -55
- {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +16 -33
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
- cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
- cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
- cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
- cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
- cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
- cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
- cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
- cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
- cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
- cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
- cocotb_tools/py.typed +0 -0
- cocotb_tools/runner.py +1868 -0
- cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
- pygpi/entry.py +34 -18
- pygpi/py.typed +0 -0
- cocotb/ANSI.py +0 -92
- cocotb/binary.py +0 -858
- cocotb/config.py +0 -289
- cocotb/decorators.py +0 -332
- cocotb/log.py +0 -303
- cocotb/memdebug.py +0 -35
- cocotb/outcomes.py +0 -56
- cocotb/runner.py +0 -1400
- cocotb/scheduler.py +0 -1099
- cocotb/share/makefiles/Makefile.deprecations +0 -12
- cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
- cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
- cocotb/share/makefiles/simulators/Makefile.ius +0 -125
- cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
- cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
- cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
- cocotb/types/array.py +0 -309
- cocotb/types/logic.py +0 -292
- cocotb/types/logic_array.py +0 -298
- cocotb/wavedrom.py +0 -199
- cocotb/xunit_reporter.py +0 -80
- cocotb-1.9.2.dist-info/METADATA +0 -168
- cocotb-1.9.2.dist-info/RECORD +0 -121
- cocotb-1.9.2.dist-info/entry_points.txt +0 -2
- /cocotb/{_vendor/__init__.py → py.typed} +0 -0
- {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
cocotb/handle.py
CHANGED
|
@@ -1,48 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# Copyright cocotb contributors
|
|
3
2
|
# Copyright (c) 2013 Potential Ventures Ltd
|
|
4
3
|
# Copyright (c) 2013 SolarFlare Communications Inc
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
|
|
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
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
35
12
|
from functools import lru_cache
|
|
36
|
-
from
|
|
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.
|
|
41
|
-
from cocotb.
|
|
42
|
-
from cocotb.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
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.
|
|
292
|
+
self._log.exception(
|
|
278
293
|
"Unable to translate handle >%s< to a valid _sub_handle key",
|
|
279
|
-
|
|
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
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
292
|
-
|
|
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
|
-
|
|
296
|
-
|
|
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
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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[
|
|
337
|
+
return self._sub_handles[key]
|
|
307
338
|
except KeyError:
|
|
308
339
|
pass
|
|
309
340
|
|
|
310
|
-
#
|
|
311
|
-
|
|
312
|
-
if
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
358
|
+
Args:
|
|
359
|
+
key: The key of the child object.
|
|
360
|
+
discovery_method: How to discover the object using the GPI.
|
|
329
361
|
|
|
330
|
-
|
|
331
|
-
|
|
362
|
+
Returns:
|
|
363
|
+
A raw simulator handle for the child object at the given key, or ``None``.
|
|
332
364
|
"""
|
|
333
365
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
384
|
+
Returns:
|
|
385
|
+
A unique key for the child object.
|
|
355
386
|
|
|
356
|
-
|
|
357
|
-
|
|
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
|
|
465
|
+
return object.__setattr__(self, name, value)
|
|
362
466
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
368
|
-
|
|
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
|
-
|
|
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
|
-
|
|
373
|
-
"
|
|
374
|
-
|
|
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.
|
|
385
|
-
if handle is
|
|
386
|
-
|
|
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
|
-
|
|
557
|
+
Accessing a non-existent child results in an :class:`IndexError`.
|
|
389
558
|
|
|
559
|
+
Iteration yields all child objects in order.
|
|
390
560
|
|
|
391
|
-
|
|
392
|
-
"""Hierarchy Arrays are containers of Hierarchy Objects."""
|
|
561
|
+
.. code-block:: python
|
|
393
562
|
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
416
|
-
|
|
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
|
-
|
|
422
|
-
|
|
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,
|
|
425
|
-
if isinstance(
|
|
426
|
-
raise
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
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
|
-
|
|
448
|
-
|
|
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,
|
|
452
|
-
self.
|
|
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
|
-
|
|
466
|
-
|
|
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
|
-
|
|
469
|
-
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
488
|
-
|
|
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
|
-
|
|
492
|
-
|
|
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
|
-
|
|
495
|
-
|
|
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
|
-
|
|
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
|
-
|
|
500
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
|
|
515
|
-
|
|
735
|
+
class Immediate(Generic[_ValueT]):
|
|
736
|
+
""":term:`Deposit <no-delay deposit>` a value on a simulator object without delay.
|
|
516
737
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
-
|
|
546
|
-
|
|
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
|
|
551
|
-
"""
|
|
844
|
+
class ValueObjectBase(SimHandleBase, Generic[ValueGetT, ValueSetT]):
|
|
845
|
+
"""Abstract base class for simulation objects that have a value.
|
|
552
846
|
|
|
553
|
-
|
|
554
|
-
time and won't change within a simulation.
|
|
847
|
+
Inherits from :class:`SimHandleBase`.
|
|
555
848
|
"""
|
|
556
849
|
|
|
557
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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
|
-
|
|
582
|
-
|
|
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
|
-
|
|
588
|
-
|
|
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
|
-
|
|
593
|
-
|
|
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
|
|
602
|
-
|
|
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
|
-
|
|
605
|
-
by iterating through sub-handles in left-to-right order.
|
|
996
|
+
Inherits from :class:`SimHandleBase` and :class:`ValueObjectBase`.
|
|
606
997
|
|
|
607
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1004
|
+
These objects can be iterated over to yield child objects:
|
|
618
1005
|
|
|
619
|
-
..
|
|
620
|
-
Assigning a value to a sub-handle:
|
|
1006
|
+
.. code-block:: python
|
|
621
1007
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
"""
|
|
1008
|
+
for child in dut.array_object:
|
|
1009
|
+
print(child._path)
|
|
625
1010
|
|
|
626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
660
|
-
if self._range is None:
|
|
661
|
-
return
|
|
1015
|
+
child = dut.array_object[0]
|
|
662
1016
|
|
|
663
|
-
|
|
664
|
-
for
|
|
665
|
-
|
|
666
|
-
|
|
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
|
|
672
|
-
|
|
673
|
-
|
|
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
|
-
|
|
682
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
|
|
710
|
-
|
|
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
|
-
|
|
1062
|
+
.. warning::
|
|
1063
|
+
Assigning a value to a sub-handle:
|
|
713
1064
|
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
|
|
718
|
-
|
|
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
|
-
|
|
723
|
-
|
|
1071
|
+
Raises:
|
|
1072
|
+
TypeError: If *value* is of a type that can't be assigned to the simulation object.
|
|
724
1073
|
|
|
725
|
-
|
|
726
|
-
|
|
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
|
-
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
-
|
|
742
|
-
|
|
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
|
-
|
|
746
|
-
|
|
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
|
-
|
|
749
|
-
|
|
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
|
|
753
|
-
"""
|
|
1131
|
+
class LogicObject(_NonIndexableValueObjectBase[Logic, Union[Logic, int, str]]):
|
|
1132
|
+
"""A scalar logic simulation object.
|
|
754
1133
|
|
|
755
|
-
|
|
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
|
-
|
|
760
|
-
|
|
1138
|
+
* ``logic``
|
|
1139
|
+
* ``bit``
|
|
761
1140
|
|
|
762
|
-
|
|
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
|
-
|
|
767
|
-
|
|
1148
|
+
def __init__(self, handle: simulator.gpi_sim_hdl, path: Optional[str]) -> None:
|
|
1149
|
+
super().__init__(handle, path)
|
|
768
1150
|
|
|
769
|
-
def
|
|
770
|
-
|
|
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
|
-
|
|
774
|
-
|
|
1167
|
+
elif isinstance(value, Logic):
|
|
1168
|
+
value_ = str(value)
|
|
775
1169
|
|
|
776
|
-
|
|
777
|
-
|
|
1170
|
+
else:
|
|
1171
|
+
raise TypeError(
|
|
1172
|
+
f"Unsupported type for value assignment: {type(value)} ({value!r})"
|
|
1173
|
+
)
|
|
778
1174
|
|
|
779
|
-
|
|
780
|
-
object, e.g. net, signal or variable.
|
|
1175
|
+
_schedule_write(self, self._handle.set_signal_val_binstr, action, value_)
|
|
781
1176
|
|
|
782
|
-
|
|
783
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
793
|
-
|
|
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
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
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
|
-
|
|
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
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1277
|
+
value_ = str(
|
|
1278
|
+
LogicArray.from_signed(
|
|
1279
|
+
value,
|
|
1280
|
+
Range(len(self) - 1, "downto", 0),
|
|
1281
|
+
)
|
|
820
1282
|
)
|
|
821
1283
|
else:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1284
|
+
value_ = str(
|
|
1285
|
+
LogicArray.from_unsigned(
|
|
1286
|
+
value,
|
|
1287
|
+
Range(len(self) - 1, "downto", 0),
|
|
1288
|
+
)
|
|
827
1289
|
)
|
|
828
1290
|
else:
|
|
829
|
-
raise
|
|
830
|
-
"Int value ({!r}) out of range for assignment of {!r}-bit signal ({!r})"
|
|
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
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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})"
|
|
894
|
-
type(value), value
|
|
895
|
-
)
|
|
1315
|
+
f"Unsupported type for value assignment: {type(value)} ({value!r})"
|
|
896
1316
|
)
|
|
897
1317
|
|
|
898
|
-
|
|
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
|
-
|
|
906
|
-
|
|
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
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
|
915
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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
|
-
|
|
928
|
-
|
|
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
|
-
|
|
933
|
-
|
|
1382
|
+
@cached_property
|
|
1383
|
+
def _len(self) -> int:
|
|
1384
|
+
return self._handle.get_num_elems()
|
|
934
1385
|
|
|
935
|
-
def
|
|
936
|
-
|
|
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
|
-
|
|
942
|
-
|
|
1395
|
+
class RealObject(_NonIndexableValueObjectBase[float, float]):
|
|
1396
|
+
"""A floating point simulation object.
|
|
943
1397
|
|
|
944
|
-
|
|
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
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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})"
|
|
955
|
-
type(value), value
|
|
956
|
-
)
|
|
1413
|
+
f"Unsupported type for real value assignment: {type(value)} ({value!r})"
|
|
957
1414
|
)
|
|
958
1415
|
|
|
959
|
-
|
|
1416
|
+
_schedule_write(self, self._handle.set_signal_val_real, action, value)
|
|
960
1417
|
|
|
961
|
-
|
|
962
|
-
|
|
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
|
|
966
|
-
|
|
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
|
-
|
|
970
|
-
|
|
1438
|
+
Raises:
|
|
1439
|
+
TypeError: If *value* is any type other than :class:`float`.
|
|
1440
|
+
"""
|
|
1441
|
+
super().set(value)
|
|
971
1442
|
|
|
972
|
-
|
|
973
|
-
"
|
|
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
|
-
|
|
979
|
-
|
|
1450
|
+
class EnumObject(_NonIndexableValueObjectBase[int, int]):
|
|
1451
|
+
"""An enumeration simulation object.
|
|
980
1452
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
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
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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})"
|
|
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
|
-
|
|
1480
|
+
_schedule_write(self, self._handle.set_signal_val_int, action, value)
|
|
999
1481
|
else:
|
|
1000
|
-
raise
|
|
1001
|
-
"Int value ({!r}) out of range for assignment of enum signal ({!r})"
|
|
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
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
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
|
|
1509
|
+
value: The value to set the simulation object to.
|
|
1022
1510
|
|
|
1023
1511
|
Raises:
|
|
1024
|
-
TypeError: If
|
|
1025
|
-
|
|
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
|
-
|
|
1028
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
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})"
|
|
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
|
-
|
|
1565
|
+
_schedule_write(self, self._handle.set_signal_val_int, action, value)
|
|
1044
1566
|
else:
|
|
1045
|
-
raise
|
|
1046
|
-
"Int value ({!r}) out of range for assignment of integer signal ({!r})"
|
|
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
|
-
|
|
1052
|
-
|
|
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
|
-
|
|
1057
|
-
|
|
1588
|
+
Args:
|
|
1589
|
+
value: The value to set the simulation object to.
|
|
1058
1590
|
|
|
1059
|
-
|
|
1060
|
-
|
|
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
|
-
|
|
1063
|
-
|
|
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
|
-
|
|
1066
|
-
|
|
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
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1607
|
+
class StringObject(
|
|
1608
|
+
_NonIndexableValueObjectBase[bytes, bytes],
|
|
1609
|
+
_RangeableObjectMixin,
|
|
1610
|
+
):
|
|
1611
|
+
"""A string simulation object.
|
|
1076
1612
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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
|
-
|
|
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})"
|
|
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
|
-
|
|
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
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
-
|
|
1114
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1126
|
-
path
|
|
1728
|
+
handle: The GPI handle to the simulator object.
|
|
1729
|
+
path: Path to this handle.
|
|
1127
1730
|
|
|
1128
1731
|
Returns:
|
|
1129
|
-
|
|
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
|
|
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
|