cocotb 1.9.2__cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cocotb might be problematic. Click here for more details.

Files changed (89) hide show
  1. cocotb/ANSI.py +92 -0
  2. cocotb/__init__.py +371 -0
  3. cocotb/_deprecation.py +36 -0
  4. cocotb/_py_compat.py +63 -0
  5. cocotb/_sim_versions.py +145 -0
  6. cocotb/_vendor/__init__.py +0 -0
  7. cocotb/_vendor/distutils_version.py +346 -0
  8. cocotb/_version.py +8 -0
  9. cocotb/binary.py +858 -0
  10. cocotb/clock.py +174 -0
  11. cocotb/config.py +289 -0
  12. cocotb/decorators.py +332 -0
  13. cocotb/handle.py +1175 -0
  14. cocotb/ipython_support.py +92 -0
  15. cocotb/libs/libcocotb.so +0 -0
  16. cocotb/libs/libcocotbfli_modelsim.so +0 -0
  17. cocotb/libs/libcocotbutils.so +0 -0
  18. cocotb/libs/libcocotbvhpi_aldec.so +0 -0
  19. cocotb/libs/libcocotbvhpi_ius.so +0 -0
  20. cocotb/libs/libcocotbvhpi_modelsim.so +0 -0
  21. cocotb/libs/libcocotbvhpi_nvc.so +0 -0
  22. cocotb/libs/libcocotbvpi_aldec.so +0 -0
  23. cocotb/libs/libcocotbvpi_ghdl.so +0 -0
  24. cocotb/libs/libcocotbvpi_icarus.vpl +0 -0
  25. cocotb/libs/libcocotbvpi_ius.so +0 -0
  26. cocotb/libs/libcocotbvpi_modelsim.so +0 -0
  27. cocotb/libs/libcocotbvpi_vcs.so +0 -0
  28. cocotb/libs/libcocotbvpi_verilator.so +0 -0
  29. cocotb/libs/libembed.so +0 -0
  30. cocotb/libs/libgpi.so +0 -0
  31. cocotb/libs/libgpilog.so +0 -0
  32. cocotb/libs/libpygpilog.so +0 -0
  33. cocotb/log.py +303 -0
  34. cocotb/memdebug.py +35 -0
  35. cocotb/outcomes.py +56 -0
  36. cocotb/queue.py +179 -0
  37. cocotb/regression.py +933 -0
  38. cocotb/result.py +209 -0
  39. cocotb/runner.py +1400 -0
  40. cocotb/scheduler.py +1099 -0
  41. cocotb/share/def/.gitignore +2 -0
  42. cocotb/share/def/README.md +4 -0
  43. cocotb/share/def/aldec.def +61 -0
  44. cocotb/share/def/ghdl.def +43 -0
  45. cocotb/share/def/icarus.def +43 -0
  46. cocotb/share/def/modelsim.def +137 -0
  47. cocotb/share/include/cocotb_utils.h +93 -0
  48. cocotb/share/include/embed.h +56 -0
  49. cocotb/share/include/exports.h +20 -0
  50. cocotb/share/include/gpi.h +265 -0
  51. cocotb/share/include/gpi_logging.h +212 -0
  52. cocotb/share/include/py_gpi_logging.h +30 -0
  53. cocotb/share/include/vhpi_user_ext.h +26 -0
  54. cocotb/share/include/vpi_user_ext.h +55 -0
  55. cocotb/share/lib/verilator/verilator.cpp +196 -0
  56. cocotb/share/makefiles/Makefile.deprecations +12 -0
  57. cocotb/share/makefiles/Makefile.inc +176 -0
  58. cocotb/share/makefiles/Makefile.sim +113 -0
  59. cocotb/share/makefiles/simulators/Makefile.activehdl +79 -0
  60. cocotb/share/makefiles/simulators/Makefile.cvc +94 -0
  61. cocotb/share/makefiles/simulators/Makefile.ghdl +113 -0
  62. cocotb/share/makefiles/simulators/Makefile.icarus +111 -0
  63. cocotb/share/makefiles/simulators/Makefile.ius +125 -0
  64. cocotb/share/makefiles/simulators/Makefile.modelsim +32 -0
  65. cocotb/share/makefiles/simulators/Makefile.nvc +64 -0
  66. cocotb/share/makefiles/simulators/Makefile.questa +171 -0
  67. cocotb/share/makefiles/simulators/Makefile.riviera +183 -0
  68. cocotb/share/makefiles/simulators/Makefile.vcs +98 -0
  69. cocotb/share/makefiles/simulators/Makefile.verilator +86 -0
  70. cocotb/share/makefiles/simulators/Makefile.xcelium +136 -0
  71. cocotb/simulator.cpython-313-i386-linux-gnu.so +0 -0
  72. cocotb/task.py +325 -0
  73. cocotb/triggers.py +1104 -0
  74. cocotb/types/__init__.py +50 -0
  75. cocotb/types/array.py +309 -0
  76. cocotb/types/logic.py +292 -0
  77. cocotb/types/logic_array.py +298 -0
  78. cocotb/types/range.py +198 -0
  79. cocotb/utils.py +698 -0
  80. cocotb/wavedrom.py +199 -0
  81. cocotb/xunit_reporter.py +80 -0
  82. cocotb-1.9.2.dist-info/LICENSE +28 -0
  83. cocotb-1.9.2.dist-info/METADATA +168 -0
  84. cocotb-1.9.2.dist-info/RECORD +89 -0
  85. cocotb-1.9.2.dist-info/WHEEL +8 -0
  86. cocotb-1.9.2.dist-info/entry_points.txt +2 -0
  87. cocotb-1.9.2.dist-info/top_level.txt +21 -0
  88. pygpi/__init__.py +0 -0
  89. pygpi/entry.py +26 -0
