esiaccel 0.2.3.dev80__cp314-cp314-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.
Files changed (58) hide show
  1. esiaccel/CosimBackend.dll +0 -0
  2. esiaccel/CosimBackend.lib +0 -0
  3. esiaccel/CosimRpc.dll +0 -0
  4. esiaccel/CosimRpc.lib +0 -0
  5. esiaccel/ESICppRuntime.dll +0 -0
  6. esiaccel/ESICppRuntime.lib +0 -0
  7. esiaccel/EsiCosimDpiServer.dll +0 -0
  8. esiaccel/EsiCosimDpiServer.lib +0 -0
  9. esiaccel/MtiPli.dll +0 -0
  10. esiaccel/MtiPli.lib +0 -0
  11. esiaccel/__init__.py +31 -0
  12. esiaccel/abseil_dll.dll +0 -0
  13. esiaccel/accelerator.py +134 -0
  14. esiaccel/cares.dll +0 -0
  15. esiaccel/cmake/esiaccelConfig.cmake +49 -0
  16. esiaccel/codegen.py +197 -0
  17. esiaccel/cosim/Cosim_CycleCount.sv +84 -0
  18. esiaccel/cosim/Cosim_DpiPkg.sv +85 -0
  19. esiaccel/cosim/Cosim_Endpoint.sv +218 -0
  20. esiaccel/cosim/Cosim_Manifest.sv +32 -0
  21. esiaccel/cosim/driver.cpp +131 -0
  22. esiaccel/cosim/driver.sv +74 -0
  23. esiaccel/cosim/questa.py +141 -0
  24. esiaccel/cosim/simulator.py +383 -0
  25. esiaccel/cosim/verilator.py +92 -0
  26. esiaccel/esi-cosim.py +104 -0
  27. esiaccel/esiCppAccel.cp312-win_amd64.pyd +0 -0
  28. esiaccel/esiquery.exe +0 -0
  29. esiaccel/include/esi/Accelerator.h +229 -0
  30. esiaccel/include/esi/CLI.h +77 -0
  31. esiaccel/include/esi/Common.h +182 -0
  32. esiaccel/include/esi/Context.h +82 -0
  33. esiaccel/include/esi/Design.h +132 -0
  34. esiaccel/include/esi/Engines.h +124 -0
  35. esiaccel/include/esi/Logging.h +231 -0
  36. esiaccel/include/esi/Manifest.h +70 -0
  37. esiaccel/include/esi/Ports.h +482 -0
  38. esiaccel/include/esi/Services.h +467 -0
  39. esiaccel/include/esi/Types.h +334 -0
  40. esiaccel/include/esi/Utils.h +102 -0
  41. esiaccel/include/esi/Values.h +313 -0
  42. esiaccel/include/esi/backends/Cosim.h +78 -0
  43. esiaccel/include/esi/backends/RpcClient.h +97 -0
  44. esiaccel/include/esi/backends/RpcServer.h +73 -0
  45. esiaccel/include/esi/backends/Trace.h +87 -0
  46. esiaccel/libcrypto-3-x64.dll +0 -0
  47. esiaccel/libprotobuf.dll +0 -0
  48. esiaccel/libssl-3-x64.dll +0 -0
  49. esiaccel/re2.dll +0 -0
  50. esiaccel/types.py +565 -0
  51. esiaccel/utils.py +54 -0
  52. esiaccel/zlib1.dll +0 -0
  53. esiaccel-0.2.3.dev80.dist-info/METADATA +254 -0
  54. esiaccel-0.2.3.dev80.dist-info/RECORD +58 -0
  55. esiaccel-0.2.3.dev80.dist-info/WHEEL +5 -0
  56. esiaccel-0.2.3.dev80.dist-info/entry_points.txt +4 -0
  57. esiaccel-0.2.3.dev80.dist-info/licenses/LICENSE +234 -0
  58. esiaccel-0.2.3.dev80.dist-info/top_level.txt +1 -0
