CIRCT 22.0.0git
Loading...
Searching...
No Matches
types.py
Go to the documentation of this file.
1# ===-----------------------------------------------------------------------===#
2# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3# See https://llvm.org/LICENSE.txt for license information.
4# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5# ===-----------------------------------------------------------------------===#
6#
7# The structure of the Python classes and hierarchy roughly mirrors the C++
8# side, but wraps the C++ objects. The wrapper classes sometimes add convenience
9# functionality and serve to return wrapped versions of the returned objects.
10#
11# ===-----------------------------------------------------------------------===#
12
13from __future__ import annotations
14
15from . import esiCppAccel as cpp
16
17from typing import TYPE_CHECKING
18
19if TYPE_CHECKING:
20 from .accelerator import HWModule
21
22from concurrent.futures import Future
23from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
24import sys
25import traceback
26
27
28def _get_esi_type(cpp_type: cpp.Type):
29 """Get the wrapper class for a C++ type."""
30 if isinstance(cpp_type, cpp.ChannelType):
31 return _get_esi_type(cpp_type.inner)
32
33 for cpp_type_cls, wrapper_cls in __esi_mapping.items():
34 if isinstance(cpp_type, cpp_type_cls):
35 return wrapper_cls.wrap_cpp(cpp_type)
36 return ESIType.wrap_cpp(cpp_type)
37
38
39# Mapping from C++ types to wrapper classes
40__esi_mapping: Dict[Type, Type] = {}
41
42
43class ESIType:
44
45 def __init__(self, id: str):
46 self._init_from_cpp(cpp.Type(id))
47
48 @classmethod
49 def wrap_cpp(cls, cpp_type: cpp.Type):
50 """Wrap a C++ ESI type with its corresponding Python ESI Type."""
51 instance = cls.__new__(cls)
52 instance._init_from_cpp(cpp_type)
53 return instance
54
55 def _init_from_cpp(self, cpp_type: cpp.Type):
56 """Initialize instance attributes from a C++ type object."""
57 self.cpp_type = cpp_type
58
59 @property
60 def supports_host(self) -> Tuple[bool, Optional[str]]:
61 """Does this type support host communication via Python? Returns either
62 '(True, None)' if it is, or '(False, reason)' if it is not."""
63
64 if self.bit_width % 8 != 0:
65 return (False, "runtime only supports types with multiple of 8 bits")
66 return (True, None)
67
68 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
69 """Is a Python object compatible with HW type? Returns either '(True,
70 None)' if it is, or '(False, reason)' if it is not."""
71 assert False, "unimplemented"
72
73 @property
74 def bit_width(self) -> int:
75 """Size of this type, in bits. Negative for unbounded types."""
76 assert False, "unimplemented"
77
78 @property
79 def max_size(self) -> int:
80 """Maximum size of a value of this type, in bytes."""
81 bitwidth = int((self.bit_width + 7) / 8)
82 if bitwidth < 0:
83 return bitwidth
84 return bitwidth
85
86 def serialize(self, obj) -> bytearray:
87 """Convert a Python object to a bytearray."""
88 assert False, "unimplemented"
89
90 def deserialize(self, data: bytearray) -> Tuple[object, bytearray]:
91 """Convert a bytearray to a Python object. Return the object and the
92 leftover bytes."""
93 assert False, "unimplemented"
94
95 def __str__(self) -> str:
96 return str(self.cpp_type)
97
98
100
101 def __init__(self, id: str):
102 self._init_from_cpp(cpp.VoidType(id))
103
104 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
105 if obj is not None:
106 return (False, f"void type cannot must represented by None, not {obj}")
107 return (True, None)
108
109 @property
110 def bit_width(self) -> int:
111 return 8
112
113 def serialize(self, obj) -> bytearray:
114 # By convention, void is represented by a single byte of value 0.
115 return bytearray([0])
116
117 def deserialize(self, data: bytearray) -> Tuple[object, bytearray]:
118 if len(data) == 0:
119 raise ValueError(f"void type cannot be represented by {data}")
120 return (None, data[1:])
121
122
123__esi_mapping[cpp.VoidType] = VoidType
124
125
127
128 def __init__(self, id: str, width: int):
129 self._init_from_cpp(cpp.BitsType(id, width))
130
131 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
132 if not isinstance(obj, (bytearray, bytes, list)):
133 return (False, f"invalid type: {type(obj)}")
134 if isinstance(obj, list) and not all(
135 [isinstance(b, int) and b.bit_length() <= 8 for b in obj]):
136 return (False, f"list item too large: {obj}")
137 if len(obj) != self.max_size:
138 return (False, f"wrong size: {len(obj)}")
139 return (True, None)
140
141 @property
142 def bit_width(self) -> int:
143 return self.cpp_type.width
144
145 def serialize(self, obj: Union[bytearray, bytes, List[int]]) -> bytearray:
146 if isinstance(obj, bytearray):
147 return obj
148 if isinstance(obj, bytes) or isinstance(obj, list):
149 return bytearray(obj)
150 raise ValueError(f"cannot convert {obj} to bytearray")
151
152 def deserialize(self, data: bytearray) -> Tuple[bytearray, bytearray]:
153 return (data[0:self.max_size], data[self.max_size:])
154
155
156__esi_mapping[cpp.BitsType] = BitsType
157
158
160
161 def __init__(self, id: str, width: int):
162 self._init_from_cpp(cpp.IntegerType(id, width))
163
164 @property
165 def bit_width(self) -> int:
166 return self.cpp_type.width
167
168
170
171 def __init__(self, id: str, width: int):
172 self._init_from_cpp(cpp.UIntType(id, width))
173
174 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
175 if not isinstance(obj, int):
176 return (False, f"must be an int, not {type(obj)}")
177 if obj < 0 or obj.bit_length() > self.bit_widthbit_width:
178 return (False, f"out of range: {obj}")
179 return (True, None)
180
181 def __str__(self) -> str:
182 return f"uint{self.bit_width}"
183
184 def serialize(self, obj: int) -> bytearray:
185 return bytearray(int.to_bytes(obj, self.max_sizemax_size, "little"))
186
187 def deserialize(self, data: bytearray) -> Tuple[int, bytearray]:
188 return (int.from_bytes(data[0:self.max_sizemax_size],
189 "little"), data[self.max_sizemax_size:])
190
191
192__esi_mapping[cpp.UIntType] = UIntType
193
194
196
197 def __init__(self, id: str, width: int):
198 self._init_from_cpp(cpp.SIntType(id, width))
199
200 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
201 if not isinstance(obj, int):
202 return (False, f"must be an int, not {type(obj)}")
203 if obj < 0:
204 if (-1 * obj) > 2**(self.bit_widthbit_width - 1):
205 return (False, f"out of range: {obj}")
206 elif obj < 0:
207 if obj >= 2**(self.bit_widthbit_width - 1) - 1:
208 return (False, f"out of range: {obj}")
209 return (True, None)
210
211 def __str__(self) -> str:
212 return f"sint{self.bit_width}"
213
214 def serialize(self, obj: int) -> bytearray:
215 return bytearray(int.to_bytes(obj, self.max_sizemax_size, "little", signed=True))
216
217 def deserialize(self, data: bytearray) -> Tuple[int, bytearray]:
218 return (int.from_bytes(data[0:self.max_sizemax_size], "little",
219 signed=True), data[self.max_sizemax_size:])
220
221
222__esi_mapping[cpp.SIntType] = SIntType
223
224
226
227 def __init__(self, id: str, fields: List[Tuple[str, "ESIType"]]):
228 # Convert Python ESIType fields to cpp Type fields
229 cpp_fields = [(name, field_type.cpp_type) for name, field_type in fields]
230 self._init_from_cpp_init_from_cpp(cpp.StructType(id, cpp_fields))
231
232 def _init_from_cpp(self, cpp_type: cpp.StructType):
233 """Initialize instance attributes from a C++ type object."""
234 super()._init_from_cpp(cpp_type)
235 # For wrap_cpp path, we need to convert C++ fields back to Python
236 self.fields = [(name, _get_esi_type(ty)) for (name, ty) in cpp_type.fields]
237
238 @property
239 def bit_width(self) -> int:
240 widths = [ty.bit_width for (_, ty) in self.fields]
241 if any([w < 0 for w in widths]):
242 return -1
243 return sum(widths)
244
245 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
246 fields_count = 0
247 if not isinstance(obj, dict):
248 if not hasattr(obj, "__dict__"):
249 return (False, "must be a dict or have __dict__ attribute")
250 obj = obj.__dict__
251
252 for (fname, ftype) in self.fields:
253 if fname not in obj:
254 return (False, f"missing field '{fname}'")
255 fvalid, reason = ftype.is_valid(obj[fname])
256 if not fvalid:
257 return (False, f"invalid field '{fname}': {reason}")
258 fields_count += 1
259 if fields_count != len(obj):
260 return (False, "missing fields")
261 return (True, None)
262
263 def serialize(self, obj) -> bytearray:
264 ret = bytearray()
265 if not isinstance(obj, dict):
266 obj = obj.__dict__
267 ordered_fields = reversed(
268 self.fields) if self.cpp_type.reverse else self.fields
269 for (fname, ftype) in ordered_fields:
270 fval = obj[fname]
271 ret.extend(ftype.serialize(fval))
272 return ret
273
274 def deserialize(self, data: bytearray) -> Tuple[Dict[str, Any], bytearray]:
275 ret = {}
276 ordered_fields = reversed(
277 self.fields) if self.cpp_type.reverse else self.fields
278 for (fname, ftype) in ordered_fields:
279 (fval, data) = ftype.deserialize(data)
280 ret[fname] = fval
281 return (ret, data)
282
283
284__esi_mapping[cpp.StructType] = StructType
285
286
288
289 def __init__(self, id: str, element_type: "ESIType", size: int):
290 self._init_from_cpp_init_from_cpp(cpp.ArrayType(id, element_type.cpp_type, size))
291
292 def _init_from_cpp(self, cpp_type: cpp.ArrayType):
293 """Initialize instance attributes from a C++ type object."""
294 super()._init_from_cpp(cpp_type)
295 self.element_type = _get_esi_type(cpp_type.element)
296 self.size = cpp_type.size
297
298 @property
299 def bit_width(self) -> int:
300 return self.element_type.bit_width * self.size
301
302 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
303 if not isinstance(obj, list):
304 return (False, f"must be a list, not {type(obj)}")
305 if len(obj) != self.size:
306 return (False, f"wrong size: expected {self.size} not {len(obj)}")
307 for (idx, e) in enumerate(obj):
308 evalid, reason = self.element_type.is_valid(e)
309 if not evalid:
310 return (False, f"invalid element {idx}: {reason}")
311 return (True, None)
312
313 def serialize(self, lst: list) -> bytearray:
314 ret = bytearray()
315 for e in reversed(lst):
316 ret.extend(self.element_type.serialize(e))
317 return ret
318
319 def deserialize(self, data: bytearray) -> Tuple[List[Any], bytearray]:
320 ret = []
321 for _ in range(self.size):
322 (obj, data) = self.element_type.deserialize(data)
323 ret.append(obj)
324 ret.reverse()
325 return (ret, data)
326
327
328__esi_mapping[cpp.ArrayType] = ArrayType
329
330
331class Port:
332 """A unidirectional communication channel. This is the basic communication
333 method with an accelerator."""
334
335 def __init__(self, owner: BundlePort, cpp_port: cpp.ChannelPort):
336 self.owner = owner
337 self.cpp_port = cpp_port
338 self.type = _get_esi_type(cpp_port.type)
339
340 def connect(self, buffer_size: Optional[int] = None):
341 (supports_host, reason) = self.type.supports_host
342 if not supports_host:
343 raise TypeError(f"unsupported type: {reason}")
344
345 opts = cpp.ConnectOptions()
346 opts.buffer_size = buffer_size
347 self.cpp_port.connect(opts)
348 return self
349
350 def disconnect(self):
351 self.cpp_port.disconnect()
352
353
355 """A unidirectional communication channel from the host to the accelerator."""
356
357 def __init__(self, owner: BundlePort, cpp_port: cpp.WriteChannelPort):
358 super().__init__(owner, cpp_port)
359 self.cpp_port: cpp.WriteChannelPort = cpp_port
360
361 def __serialize_msg(self, msg=None) -> bytearray:
362 valid, reason = self.type.is_valid(msg)
363 if not valid:
364 raise ValueError(
365 f"'{msg}' cannot be converted to '{self.type}': {reason}")
366 msg_bytes: bytearray = self.type.serialize(msg)
367 return msg_bytes
368
369 def write(self, msg=None) -> bool:
370 """Write a typed message to the channel. Attempts to serialize 'msg' to what
371 the accelerator expects, but will fail if the object is not convertible to
372 the port type."""
373 self.cpp_port.write(self.__serialize_msg(msg))
374 return True
375
376 def try_write(self, msg=None) -> bool:
377 """Like 'write', but uses the non-blocking tryWrite method of the underlying
378 port. Returns True if the write was successful, False otherwise."""
379 return self.cpp_port.tryWrite(self.__serialize_msg(msg))
380
381
383 """A unidirectional communication channel from the accelerator to the host."""
384
385 def __init__(self, owner: BundlePort, cpp_port: cpp.ReadChannelPort):
386 super().__init__(owner, cpp_port)
387 self.cpp_port: cpp.ReadChannelPort = cpp_port
388
389 def read(self) -> object:
390 """Read a typed message from the channel. Returns a deserialized object of a
391 type defined by the port type."""
392
393 buffer = self.cpp_port.read()
394 (msg, leftover) = self.type.deserialize(buffer)
395 if len(leftover) != 0:
396 raise ValueError(f"leftover bytes: {leftover}")
397 return msg
398
399
401 """A collections of named, unidirectional communication channels."""
402
403 # When creating a new port, we need to determine if it is a service port and
404 # instantiate it correctly.
405 def __new__(cls, owner: HWModule, cpp_port: cpp.BundlePort):
406 # TODO: add a proper registration mechanism for service ports.
407 if isinstance(cpp_port, cpp.Function):
408 return super().__new__(FunctionPort)
409 if isinstance(cpp_port, cpp.Callback):
410 return super().__new__(CallbackPort)
411 if isinstance(cpp_port, cpp.MMIORegion):
412 return super().__new__(MMIORegion)
413 if isinstance(cpp_port, cpp.Metric):
414 return super().__new__(MetricPort)
415 return super().__new__(cls)
416
417 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
418 self.owner = owner
419 self.cpp_port = cpp_port
420
421 def write_port(self, channel_name: str) -> WritePort:
422 return WritePort(self, self.cpp_port.getWrite(channel_name))
423
424 def read_port(self, channel_name: str) -> ReadPort:
425 return ReadPort(self, self.cpp_port.getRead(channel_name))
426
427
428class MessageFuture(Future):
429 """A specialization of `Future` for ESI messages. Wraps the cpp object and
430 deserializes the result. Hopefully overrides all the methods necessary for
431 proper operation, which is assumed to be not all of them."""
432
433 def __init__(self, result_type: Type, cpp_future: cpp.MessageDataFuture):
434 self.result_type = result_type
435 self.cpp_future = cpp_future
436
437 def running(self) -> bool:
438 return True
439
440 def done(self) -> bool:
441 return self.cpp_future.valid()
442
443 def result(self, timeout: Optional[Union[int, float]] = None) -> Any:
444 # TODO: respect timeout
445 self.cpp_future.wait()
446 result_bytes = self.cpp_future.get()
447 (msg, leftover) = self.result_type.deserialize(result_bytes)
448 if len(leftover) != 0:
449 raise ValueError(f"leftover bytes: {leftover}")
450 return msg
451
452 def add_done_callback(self, fn: Callable[[Future], object]) -> None:
453 raise NotImplementedError("add_done_callback is not implemented")
454
455
457 """A region of memory-mapped I/O space. This is a collection of named
458 channels, which are either read or read-write. The channels are accessed
459 by name, and can be connected to the host."""
460
461 def __init__(self, owner: HWModule, cpp_port: cpp.MMIORegion):
462 super().__init__(owner, cpp_port)
463 self.region = cpp_port
464
465 @property
466 def descriptor(self) -> cpp.MMIORegionDesc:
467 return self.region.descriptor
468
469 def read(self, offset: int) -> bytearray:
470 """Read a value from the MMIO region at the given offset."""
471 return self.region.read(offset)
472
473 def write(self, offset: int, data: bytearray) -> None:
474 """Write a value to the MMIO region at the given offset."""
475 self.region.write(offset, data)
476
477
479 """A pair of channels which carry the input and output of a function."""
480
481 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
482 super().__init__(owner, cpp_port)
483 self.arg_type = self.write_port("arg").type
484 self.result_type = self.read_port("result").type
485 self.connected = False
486
487 def connect(self):
488 self.cpp_port.connect()
489 self.connected = True
490
491 def call(self, *args: Any, **kwargs: Any) -> Future:
492 """Call the function with the given argument and returns a future of the
493 result."""
494
495 # Accept either positional or keyword arguments, but not both.
496 if len(args) > 0 and len(kwargs) > 0:
497 raise ValueError("cannot use both positional and keyword arguments")
498
499 # Handle arguments: for single positional arg, unwrap it from tuple
500 if len(args) == 1:
501 selected = args[0]
502 elif len(args) > 1:
503 selected = args
504 else:
505 selected = kwargs
506
507 valid, reason = self.arg_type.is_valid(selected)
508 if not valid:
509 raise ValueError(
510 f"'{selected}' cannot be converted to '{self.arg_type}': {reason}")
511 arg_bytes: bytearray = self.arg_type.serialize(selected)
512 cpp_future = self.cpp_port.call(arg_bytes)
513 return MessageFuture(self.result_type, cpp_future)
514
515 def __call__(self, *args: Any, **kwds: Any) -> Future:
516 return self.call(*args, **kwds)
517
518
520 """Callback ports are the inverse of function ports -- instead of calls to the
521 accelerator, they get called from the accelerator. Specify the function which
522 you'd like the accelerator to call when you call `connect`."""
523
524 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
525 super().__init__(owner, cpp_port)
526 self.arg_type = self.read_port("arg").type
527 self.result_type = self.write_port("result").type
528 self.connected = False
529
530 def connect(self, cb: Callable[[Any], Any]):
531
532 def type_convert_wrapper(cb: Callable[[Any], Any],
533 msg: bytearray) -> Optional[bytearray]:
534 try:
535 (obj, leftover) = self.arg_type.deserialize(msg)
536 if len(leftover) != 0:
537 raise ValueError(f"leftover bytes: {leftover}")
538 result = cb(obj)
539 if result is None:
540 return None
541 return self.result_type.serialize(result)
542 except Exception as e:
543 traceback.print_exception(e)
544 return None
545
546 self.cpp_port.connect(lambda x: type_convert_wrapper(cb=cb, msg=x))
547 self.connected = True
548
549
551 """Telemetry ports report an individual piece of information from the
552 acceelerator. The method of accessing telemetry will likely change in the
553 future."""
554
555 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
556 super().__init__(owner, cpp_port)
557 self.connected = False
558
559 def connect(self):
560 self.cpp_port.connect()
561 self.connected = True
562
563 def read(self) -> Future:
564 cpp_future = self.cpp_port.read()
565 return MessageFuture(self.cpp_port.type, cpp_future)
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:302
_init_from_cpp(self, cpp.ArrayType cpp_type)
Definition types.py:292
Tuple[List[Any], bytearray] deserialize(self, bytearray data)
Definition types.py:319
__init__(self, str id, "ESIType" element_type, int size)
Definition types.py:289
bytearray serialize(self, list lst)
Definition types.py:313
bytearray serialize(self, Union[bytearray, bytes, List[int]] obj)
Definition types.py:145
int bit_width(self)
Definition types.py:142
Tuple[bytearray, bytearray] deserialize(self, bytearray data)
Definition types.py:152
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:131
__init__(self, str id, int width)
Definition types.py:128
__new__(cls, HWModule owner, cpp.BundlePort cpp_port)
Definition types.py:405
WritePort write_port(self, str channel_name)
Definition types.py:421
ReadPort read_port(self, str channel_name)
Definition types.py:424
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
Definition types.py:417
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
Definition types.py:524
connect(self, Callable[[Any], Any] cb)
Definition types.py:530
int bit_width(self)
Definition types.py:74
Tuple[bool, Optional[str]] supports_host(self)
Definition types.py:60
Tuple[object, bytearray] deserialize(self, bytearray data)
Definition types.py:90
_init_from_cpp(self, cpp.Type cpp_type)
Definition types.py:55
__init__(self, str id)
Definition types.py:45
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:68
int max_size(self)
Definition types.py:79
wrap_cpp(cls, cpp.Type cpp_type)
Definition types.py:49
str __str__(self)
Definition types.py:95
bytearray serialize(self, obj)
Definition types.py:86
Future call(self, *Any args, **Any kwargs)
Definition types.py:491
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
Definition types.py:481
Future __call__(self, *Any args, **Any kwds)
Definition types.py:515
int bit_width(self)
Definition types.py:165
__init__(self, str id, int width)
Definition types.py:161
None write(self, int offset, bytearray data)
Definition types.py:473
bytearray read(self, int offset)
Definition types.py:469
__init__(self, HWModule owner, cpp.MMIORegion cpp_port)
Definition types.py:461
cpp.MMIORegionDesc descriptor(self)
Definition types.py:466
Any result(self, Optional[Union[int, float]] timeout=None)
Definition types.py:443
__init__(self, Type result_type, cpp.MessageDataFuture cpp_future)
Definition types.py:433
None add_done_callback(self, Callable[[Future], object] fn)
Definition types.py:452
Future read(self)
Definition types.py:563
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
Definition types.py:555
__init__(self, BundlePort owner, cpp.ChannelPort cpp_port)
Definition types.py:335
connect(self, Optional[int] buffer_size=None)
Definition types.py:340
__init__(self, BundlePort owner, cpp.ReadChannelPort cpp_port)
Definition types.py:385
object read(self)
Definition types.py:389
Tuple[int, bytearray] deserialize(self, bytearray data)
Definition types.py:217
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:200
bytearray serialize(self, int obj)
Definition types.py:214
__init__(self, str id, int width)
Definition types.py:197
__init__(self, str id, List[Tuple[str, "ESIType"]] fields)
Definition types.py:227
bytearray serialize(self, obj)
Definition types.py:263
Tuple[Dict[str, Any], bytearray] deserialize(self, bytearray data)
Definition types.py:274
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:245
_init_from_cpp(self, cpp.StructType cpp_type)
Definition types.py:232
__init__(self, str id, int width)
Definition types.py:171
Tuple[int, bytearray] deserialize(self, bytearray data)
Definition types.py:187
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:174
bytearray serialize(self, int obj)
Definition types.py:184
int bit_width(self)
Definition types.py:110
Tuple[object, bytearray] deserialize(self, bytearray data)
Definition types.py:117
Tuple[bool, Optional[str]] is_valid(self, obj)
Definition types.py:104
__init__(self, str id)
Definition types.py:101
bytearray serialize(self, obj)
Definition types.py:113
bool try_write(self, msg=None)
Definition types.py:376
bytearray __serialize_msg(self, msg=None)
Definition types.py:361
bool write(self, msg=None)
Definition types.py:369
__init__(self, BundlePort owner, cpp.WriteChannelPort cpp_port)
Definition types.py:357
_get_esi_type(cpp.Type cpp_type)
Definition types.py:28