cocotb/handle.py ADDED
@@ -0,0 +1,1175 @@
1
+ #!/usr/bin/env python
2
+
3
+ # Copyright (c) 2013 Potential Ventures Ltd
4
+ # Copyright (c) 2013 SolarFlare Communications Inc
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are met:
9
+ # * Redistributions of source code must retain the above copyright
10
+ # notice, this list of conditions and the following disclaimer.
11
+ # * Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ # * Neither the name of Potential Ventures Ltd,
15
+ # SolarFlare Communications Inc nor the
16
+ # names of its contributors may be used to endorse or promote products
17
+ # derived from this software without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ # DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
23
+ # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ # -*- coding: utf-8 -*-
31
+
32
+ import ctypes
33
+ import enum
34
+ import warnings
35
+ from functools import lru_cache
36
+ from typing import Optional
37
+
38
+ import cocotb
39
+ from cocotb import simulator
40
+ from cocotb.binary import BinaryRepresentation, BinaryValue
41
+ from cocotb.log import SimLog
42
+ from cocotb.types import Logic, LogicArray
43
+
44
+ # Only issue a warning for each deprecated attribute access
45
+ _deprecation_warned = set()
46
+
47
+
48
+ class _Limits(enum.IntEnum):
49
+ SIGNED_NBIT = 1
50
+ UNSIGNED_NBIT = 2
51
+ VECTOR_NBIT = 3
52
+
53
+
54
+ @lru_cache(maxsize=None)
55
+ def _value_limits(n_bits, limits):
56
+ """Calculate min/max for given number of bits and limits class"""
57
+ if limits == _Limits.SIGNED_NBIT:
58
+ min_val = -(2 ** (n_bits - 1))
59
+ max_val = 2 ** (n_bits - 1) - 1
60
+ elif limits == _Limits.UNSIGNED_NBIT:
61
+ min_val = 0
62
+ max_val = 2**n_bits - 1
63
+ else:
64
+ min_val = -(2 ** (n_bits - 1))
65
+ max_val = 2**n_bits - 1
66
+
67
+ return min_val, max_val
68
+
69
+
70
+ class SimHandleBase:
71
+ """Base class for all simulation objects.
72
+
73
+ We maintain a handle which we can use for GPI calls.
74
+ """
75
+
76
+ # For backwards compatibility we support a mapping of old member names
77
+ # which may alias with the simulator hierarchy. In these cases the
78
+ # simulator result takes priority, only falling back to the python member
79
+ # if there is no colliding object in the elaborated design.
80
+ _compat_mapping = {
81
+ "log": "_log",
82
+ "fullname": "_fullname",
83
+ "name": "_name",
84
+ }
85
+
86
+ def __init__(self, handle, path):
87
+ """
88
+ .. Constructor. This RST comment works around sphinx-doc/sphinx#6885
89
+
90
+ Args:
91
+ handle (int): The GPI handle to the simulator object.
92
+ path (str): Path to this handle, ``None`` if root.
93
+ """
94
+ self._handle = handle
95
+ self._len: Optional[int] = None
96
+ """The "length" (the number of elements) of the underlying object. For vectors this is the number of bits."""
97
+ self._sub_handles: dict = {}
98
+ """Dictionary of this handle's children."""
99
+ self._invalid_sub_handles: set = set()
100
+ """Python :class:`set` of invalid queries, for caching purposes."""
101
+ self._name: str = self._handle.get_name_string()
102
+ """The name of an object.
103
+
104
+ :meta public:
105
+ """
106
+ self._type: str = self._handle.get_type_string()
107
+ """The type of an object as a string.
108
+
109
+ :meta public:
110
+ """
111
+ self._fullname: str = self._name + "(%s)" % self._type
112
+ """The name of an object with its type appended in parentheses."""
113
+ self._path: str = self._name if path is None else path
114
+ """The path to this handle, or its name if this is the root handle.
115
+
116
+ :meta public:
117
+ """
118
+ self._log = SimLog("cocotb.%s" % self._name)
119
+ """The logging object."""
120
+ self._log.debug("Created")
121
+ self._def_name: str = self._handle.get_definition_name()
122
+ """The name of a GPI object's definition.
123
+
124
+ This is the value of ``vpiDefName`` for VPI, ``vhpiNameP`` for VHPI,
125
+ and ``mti_GetPrimaryName`` for FLI.
126
+ Support for this depends on the specific object type and simulator used.
127
+
128
+ :meta public:
129
+ """
130
+ self._def_file: str = self._handle.get_definition_file()
131
+ """The name of the file that sources the object's definition.
132
+
133
+ This is the value of ``vpiDefFile`` for VPI, ``vhpiFileNameP`` for VHPI,
134
+ and ``mti_GetRegionSourceName`` for FLI.
135
+ Support for this depends on the specific object type and simulator used.
136
+
137
+ :meta public:
138
+ """
139
+
140
+ def get_definition_name(self):
141
+ return self._def_name
142
+
143
+ def get_definition_file(self):
144
+ return self._def_file
145
+
146
+ def __hash__(self):
147
+ return hash(self._handle)
148
+
149
+ def __len__(self):
150
+ """Return the "length" (the number of elements) of the underlying object.
151
+
152
+ For vectors this is the number of bits.
153
+ """
154
+ if self._len is None:
155
+ self._len = self._handle.get_num_elems()
156
+ return self._len
157
+
158
+ def __eq__(self, other):
159
+ """Compare equality of handles.
160
+
161
+ Example usage::
162
+
163
+ if clk == dut.clk:
164
+ do_something()
165
+ """
166
+ if not isinstance(other, SimHandleBase):
167
+ return NotImplemented
168
+ return self._handle == other._handle
169
+
170
+ def __ne__(self, other):
171
+ if not isinstance(other, SimHandleBase):
172
+ return NotImplemented
173
+ return self._handle != other._handle
174
+
175
+ def __repr__(self):
176
+ desc = self._path
177
+ defname = self._def_name
178
+ if defname:
179
+ desc += " with definition " + defname
180
+ deffile = self._def_file
181
+ if deffile:
182
+ desc += " (at " + deffile + ")"
183
+ return type(self).__qualname__ + "(" + desc + ")"
184
+
185
+ def __str__(self):
186
+ return self._path
187
+
188
+ def __setattr__(self, name, value):
189
+ if name in self._compat_mapping:
190
+ if name not in _deprecation_warned:
191
+ warnings.warn(
192
+ "Use of attribute {!r} is deprecated, use {!r} instead".format(
193
+ name, self._compat_mapping[name]
194
+ ),
195
+ DeprecationWarning,
196
+ stacklevel=3,
197
+ )
198
+ _deprecation_warned.add(name)
199
+ return setattr(self, self._compat_mapping[name], value)
200
+ else:
201
+ return object.__setattr__(self, name, value)
202
+
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
+
218
+
219
+ class RegionObject(SimHandleBase):
220
+ """A region object, such as a scope or namespace.
221
+
222
+ Region objects don't have values, they are effectively scopes or namespaces.
223
+ """
224
+
225
+ def __init__(self, handle, path):
226
+ SimHandleBase.__init__(self, handle, path)
227
+ self._discovered = False # True if this object has already been discovered
228
+
229
+ def __iter__(self):
230
+ """Iterate over all known objects in this layer of hierarchy."""
231
+ if not self._discovered:
232
+ self._discover_all()
233
+
234
+ for name, handle in self._sub_handles.items():
235
+ if isinstance(handle, list):
236
+ self._log.debug("Found index list length %d", len(handle))
237
+ for subindex, subhdl in enumerate(handle):
238
+ if subhdl is None:
239
+ self._log.warning(
240
+ "Index %d doesn't exist in %s.%s",
241
+ subindex,
242
+ self._name,
243
+ name,
244
+ )
245
+ continue
246
+ self._log.debug(
247
+ "Yielding index %d from %s (%s)", subindex, name, type(subhdl)
248
+ )
249
+ yield subhdl
250
+ else:
251
+ self._log.debug(
252
+ "Yielding %s of type %s (%s)", name, type(handle), handle._path
253
+ )
254
+ yield handle
255
+
256
+ def _discover_all(self):
257
+ """When iterating or performing IPython tab completion, we run through ahead of
258
+ time and discover all possible children, populating the :any:`_sub_handles`
259
+ mapping. Hierarchy can't change after elaboration so we only have to
260
+ do this once.
261
+ """
262
+ if self._discovered:
263
+ return
264
+ self._log.debug("Discovering all on %s", self._name)
265
+ for thing in self._handle.iterate(simulator.OBJECTS):
266
+ 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
+
274
+ try:
275
+ key = self._sub_handle_key(name)
276
+ except ValueError:
277
+ self._log.debug(
278
+ "Unable to translate handle >%s< to a valid _sub_handle key",
279
+ hdl._name,
280
+ )
281
+ continue
282
+
283
+ self._sub_handles[key] = hdl
284
+
285
+ self._discovered = True
286
+
287
+ def _child_path(self, name) -> str:
288
+ """Return a string of the path of the child :any:`SimHandle` for a given *name*."""
289
+ return self._path + "." + name
290
+
291
+ def _sub_handle_key(self, name):
292
+ """Translate the handle name to a key to use in :any:`_sub_handles` dictionary."""
293
+ return name.split(".")[-1]
294
+
295
+ def __dir__(self):
296
+ """Permits IPython tab completion to work."""
297
+ self._discover_all()
298
+ return super().__dir__() + [str(k) for k in self._sub_handles]
299
+
300
+
301
+ class HierarchyObject(RegionObject):
302
+ """Hierarchy objects are namespace/scope objects."""
303
+
304
+ def __get_sub_handle_by_name(self, name):
305
+ try:
306
+ return self._sub_handles[name]
307
+ except KeyError:
308
+ pass
309
+
310
+ # Cache to avoid a call to the simulator if we already know the name is
311
+ # invalid. Unclear if we care, but we had this before.
312
+ if name in self._invalid_sub_handles:
313
+ return None
314
+
315
+ new_handle = self._handle.get_handle_by_name(name)
316
+
317
+ if not new_handle:
318
+ self._invalid_sub_handles.add(name)
319
+ return None
320
+
321
+ sub_handle = SimHandle(new_handle, self._child_path(name))
322
+ self._sub_handles[name] = sub_handle
323
+ return sub_handle
324
+
325
+ def __setattr__(self, name, value):
326
+ """Provide transparent access to signals via the hierarchy.
327
+
328
+ Slightly hacky version of operator overloading in Python.
329
+
330
+ Raise an :exc:`AttributeError` if users attempt to create new members which
331
+ don't exist in the design.
332
+ """
333
+
334
+ # private attributes pass through directly
335
+ if name.startswith("_"):
336
+ return SimHandleBase.__setattr__(self, name, value)
337
+
338
+ # then try handles
339
+ sub = self.__get_sub_handle_by_name(name)
340
+ if sub is not None:
341
+ warnings.warn(
342
+ "Setting values on handles using the ``dut.handle = value`` syntax is deprecated. "
343
+ "Instead use the ``handle.value = value`` syntax",
344
+ DeprecationWarning,
345
+ stacklevel=2,
346
+ )
347
+ sub.value = value
348
+ return
349
+
350
+ # compat behavior
351
+ if name in self._compat_mapping:
352
+ return SimHandleBase.__setattr__(self, name, value)
353
+
354
+ raise AttributeError(f"{self._name} contains no object named {name}")
355
+
356
+ def __getattr__(self, name):
357
+ """Query the simulator for an object with the specified name
358
+ and cache the result to build a tree of objects.
359
+ """
360
+ if name.startswith("_"):
361
+ return SimHandleBase.__getattr__(self, name)
362
+
363
+ handle = self.__get_sub_handle_by_name(name)
364
+ if handle is not None:
365
+ return handle
366
+
367
+ if name in self._compat_mapping:
368
+ return SimHandleBase.__getattr__(self, name)
369
+
370
+ raise AttributeError(f"{self._name} contains no object named {name}")
371
+
372
+ def _id(self, name, extended: bool = True):
373
+ """Query the simulator for an object with the specified *name*,
374
+ and cache the result to build a tree of objects.
375
+
376
+ If *extended* is ``True``, run the query only for VHDL extended identifiers.
377
+ For Verilog, only ``extended=False`` is supported.
378
+
379
+ :meta public:
380
+ """
381
+ if extended:
382
+ name = "\\" + name + "\\"
383
+
384
+ handle = self.__get_sub_handle_by_name(name)
385
+ if handle is not None:
386
+ return handle
387
+
388
+ raise AttributeError(f"{self._name} contains no object named {name}")
389
+
390
+
391
+ class HierarchyArrayObject(RegionObject):
392
+ """Hierarchy Arrays are containers of Hierarchy Objects."""
393
+
394
+ def _sub_handle_key(self, name):
395
+ """Translate the handle name to a key to use in :any:`_sub_handles` dictionary."""
396
+ # This is slightly hacky, but we need to extract the index from the name
397
+ # See also GEN_IDX_SEP_* in VhpiImpl.h for the VHPI separators.
398
+ #
399
+ # FLI and VHPI: _name(X) where X is the index
400
+ # VHPI(ALDEC): _name__X where X is the index
401
+ # VPI: _name[X] where X is the index
402
+ import re
403
+
404
+ result = re.match(rf"{self._name}__(?P<index>\d+)$", name)
405
+ if not result:
406
+ result = re.match(rf"{self._name}\((?P<index>\d+)\)$", name, re.IGNORECASE)
407
+ if not result:
408
+ result = re.match(rf"{self._name}\[(?P<index>\d+)\]$", name)
409
+
410
+ if result:
411
+ return int(result.group("index"))
412
+ else:
413
+ raise ValueError(f"Unable to match an index pattern: {name}")
414
+
415
+ def __len__(self):
416
+ """Return the "length" of the generate block."""
417
+ if self._len is None:
418
+ if not self._discovered:
419
+ self._discover_all()
420
+
421
+ self._len = len(self._sub_handles)
422
+ return self._len
423
+
424
+ def __getitem__(self, index):
425
+ if isinstance(index, slice):
426
+ raise IndexError("Slice indexing is not supported")
427
+ if index in self._sub_handles:
428
+ return self._sub_handles[index]
429
+ new_handle = self._handle.get_handle_by_index(index)
430
+ if not new_handle:
431
+ raise IndexError("%s contains no object at index %d" % (self._name, index))
432
+ path = self._path + "[" + str(index) + "]"
433
+ self._sub_handles[index] = SimHandle(new_handle, path)
434
+ return self._sub_handles[index]
435
+
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
+
441
+ def __setitem__(self, index, value):
442
+ raise TypeError("Not permissible to set %s at index %d" % (self._name, index))
443
+
444
+
445
+ class _AssignmentResult:
446
+ """
447
+ An object that exists solely to provide an error message if the caller
448
+ is not aware of cocotb's meaning of ``<=``.
449
+ """
450
+
451
+ def __init__(self, signal, value):
452
+ self._signal = signal
453
+ self._value = value
454
+
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
+
464
+
465
+ class NonHierarchyObject(SimHandleBase):
466
+ """Common base class for all non-hierarchy objects."""
467
+
468
+ def __iter__(self):
469
+ return iter(())
470
+
471
+ @property
472
+ def value(self):
473
+ """The value of this simulation object.
474
+
475
+ .. note::
476
+ When setting this property, the value is stored by the :class:`~cocotb.scheduler.Scheduler`
477
+ and all stored values are written at the same time at the end of the current simulator time step.
478
+
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
+
487
+ @value.setter
488
+ def value(self, value):
489
+ self._set_value(value, cocotb.scheduler._schedule_write)
490
+
491
+ def setimmediatevalue(self, value):
492
+ """Assign a value to this simulation object immediately."""
493
+
494
+ def _call_now(handle, f, *args):
495
+ f(*args)
496
+
497
+ self._set_value(value, _call_now)
498
+
499
+ def _set_value(self, value, call_sim):
500
+ """This should be overriden in subclasses.
501
+
502
+ This is used to implement both the setter for :attr:`value`, and the
503
+ :meth:`setimmediatevalue` method.
504
+
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
+
514
+ def __le__(self, value):
515
+ """Overload less-than-or-equal-to operator to provide an HDL-like shortcut.
516
+
517
+ Example:
518
+ >>> module.signal <= 2
519
+ """
520
+ warnings.warn(
521
+ "Setting values on handles using the ``handle <= value`` syntax is deprecated. "
522
+ "Instead use the ``handle.value = value`` syntax",
523
+ DeprecationWarning,
524
+ stacklevel=2,
525
+ )
526
+ self.value = value
527
+ return _AssignmentResult(self, value)
528
+
529
+ def __eq__(self, other):
530
+ """Equality comparator for non-hierarchy objects
531
+
532
+ If ``other`` is not a :class:`SimHandleBase` instance the comparison
533
+ uses the comparison method of the ``other`` object against our
534
+ ``.value``.
535
+ """
536
+ if isinstance(other, SimHandleBase):
537
+ return SimHandleBase.__eq__(self, other)
538
+ return self.value == other
539
+
540
+ def __ne__(self, other):
541
+ if isinstance(other, SimHandleBase):
542
+ return SimHandleBase.__ne__(self, other)
543
+ return self.value != other
544
+
545
+ # Re-define hash because we defined __eq__
546
+ def __hash__(self):
547
+ return SimHandleBase.__hash__(self)
548
+
549
+
550
+ class ConstantObject(NonHierarchyObject):
551
+ """An object which has a value that can be read, but not set.
552
+
553
+ The value is cached in the class since it is fixed at elaboration
554
+ time and won't change within a simulation.
555
+ """
556
+
557
+ def __init__(self, handle, path, handle_type):
558
+ """
559
+ Args:
560
+ handle (int): The GPI handle to the simulator object.
561
+ path (str): Path to this handle, ``None`` if root.
562
+ handle_type: The type of the handle
563
+ (``simulator.INTEGER``, ``simulator.ENUM``,
564
+ ``simulator.REAL``, ``simulator.STRING``).
565
+ """
566
+ NonHierarchyObject.__init__(self, handle, path)
567
+ if handle_type in [simulator.INTEGER, simulator.ENUM]:
568
+ self._value = self._handle.get_signal_val_long()
569
+ elif handle_type == simulator.REAL:
570
+ self._value = self._handle.get_signal_val_real()
571
+ elif handle_type == simulator.STRING:
572
+ self._value = self._handle.get_signal_val_str()
573
+ else:
574
+ val = self._handle.get_signal_val_binstr()
575
+ self._value = BinaryValue(n_bits=len(val))
576
+ try:
577
+ self._value.binstr = val
578
+ except Exception:
579
+ self._value = val
580
+
581
+ def __int__(self):
582
+ return int(self.value)
583
+
584
+ def __float__(self):
585
+ return float(self.value)
586
+
587
+ @NonHierarchyObject.value.getter
588
+ def value(self):
589
+ """The value of this simulation object."""
590
+ return self._value
591
+
592
+ def __str__(self):
593
+ if isinstance(self.value, bytes):
594
+ StringObject._emit_str_warning(self)
595
+ return self.value.decode("ascii")
596
+ else:
597
+ ModifiableObject._emit_str_warning(self)
598
+ return str(self.value)
599
+
600
+
601
+ class NonHierarchyIndexableObject(NonHierarchyObject):
602
+ """A non-hierarchy indexable object.
603
+
604
+ Getting and setting the current value of an array is done
605
+ by iterating through sub-handles in left-to-right order.
606
+
607
+ Given an HDL array ``arr``:
608
+
609
+ +--------------+---------------------+--------------------------------------------------------------+
610
+ | Verilog | VHDL | ``arr.value`` is equivalent to |
611
+ +==============+=====================+==============================================================+
612
+ | ``arr[4:7]`` | ``arr(4 to 7)`` | ``[arr[4].value, arr[5].value, arr[6].value, arr[7].value]`` |
613
+ +--------------+---------------------+--------------------------------------------------------------+
614
+ | ``arr[7:4]`` | ``arr(7 downto 4)`` | ``[arr[7].value, arr[6].value, arr[5].value, arr[4].value]`` |
615
+ +--------------+---------------------+--------------------------------------------------------------+
616
+
617
+ When setting the signal as in ``arr.value = ...``, the same index equivalence as noted in the table holds.
618
+
619
+ .. warning::
620
+ Assigning a value to a sub-handle:
621
+
622
+ - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as a list then updates index 0)
623
+ - **Correct**: ``dut.some_array[0].value = 1``
624
+ """
625
+
626
+ def __init__(self, handle, path):
627
+ NonHierarchyObject.__init__(self, handle, path)
628
+ self._range = self._handle.get_range()
629
+
630
+ def __setitem__(self, index, value):
631
+ """Provide transparent assignment to indexed array handles."""
632
+ warnings.warn(
633
+ "Setting values on handles using the ``dut.handle[i] = value`` syntax is deprecated. "
634
+ "Instead use the ``handle[i].value = value`` syntax",
635
+ DeprecationWarning,
636
+ stacklevel=2,
637
+ )
638
+ self[index].value = value
639
+
640
+ def __getitem__(self, index):
641
+ if isinstance(index, slice):
642
+ raise IndexError("Slice indexing is not supported")
643
+ if self._range is None:
644
+ raise IndexError(
645
+ "%s is not indexable. Unable to get object at index %d"
646
+ % (self._fullname, index)
647
+ )
648
+ if index in self._sub_handles:
649
+ return self._sub_handles[index]
650
+ new_handle = self._handle.get_handle_by_index(index)
651
+ if not new_handle:
652
+ raise IndexError(
653
+ "%s contains no object at index %d" % (self._fullname, index)
654
+ )
655
+ path = self._path + "[" + str(index) + "]"
656
+ self._sub_handles[index] = SimHandle(new_handle, path)
657
+ return self._sub_handles[index]
658
+
659
+ def __iter__(self):
660
+ if self._range is None:
661
+ return
662
+
663
+ self._log.debug("Iterating with range [%d:%d]", self._range[0], self._range[1])
664
+ for i in self._range_iter(self._range[0], self._range[1]):
665
+ try:
666
+ result = self[i]
667
+ yield result
668
+ except IndexError:
669
+ continue
670
+
671
+ def _range_iter(self, left, right):
672
+ if left > right:
673
+ while left >= right:
674
+ yield left
675
+ left = left - 1
676
+ else:
677
+ while left <= right:
678
+ yield left
679
+ left = left + 1
680
+
681
+ @NonHierarchyObject.value.getter
682
+ def value(self) -> list:
683
+ # Don't use self.__iter__, because it has an unwanted `except IndexError`
684
+ return [self[i].value for i in self._range_iter(self._range[0], self._range[1])]
685
+
686
+ def _set_value(self, value, call_sim):
687
+ """Assign value from a list of same length to an array in left-to-right order.
688
+ Index 0 of the list maps to the left-most index in the array.
689
+
690
+ See the docstring for this class.
691
+ """
692
+ if type(value) is not list:
693
+ raise TypeError(
694
+ "Assigning non-list value to object {} of type {}".format(
695
+ self._name, type(self)
696
+ )
697
+ )
698
+ if len(value) != len(self):
699
+ raise ValueError(
700
+ "Assigning list of length %d to object %s of length %d"
701
+ % (len(value), self._name, len(self))
702
+ )
703
+ for val_idx, self_idx in enumerate(
704
+ self._range_iter(self._range[0], self._range[1])
705
+ ):
706
+ self[self_idx]._set_value(value[val_idx], call_sim)
707
+
708
+
709
+ class NonConstantObject(NonHierarchyIndexableObject):
710
+ """A non-constant object"""
711
+
712
+ # FIXME: what is the difference to ModifiableObject? Explain in docstring.
713
+
714
+ def drivers(self):
715
+ """An iterator for gathering all drivers for a signal.
716
+
717
+ This is currently only available for VPI.
718
+ Also, only a few simulators implement this.
719
+ """
720
+ return self._handle.iterate(simulator.DRIVERS)
721
+
722
+ def loads(self):
723
+ """An iterator for gathering all loads on a signal.
724
+
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
+
730
+
731
+ class _SetAction:
732
+ """Base class representing the type of action used while write-accessing a handle."""
733
+
734
+ pass
735
+
736
+
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
+
741
+ def __init__(self, value):
742
+ self.value = value
743
+
744
+
745
+ class Deposit(_SetValueAction):
746
+ """Action used for placing a value into a given handle."""
747
+
748
+ def _as_gpi_args_for(self, hdl):
749
+ return self.value, 0 # GPI_DEPOSIT
750
+
751
+
752
+ class Force(_SetValueAction):
753
+ """Action used to force a handle to a given value until a release is applied."""
754
+
755
+ def _as_gpi_args_for(self, hdl):
756
+ return self.value, 1 # GPI_FORCE
757
+
758
+
759
+ class Freeze(_SetAction):
760
+ """Action used to make a handle keep its current value until a release is used."""
761
+
762
+ def _as_gpi_args_for(self, hdl):
763
+ return hdl.value, 1 # GPI_FORCE
764
+
765
+
766
+ class Release(_SetAction):
767
+ """Action used to stop the effects of a previously applied force/freeze action."""
768
+
769
+ def _as_gpi_args_for(self, hdl):
770
+ return 0, 2 # GPI_RELEASE
771
+
772
+
773
+ class ModifiableObject(NonConstantObject):
774
+ """Base class for simulator objects whose values can be modified."""
775
+
776
+ def _set_value(self, value, call_sim):
777
+ """Set the value of the underlying simulation object to *value*.
778
+
779
+ This operation will fail unless the handle refers to a modifiable
780
+ object, e.g. net, signal or variable.
781
+
782
+ We determine the library call to make based on the type of the value
783
+ because assigning integers less than 32 bits is faster.
784
+
785
+ Args:
786
+ value (cocotb.binary.BinaryValue, int):
787
+ The value to drive onto the simulator object.
788
+
789
+ Raises:
790
+ TypeError: If target has an unsupported type for value assignment.
791
+
792
+ OverflowError: If int value is out of the range that can be represented
793
+ by the target. -2**(Nbits - 1) <= value <= 2**Nbits - 1
794
+
795
+ .. deprecated:: 1.5
796
+ :class:`ctypes.Structure` objects are no longer accepted as values for assignment.
797
+ Convert the struct object to a :class:`~cocotb.binary.BinaryValue` before assignment using
798
+ ``BinaryValue(value=bytes(struct_obj), n_bits=len(signal))`` instead.
799
+
800
+ .. deprecated:: 1.5
801
+ :class:`dict` objects are no longer accepted as values for assignment.
802
+ Convert the dictionary to an integer before assignment using
803
+ ``sum(v << (d['bits'] * i) for i, v in enumerate(d['values']))``.
804
+ """
805
+ value, set_action = self._check_for_set_action(value)
806
+
807
+ if isinstance(value, int):
808
+ min_val, max_val = _value_limits(len(self), _Limits.VECTOR_NBIT)
809
+ if min_val <= value <= max_val:
810
+ if len(self) <= 32:
811
+ call_sim(self, self._handle.set_signal_val_int, set_action, value)
812
+ return
813
+
814
+ if value < 0:
815
+ value = BinaryValue(
816
+ value=value,
817
+ n_bits=len(self),
818
+ bigEndian=False,
819
+ binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT,
820
+ )
821
+ else:
822
+ value = BinaryValue(
823
+ value=value,
824
+ n_bits=len(self),
825
+ bigEndian=False,
826
+ binaryRepresentation=BinaryRepresentation.UNSIGNED,
827
+ )
828
+ else:
829
+ raise OverflowError(
830
+ "Int value ({!r}) out of range for assignment of {!r}-bit signal ({!r})".format(
831
+ value, len(self), self._name
832
+ )
833
+ )
834
+
835
+ if isinstance(value, ctypes.Structure):
836
+ warnings.warn(
837
+ "`ctypes.Structure` values are no longer accepted for value assignment. "
838
+ "Use `BinaryValue(value=bytes(struct_obj), n_bits=len(signal))` instead",
839
+ DeprecationWarning,
840
+ stacklevel=3,
841
+ )
842
+ value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self))
843
+
844
+ elif isinstance(value, dict):
845
+ warnings.warn(
846
+ "dict values are no longer accepted for value assignment. "
847
+ "Use `sum(v << (d['bits'] * i) for i, v in enumerate(d['values']))` "
848
+ "to convert the dict to an int before assignment.",
849
+ DeprecationWarning,
850
+ stacklevel=3,
851
+ )
852
+ # We're given a dictionary with a list of values and a bit size...
853
+ num = 0
854
+ vallist = list(value["values"])
855
+ vallist.reverse()
856
+ if len(vallist) * value["bits"] != len(self):
857
+ raise TypeError(
858
+ "Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long"
859
+ % (
860
+ len(value["values"]),
861
+ value["bits"],
862
+ len(value["values"]) * value["bits"],
863
+ len(self),
864
+ )
865
+ )
866
+
867
+ for val in vallist:
868
+ num = (num << value["bits"]) + val
869
+ value = BinaryValue(value=num, n_bits=len(self), bigEndian=False)
870
+
871
+ elif isinstance(value, LogicArray):
872
+ if len(self) != len(value):
873
+ raise ValueError(
874
+ f"cannot assign value of length {len(value)} to handle of length {len(self)}"
875
+ )
876
+ value = value.to_BinaryValue()
877
+
878
+ elif isinstance(value, Logic):
879
+ if len(self) != 1:
880
+ raise ValueError(
881
+ f"cannot assign value of length 1 to handle of length {len(self)}"
882
+ )
883
+ value = BinaryValue(str(value))
884
+
885
+ elif isinstance(value, BinaryValue):
886
+ if len(value) != len(self):
887
+ raise ValueError(
888
+ f"cannot assign value of length {len(value)} to handle of length {len(self)}"
889
+ )
890
+
891
+ else:
892
+ raise TypeError(
893
+ "Unsupported type for value assignment: {} ({!r})".format(
894
+ type(value), value
895
+ )
896
+ )
897
+
898
+ call_sim(self, self._handle.set_signal_val_binstr, set_action, value.binstr)
899
+
900
+ def _check_for_set_action(self, value):
901
+ if not isinstance(value, _SetAction):
902
+ return value, 0 # GPI_DEPOSIT
903
+ return value._as_gpi_args_for(self)
904
+
905
+ @NonConstantObject.value.getter
906
+ def value(self) -> BinaryValue:
907
+ binstr = self._handle.get_signal_val_binstr()
908
+ # Skip BinaryValue.assign() as we know we are using a binstr
909
+ result = BinaryValue(n_bits=len(binstr))
910
+ # Skip the permitted characters check as we trust the simulator
911
+ result._set_trusted_binstr(binstr)
912
+ return result
913
+
914
+ def __int__(self):
915
+ return int(self.value)
916
+
917
+ def _emit_str_warning(self):
918
+ warnings.warn(
919
+ "`str({t})` is deprecated, and in future will return `{t}._path`. "
920
+ "To get a string representation of the value, use `str({t}.value)`.".format(
921
+ t=type(self).__qualname__
922
+ ),
923
+ FutureWarning,
924
+ stacklevel=3,
925
+ )
926
+
927
+ def __str__(self):
928
+ self._emit_str_warning()
929
+ return str(self.value)
930
+
931
+
932
+ class RealObject(ModifiableObject):
933
+ """Specific object handle for Real signals and variables."""
934
+
935
+ def _set_value(self, value, call_sim):
936
+ """Set the value of the underlying simulation object to value.
937
+
938
+ This operation will fail unless the handle refers to a modifiable
939
+ object, e.g. net, signal or variable.
940
+
941
+ Args:
942
+ value (float): The value to drive onto the simulator object.
943
+
944
+ Raises:
945
+ TypeError: If target has an unsupported type for
946
+ real value assignment.
947
+ """
948
+ value, set_action = self._check_for_set_action(value)
949
+
950
+ try:
951
+ value = float(value)
952
+ except ValueError:
953
+ raise TypeError(
954
+ "Unsupported type for real value assignment: {} ({!r})".format(
955
+ type(value), value
956
+ )
957
+ )
958
+
959
+ call_sim(self, self._handle.set_signal_val_real, set_action, value)
960
+
961
+ @ModifiableObject.value.getter
962
+ def value(self) -> float:
963
+ return self._handle.get_signal_val_real()
964
+
965
+ def __float__(self):
966
+ return float(self.value)
967
+
968
+
969
+ class EnumObject(ModifiableObject):
970
+ """Specific object handle for enumeration signals and variables."""
971
+
972
+ def _set_value(self, value, call_sim):
973
+ """Set the value of the underlying simulation object to *value*.
974
+
975
+ This operation will fail unless the handle refers to a modifiable
976
+ object, e.g. net, signal or variable.
977
+
978
+ Args:
979
+ value (int): The value to drive onto the simulator object.
980
+
981
+ Raises:
982
+ TypeError: If target has an unsupported type for
983
+ integer value assignment.
984
+ """
985
+ value, set_action = self._check_for_set_action(value)
986
+
987
+ if isinstance(value, BinaryValue):
988
+ value = int(value)
989
+ elif not isinstance(value, int):
990
+ raise TypeError(
991
+ "Unsupported type for enum value assignment: {} ({!r})".format(
992
+ type(value), value
993
+ )
994
+ )
995
+
996
+ min_val, max_val = _value_limits(32, _Limits.UNSIGNED_NBIT)
997
+ if min_val <= value <= max_val:
998
+ call_sim(self, self._handle.set_signal_val_int, set_action, value)
999
+ else:
1000
+ raise OverflowError(
1001
+ "Int value ({!r}) out of range for assignment of enum signal ({!r})".format(
1002
+ value, self._name
1003
+ )
1004
+ )
1005
+
1006
+ @ModifiableObject.value.getter
1007
+ def value(self) -> int:
1008
+ return self._handle.get_signal_val_long()
1009
+
1010
+
1011
+ class IntegerObject(ModifiableObject):
1012
+ """Specific object handle for integer and enumeration signals and variables."""
1013
+
1014
+ def _set_value(self, value, call_sim):
1015
+ """Set the value of the underlying simulation object to *value*.
1016
+
1017
+ This operation will fail unless the handle refers to a modifiable
1018
+ object, e.g. net, signal or variable.
1019
+
1020
+ Args:
1021
+ value (int): The value to drive onto the simulator object.
1022
+
1023
+ Raises:
1024
+ TypeError: If target has an unsupported type for
1025
+ integer value assignment.
1026
+
1027
+ OverflowError: If value is out of range for assignment
1028
+ of 32-bit IntegerObject.
1029
+ """
1030
+ value, set_action = self._check_for_set_action(value)
1031
+
1032
+ if isinstance(value, BinaryValue):
1033
+ value = int(value)
1034
+ elif not isinstance(value, int):
1035
+ raise TypeError(
1036
+ "Unsupported type for integer value assignment: {} ({!r})".format(
1037
+ type(value), value
1038
+ )
1039
+ )
1040
+
1041
+ min_val, max_val = _value_limits(32, _Limits.SIGNED_NBIT)
1042
+ if min_val <= value <= max_val:
1043
+ call_sim(self, self._handle.set_signal_val_int, set_action, value)
1044
+ else:
1045
+ raise OverflowError(
1046
+ "Int value ({!r}) out of range for assignment of integer signal ({!r})".format(
1047
+ value, self._name
1048
+ )
1049
+ )
1050
+
1051
+ @ModifiableObject.value.getter
1052
+ def value(self) -> int:
1053
+ return self._handle.get_signal_val_long()
1054
+
1055
+
1056
+ class StringObject(ModifiableObject):
1057
+ """Specific object handle for String variables."""
1058
+
1059
+ def _set_value(self, value, call_sim):
1060
+ """Set the value of the underlying simulation object to *value*.
1061
+
1062
+ This operation will fail unless the handle refers to a modifiable
1063
+ object, e.g. net, signal or variable.
1064
+
1065
+ Args:
1066
+ value (bytes): The value to drive onto the simulator object.
1067
+
1068
+ Raises:
1069
+ TypeError: If target has an unsupported type for
1070
+ string value assignment.
1071
+
1072
+ .. versionchanged:: 1.4
1073
+ Takes :class:`bytes` instead of :class:`str`.
1074
+ Users are now expected to choose an encoding when using these objects.
1075
+ As a convenience, when assigning :class:`str` values, ASCII encoding will be used as a safe default.
1076
+
1077
+ """
1078
+ value, set_action = self._check_for_set_action(value)
1079
+
1080
+ if isinstance(value, str):
1081
+ warnings.warn(
1082
+ "Handles on string objects will soon not accept `str` objects. "
1083
+ "Please use a bytes object by encoding the string as you see fit. "
1084
+ "`str.encode('ascii')` is typically sufficient.",
1085
+ DeprecationWarning,
1086
+ stacklevel=2,
1087
+ )
1088
+ value = value.encode("ascii") # may throw UnicodeEncodeError
1089
+
1090
+ if not isinstance(value, bytes):
1091
+ raise TypeError(
1092
+ "Unsupported type for string value assignment: {} ({!r})".format(
1093
+ type(value), value
1094
+ )
1095
+ )
1096
+
1097
+ call_sim(self, self._handle.set_signal_val_str, set_action, value)
1098
+
1099
+ @ModifiableObject.value.getter
1100
+ def value(self) -> bytes:
1101
+ return self._handle.get_signal_val_str()
1102
+
1103
+ def _emit_str_warning(self):
1104
+ warnings.warn(
1105
+ "`str({t})` is deprecated, and in future will return `{t}._path`. "
1106
+ "To access the `bytes` value of this handle, use `{t}.value`.".format(
1107
+ t=type(self).__qualname__
1108
+ ),
1109
+ FutureWarning,
1110
+ stacklevel=3,
1111
+ )
1112
+
1113
+ def __str__(self):
1114
+ self._emit_str_warning()
1115
+ return self.value.decode("ascii")
1116
+
1117
+
1118
+ _handle2obj = {}
1119
+
1120
+
1121
+ def SimHandle(handle, path=None):
1122
+ """Factory function to create the correct type of `SimHandle` object.
1123
+
1124
+ Args:
1125
+ handle (int): The GPI handle to the simulator object.
1126
+ path (str): Path to this handle, ``None`` if root.
1127
+
1128
+ Returns:
1129
+ The `SimHandle` object.
1130
+
1131
+ Raises:
1132
+ NotImplementedError: If no matching object for GPI type could be found.
1133
+ """
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
+
1147
+ # Enforce singletons since it's possible to retrieve handles avoiding
1148
+ # the hierarchy by getting driver/load information
1149
+ global _handle2obj
1150
+ try:
1151
+ return _handle2obj[handle]
1152
+ except KeyError:
1153
+ pass
1154
+
1155
+ 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
+ if t not in _type2cls:
1169
+ raise NotImplementedError(
1170
+ "Couldn't find a matching object for GPI type %s(%d) (path=%s)"
1171
+ % (handle.get_type_string(), t, path)
1172
+ )
1173
+ obj = _type2cls[t](handle, path)
1174
+ _handle2obj[handle] = obj
1175
+ return obj