13from __future__
import annotations
15from .
import esiCppAccel
as cpp
17from typing
import TYPE_CHECKING
19 from .accelerator
import HWModule
21from concurrent.futures
import Future
22from typing
import Any, Callable, Dict, List, Optional, Tuple, Type, Union
28 """Get the wrapper class for a C++ type."""
29 for cpp_type_cls, fn
in __esi_mapping.items():
30 if isinstance(cpp_type, cpp_type_cls):
37__esi_mapping: Dict[Type, Callable] = {
38 cpp.ChannelType:
lambda cpp_type:
_get_esi_type(cpp_type.inner)
49 """Does this type support host communication via Python? Returns either
50 '(True, None)' if it is, or '(False, reason)' if it is not."""
53 return (
False,
"runtime only supports types with multiple of 8 bits")
56 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
57 """Is a Python object compatible with HW type? Returns either '(True,
58 None)' if it is, or '(False, reason)' if it is not."""
59 assert False,
"unimplemented"
56 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
63 """Size of this type, in bits. Negative for unbounded types."""
64 assert False,
"unimplemented"
68 """Maximum size of a value of this type, in bytes."""
75 """Convert a Python object to a bytearray."""
76 assert False,
"unimplemented"
78 def deserialize(self, data: bytearray) -> Tuple[object, bytearray]:
79 """Convert a bytearray to a Python object. Return the object and the
81 assert False,
"unimplemented"
78 def deserialize(self, data: bytearray) -> Tuple[object, bytearray]:
…
89 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
91 return (
False, f
"void type cannot must represented by None, not {obj}")
89 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
100 return bytearray([0])
102 def deserialize(self, data: bytearray) -> Tuple[object, bytearray]:
104 raise ValueError(f
"void type cannot be represented by {data}")
105 return (
None, data[1:])
108__esi_mapping[cpp.VoidType] = VoidType
114 self.
cpp_type: cpp.BitsType = cpp_type
116 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
117 if not isinstance(obj, (bytearray, bytes, list)):
118 return (
False, f
"invalid type: {type(obj)}")
119 if isinstance(obj, list)
and not all(
120 [isinstance(b, int)
and b.bit_length() <= 8
for b
in obj]):
121 return (
False, f
"list item too large: {obj}")
123 return (
False, f
"wrong size: {len(obj)}")
116 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
130 def serialize(self, obj: Union[bytearray, bytes, List[int]]) -> bytearray:
131 if isinstance(obj, bytearray):
133 if isinstance(obj, bytes)
or isinstance(obj, list):
134 return bytearray(obj)
135 raise ValueError(f
"cannot convert {obj} to bytearray")
130 def serialize(self, obj: Union[bytearray, bytes, List[int]]) -> bytearray:
…
137 def deserialize(self, data: bytearray) -> Tuple[bytearray, bytearray]:
137 def deserialize(self, data: bytearray) -> Tuple[bytearray, bytearray]:
…
141__esi_mapping[cpp.BitsType] = BitsType
147 self.
cpp_type: cpp.IntegerType = cpp_type
156 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
157 if not isinstance(obj, int):
158 return (
False, f
"must be an int, not {type(obj)}")
160 return (
False, f
"out of range: {obj}")
156 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
164 return f
"uint{self.bit_width}"
174__esi_mapping[cpp.UIntType] = UIntType
179 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
180 if not isinstance(obj, int):
181 return (
False, f
"must be an int, not {type(obj)}")
184 return (
False, f
"out of range: {obj}")
187 return (
False, f
"out of range: {obj}")
179 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
191 return f
"sint{self.bit_width}"
201__esi_mapping[cpp.SIntType] = SIntType
208 self.
fields: List[Tuple[str, ESIType]] = [
214 widths = [ty.bit_width
for (_, ty)
in self.
fields]
215 if any([w < 0
for w
in widths]):
219 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
221 if not isinstance(obj, dict):
224 for (fname, ftype)
in self.
fields:
226 return (
False, f
"missing field '{fname}'")
227 fvalid, reason = ftype.is_valid(obj[fname])
229 return (
False, f
"invalid field '{fname}': {reason}")
231 if fields_count != len(obj):
232 return (
False,
"missing fields")
219 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
237 for (fname, ftype)
in reversed(self.
fields):
239 ret.extend(ftype.serialize(fval))
242 def deserialize(self, data: bytearray) -> Tuple[Dict[str, Any], bytearray]:
244 for (fname, ftype)
in reversed(self.
fields):
245 (fval, data) = ftype.deserialize(data)
242 def deserialize(self, data: bytearray) -> Tuple[Dict[str, Any], bytearray]:
…
250__esi_mapping[cpp.StructType] = StructType
264 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
265 if not isinstance(obj, list):
266 return (
False, f
"must be a list, not {type(obj)}")
267 if len(obj) != self.
size:
268 return (
False, f
"wrong size: expected {self.size} not {len(obj)}")
269 for (idx, e)
in enumerate(obj):
272 return (
False, f
"invalid element {idx}: {reason}")
264 def is_valid(self, obj) -> Tuple[bool, Optional[str]]:
…
277 for e
in reversed(lst):
281 def deserialize(self, data: bytearray) -> Tuple[List[Any], bytearray]:
283 for _
in range(self.
size):
281 def deserialize(self, data: bytearray) -> Tuple[List[Any], bytearray]:
…
290__esi_mapping[cpp.ArrayType] = ArrayType
294 """A unidirectional communication channel. This is the basic communication
295 method with an accelerator."""
297 def __init__(self, owner: BundlePort, cpp_port: cpp.ChannelPort):
297 def __init__(self, owner: BundlePort, cpp_port: cpp.ChannelPort):
…
302 def connect(self, buffer_size: Optional[int] =
None):
303 (supports_host, reason) = self.
type.supports_host
304 if not supports_host:
305 raise TypeError(f
"unsupported type: {reason}")
302 def connect(self, buffer_size: Optional[int] =
None):
…
315 """A unidirectional communication channel from the host to the accelerator."""
317 def __init__(self, owner: BundlePort, cpp_port: cpp.WriteChannelPort):
319 self.
cpp_port: cpp.WriteChannelPort = cpp_port
317 def __init__(self, owner: BundlePort, cpp_port: cpp.WriteChannelPort):
…
322 valid, reason = self.
type.is_valid(msg)
325 f
"'{msg}' cannot be converted to '{self.type}': {reason}")
326 msg_bytes: bytearray = self.
type.serialize(msg)
330 """Write a typed message to the channel. Attempts to serialize 'msg' to what
331 the accelerator expects, but will fail if the object is not convertible to
337 """Like 'write', but uses the non-blocking tryWrite method of the underlying
338 port. Returns True if the write was successful, False otherwise."""
343 """A unidirectional communication channel from the accelerator to the host."""
345 def __init__(self, owner: BundlePort, cpp_port: cpp.ReadChannelPort):
347 self.
cpp_port: cpp.ReadChannelPort = cpp_port
345 def __init__(self, owner: BundlePort, cpp_port: cpp.ReadChannelPort):
…
350 """Read a typed message from the channel. Returns a deserialized object of a
351 type defined by the port type."""
354 (msg, leftover) = self.
type.deserialize(buffer)
355 if len(leftover) != 0:
356 raise ValueError(f
"leftover bytes: {leftover}")
361 """A collections of named, unidirectional communication channels."""
365 def __new__(cls, owner: HWModule, cpp_port: cpp.BundlePort):
367 if isinstance(cpp_port, cpp.Function):
368 return super().
__new__(FunctionPort)
369 if isinstance(cpp_port, cpp.Callback):
370 return super().
__new__(CallbackPort)
371 if isinstance(cpp_port, cpp.MMIORegion):
372 return super().
__new__(MMIORegion)
373 if isinstance(cpp_port, cpp.Telemetry):
374 return super().
__new__(TelemetryPort)
365 def __new__(cls, owner: HWModule, cpp_port: cpp.BundlePort):
…
377 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
377 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
…
389 """A specialization of `Future` for ESI messages. Wraps the cpp object and
390 deserializes the result. Hopefully overrides all the methods necessary for
391 proper operation, which is assumed to be not all of them."""
393 def __init__(self, result_type: Type, cpp_future: cpp.MessageDataFuture):
393 def __init__(self, result_type: Type, cpp_future: cpp.MessageDataFuture):
…
403 def result(self, timeout: Optional[Union[int, float]] =
None) -> Any:
407 (msg, leftover) = self.
result_type.deserialize(result_bytes)
408 if len(leftover) != 0:
409 raise ValueError(f
"leftover bytes: {leftover}")
403 def result(self, timeout: Optional[Union[int, float]] =
None) -> Any:
…
413 raise NotImplementedError(
"add_done_callback is not implemented")
417 """A region of memory-mapped I/O space. This is a collection of named
418 channels, which are either read or read-write. The channels are accessed
419 by name, and can be connected to the host."""
421 def __init__(self, owner: HWModule, cpp_port: cpp.MMIORegion):
421 def __init__(self, owner: HWModule, cpp_port: cpp.MMIORegion):
…
427 return self.
region.descriptor
429 def read(self, offset: int) -> bytearray:
430 """Read a value from the MMIO region at the given offset."""
429 def read(self, offset: int) -> bytearray:
…
433 def write(self, offset: int, data: bytearray) ->
None:
434 """Write a value to the MMIO region at the given offset."""
433 def write(self, offset: int, data: bytearray) ->
None:
…
439 """A pair of channels which carry the input and output of a function."""
441 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
441 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
…
451 def call(self, **kwargs: Any) -> Future:
452 """Call the function with the given argument and returns a future of the
454 valid, reason = self.
arg_type.is_valid(kwargs)
457 f
"'{kwargs}' cannot be converted to '{self.arg_type}': {reason}")
458 arg_bytes: bytearray = self.
arg_type.serialize(kwargs)
451 def call(self, **kwargs: Any) -> Future:
…
462 def __call__(self, *args: Any, **kwds: Any) -> Future:
463 return self.
call(*args, **kwds)
462 def __call__(self, *args: Any, **kwds: Any) -> Future:
…
467 """Callback ports are the inverse of function ports -- instead of calls to the
468 accelerator, they get called from the accelerator. Specify the function which
469 you'd like the accelerator to call when you call `connect`."""
471 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
471 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
…
479 def type_convert_wrapper(cb: Callable[[Any], Any],
480 msg: bytearray) -> Optional[bytearray]:
482 (obj, leftover) = self.
arg_type.deserialize(msg)
483 if len(leftover) != 0:
484 raise ValueError(f
"leftover bytes: {leftover}")
489 except Exception
as e:
490 traceback.print_exception(e)
498 """Telemetry ports report an individual piece of information from the
499 acceelerator. The method of accessing telemetry will likely change in the
502 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
502 def __init__(self, owner: HWModule, cpp_port: cpp.BundlePort):
…
Tuple[bool, Optional[str]] is_valid(self, obj)
__init__(self, cpp.ArrayType cpp_type)
Tuple[List[Any], bytearray] deserialize(self, bytearray data)
bytearray serialize(self, list lst)
bytearray serialize(self, Union[bytearray, bytes, List[int]] obj)
Tuple[bytearray, bytearray] deserialize(self, bytearray data)
__init__(self, cpp.BitsType cpp_type)
Tuple[bool, Optional[str]] is_valid(self, obj)
__new__(cls, HWModule owner, cpp.BundlePort cpp_port)
WritePort write_port(self, str channel_name)
ReadPort read_port(self, str channel_name)
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
connect(self, Callable[[Any], Any] cb)
Tuple[bool, Optional[str]] supports_host(self)
Tuple[object, bytearray] deserialize(self, bytearray data)
Tuple[bool, Optional[str]] is_valid(self, obj)
__init__(self, cpp.Type cpp_type)
bytearray serialize(self, obj)
Future call(self, **Any kwargs)
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
Future __call__(self, *Any args, **Any kwds)
__init__(self, cpp.IntegerType cpp_type)
None write(self, int offset, bytearray data)
bytearray read(self, int offset)
__init__(self, HWModule owner, cpp.MMIORegion cpp_port)
cpp.MMIORegionDesc descriptor(self)
Any result(self, Optional[Union[int, float]] timeout=None)
__init__(self, Type result_type, cpp.MessageDataFuture cpp_future)
None add_done_callback(self, Callable[[Future], object] fn)
__init__(self, BundlePort owner, cpp.ChannelPort cpp_port)
connect(self, Optional[int] buffer_size=None)
__init__(self, BundlePort owner, cpp.ReadChannelPort cpp_port)
Tuple[int, bytearray] deserialize(self, bytearray data)
Tuple[bool, Optional[str]] is_valid(self, obj)
bytearray serialize(self, int obj)
__init__(self, cpp.StructType cpp_type)
bytearray serialize(self, obj)
Tuple[Dict[str, Any], bytearray] deserialize(self, bytearray data)
Tuple[bool, Optional[str]] is_valid(self, obj)
__init__(self, HWModule owner, cpp.BundlePort cpp_port)
Tuple[int, bytearray] deserialize(self, bytearray data)
Tuple[bool, Optional[str]] is_valid(self, obj)
bytearray serialize(self, int obj)
Tuple[object, bytearray] deserialize(self, bytearray data)
Tuple[bool, Optional[str]] is_valid(self, obj)
bytearray serialize(self, obj)
bool try_write(self, msg=None)
bytearray __serialize_msg(self, msg=None)
bool write(self, msg=None)
__init__(self, BundlePort owner, cpp.WriteChannelPort cpp_port)
_get_esi_type(cpp.Type cpp_type)