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