esiaccel/types.py ADDED
@@ -0,0 +1,565 @@
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
+
13
+ from __future__ import annotations
14
+
15
+ from . import esiCppAccel as cpp
16
+
17
+ from typing import TYPE_CHECKING
18
+
19
+ if TYPE_CHECKING:
20
+ from .accelerator import HWModule
21
+
22
+ from concurrent.futures import Future
23
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
24
+ import sys
25
+ import traceback
26
+
27
+
28
+ def _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
+
43
+ class 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
+
99
+ class VoidType(ESIType):
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
+
126
+ class BitsType(ESIType):
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
+
159
+ class IntType(ESIType):
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
+
169
+ class UIntType(IntType):
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_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_size, "little"))
186
+
187
+ def deserialize(self, data: bytearray) -> Tuple[int, bytearray]:
188
+ return (int.from_bytes(data[0:self.max_size],
189
+ "little"), data[self.max_size:])
190
+
191
+
192
+ __esi_mapping[cpp.UIntType] = UIntType
193
+
194
+
195
+ class SIntType(IntType):
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_width - 1):
205
+ return (False, f"out of range: {obj}")
206
+ elif obj < 0:
207
+ if obj >= 2**(self.bit_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_size, "little", signed=True))
216
+
217
+ def deserialize(self, data: bytearray) -> Tuple[int, bytearray]:
218
+ return (int.from_bytes(data[0:self.max_size], "little",
219
+ signed=True), data[self.max_size:])
220
+
221
+
222
+ __esi_mapping[cpp.SIntType] = SIntType
223
+
224
+
225
+ class StructType(ESIType):
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(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
+
287
+ class ArrayType(ESIType):
288
+
289
+ def __init__(self, id: str, element_type: "ESIType", size: int):
290
+ self._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
+
331
+ class 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
+
354
+ class WritePort(Port):
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
+
382
+ class ReadPort(Port):
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
+
400
+ class BundlePort:
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
+
428
+ class 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
+
456
+ class MMIORegion(BundlePort):
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
+
478
+ class FunctionPort(BundlePort):
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
+
519
+ class CallbackPort(BundlePort):
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
+
550
+ class MetricPort(BundlePort):
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)
esiaccel/utils.py ADDED
@@ -0,0 +1,54 @@
1
+ # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2
+ # See https://llvm.org/LICENSE.txt for license information.
3
+ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
+
5
+ from . import codegen
6
+
7
+ import platform
8
+ from pathlib import Path
9
+ import subprocess
10
+ import sys
11
+
12
+ _thisdir = Path(__file__).absolute().resolve().parent
13
+
14
+
15
+ def run_esiquery():
16
+ """Run the esiquery executable with the same arguments as this script."""
17
+ if platform.system() == "Windows":
18
+ esiquery = _thisdir / "esiquery.exe"
19
+ else:
20
+ esiquery = _thisdir / "bin" / "esiquery"
21
+ return subprocess.call([esiquery] + sys.argv[1:])
22
+
23
+
24
+ def run_esi_cosim():
25
+ """Run the esi-cosim.py script with the same arguments as this script."""
26
+ import importlib.util
27
+ if platform.system() == "Windows":
28
+ esi_cosim = _thisdir / "esi-cosim.py"
29
+ else:
30
+ esi_cosim = _thisdir / "bin" / "esi-cosim.py"
31
+ spec = importlib.util.spec_from_file_location("esi_cosim", esi_cosim)
32
+ assert spec is not None
33
+ assert spec.loader is not None
34
+ cosim_import = importlib.util.module_from_spec(spec)
35
+ spec.loader.exec_module(cosim_import)
36
+ return cosim_import.__main__(sys.argv)
37
+
38
+
39
+ def run_cppgen():
40
+ return codegen.run()
41
+
42
+
43
+ def get_cmake_dir() -> Path:
44
+ return _thisdir / "cmake"
45
+
46
+
47
+ def get_dll_dir() -> Path:
48
+ """Return the directory where the ESI dll's are located"""
49
+ import sys
50
+ import os
51
+ if sys.platform == "win32":
52
+ return _thisdir
53
+ else:
54
+ return _thisdir / "lib"
esiaccel/zlib1.dll ADDED
Binary file