esiaccel 0.1.5.dev66__cp39-cp39-win_amd64.whl → 0.1.5.dev450__cp39-cp39-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esiaccel/CosimBackend.dll +0 -0
- esiaccel/CosimBackend.lib +0 -0
- esiaccel/ESICppRuntime.dll +0 -0
- esiaccel/ESICppRuntime.lib +0 -0
- esiaccel/EsiCosimDpiServer.dll +0 -0
- esiaccel/EsiCosimDpiServer.lib +0 -0
- esiaccel/MtiPli.dll +0 -0
- esiaccel/MtiPli.lib +0 -0
- esiaccel/__init__.py +22 -4
- esiaccel/abseil_dll.dll +0 -0
- esiaccel/accelerator.py +37 -5
- esiaccel/cares.dll +0 -0
- esiaccel/codegen.py +3 -3
- esiaccel/cosim/Cosim_Endpoint.sv +61 -32
- esiaccel/cosim/driver.cpp +6 -6
- esiaccel/cosim/driver.sv +14 -0
- esiaccel/cosim/questa.py +141 -0
- esiaccel/cosim/simulator.py +382 -0
- esiaccel/cosim/verilator.py +92 -0
- esiaccel/esi-cosim.py +3 -322
- esiaccel/esiCppAccel.cp39-win_amd64.pyd +0 -0
- esiaccel/esiquery.exe +0 -0
- esiaccel/include/esi/Accelerator.h +1 -14
- esiaccel/include/esi/CLI.h +5 -5
- esiaccel/include/esi/Common.h +17 -1
- esiaccel/include/esi/Context.h +17 -9
- esiaccel/include/esi/Manifest.h +0 -2
- esiaccel/include/esi/Services.h +60 -22
- esiaccel/include/esi/Types.h +107 -10
- esiaccel/include/esi/Values.h +313 -0
- esiaccel/libcrypto-3-x64.dll +0 -0
- esiaccel/libprotobuf.dll +0 -0
- esiaccel/libssl-3-x64.dll +0 -0
- esiaccel/re2.dll +0 -0
- esiaccel/types.py +13 -5
- esiaccel/utils.py +11 -1
- esiaccel/zlib1.dll +0 -0
- {esiaccel-0.1.5.dev66.dist-info → esiaccel-0.1.5.dev450.dist-info}/METADATA +1 -1
- esiaccel-0.1.5.dev450.dist-info/RECORD +54 -0
- esiaccel-0.1.5.dev66.dist-info/RECORD +0 -43
- {esiaccel-0.1.5.dev66.dist-info → esiaccel-0.1.5.dev450.dist-info}/WHEEL +0 -0
- {esiaccel-0.1.5.dev66.dist-info → esiaccel-0.1.5.dev450.dist-info}/entry_points.txt +0 -0
- {esiaccel-0.1.5.dev66.dist-info → esiaccel-0.1.5.dev450.dist-info}/licenses/LICENSE +0 -0
- {esiaccel-0.1.5.dev66.dist-info → esiaccel-0.1.5.dev450.dist-info}/top_level.txt +0 -0
esiaccel/esi-cosim.py
CHANGED
|
@@ -14,333 +14,14 @@
|
|
|
14
14
|
# ===----------------------------------------------------------------------===//
|
|
15
15
|
|
|
16
16
|
import argparse
|
|
17
|
-
import os
|
|
18
17
|
from pathlib import Path
|
|
19
|
-
import re
|
|
20
|
-
import signal
|
|
21
|
-
import socket
|
|
22
|
-
import subprocess
|
|
23
18
|
import sys
|
|
24
19
|
import textwrap
|
|
25
|
-
import time
|
|
26
20
|
from typing import Dict, List
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def is_port_open(port) -> bool:
|
|
33
|
-
"""Check if a TCP port is open locally."""
|
|
34
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
35
|
-
result = sock.connect_ex(('127.0.0.1', port))
|
|
36
|
-
sock.close()
|
|
37
|
-
return True if result == 0 else False
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class SourceFiles:
|
|
41
|
-
|
|
42
|
-
def __init__(self, top: str) -> None:
|
|
43
|
-
# User source files.
|
|
44
|
-
self.user: List[Path] = []
|
|
45
|
-
# DPI shared objects.
|
|
46
|
-
self.dpi_so: List[str] = ["EsiCosimDpiServer"]
|
|
47
|
-
# DPI SV files.
|
|
48
|
-
self.dpi_sv: List[Path] = [
|
|
49
|
-
CosimCollateralDir / "Cosim_DpiPkg.sv",
|
|
50
|
-
CosimCollateralDir / "Cosim_Endpoint.sv",
|
|
51
|
-
CosimCollateralDir / "Cosim_Manifest.sv",
|
|
52
|
-
]
|
|
53
|
-
# Name of the top module.
|
|
54
|
-
self.top = top
|
|
55
|
-
|
|
56
|
-
def add_dir(self, dir: Path):
|
|
57
|
-
"""Add all the RTL files in a directory to the source list."""
|
|
58
|
-
for file in sorted(dir.iterdir()):
|
|
59
|
-
if file.is_file() and (file.suffix == ".sv" or file.suffix == ".v"):
|
|
60
|
-
self.user.append(file)
|
|
61
|
-
elif file.is_dir():
|
|
62
|
-
self.add_dir(file)
|
|
63
|
-
|
|
64
|
-
def dpi_so_paths(self) -> List[Path]:
|
|
65
|
-
"""Return a list of all the DPI shared object files."""
|
|
66
|
-
|
|
67
|
-
def find_so(name: str) -> Path:
|
|
68
|
-
for path in Simulator.get_env().get("LD_LIBRARY_PATH", "").split(":"):
|
|
69
|
-
if os.name == "nt":
|
|
70
|
-
so = Path(path) / f"{name}.dll"
|
|
71
|
-
else:
|
|
72
|
-
so = Path(path) / f"lib{name}.so"
|
|
73
|
-
if so.exists():
|
|
74
|
-
return so
|
|
75
|
-
raise FileNotFoundError(f"Could not find {name}.so in LD_LIBRARY_PATH")
|
|
76
|
-
|
|
77
|
-
return [find_so(name) for name in self.dpi_so]
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def rtl_sources(self) -> List[Path]:
|
|
81
|
-
"""Return a list of all the RTL source files."""
|
|
82
|
-
return self.dpi_sv + self.user
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class Simulator:
|
|
86
|
-
|
|
87
|
-
# Some RTL simulators don't use stderr for error messages. Everything goes to
|
|
88
|
-
# stdout. Boo! They should feel bad about this. Also, they can specify that
|
|
89
|
-
# broken behavior by overriding this.
|
|
90
|
-
UsesStderr = True
|
|
91
|
-
|
|
92
|
-
def __init__(self, sources: SourceFiles, run_dir: Path, debug: bool):
|
|
93
|
-
self.sources = sources
|
|
94
|
-
self.run_dir = run_dir
|
|
95
|
-
self.debug = debug
|
|
96
|
-
|
|
97
|
-
@staticmethod
|
|
98
|
-
def get_env() -> Dict[str, str]:
|
|
99
|
-
"""Get the environment variables to locate shared objects."""
|
|
100
|
-
|
|
101
|
-
env = os.environ.copy()
|
|
102
|
-
env["LIBRARY_PATH"] = env.get("LIBRARY_PATH", "") + ":" + str(
|
|
103
|
-
_thisdir.parent / "lib")
|
|
104
|
-
env["LD_LIBRARY_PATH"] = env.get("LD_LIBRARY_PATH", "") + ":" + str(
|
|
105
|
-
_thisdir.parent / "lib")
|
|
106
|
-
return env
|
|
107
|
-
|
|
108
|
-
def compile_commands(self) -> List[List[str]]:
|
|
109
|
-
"""Compile the sources. Returns the exit code of the simulation compiler."""
|
|
110
|
-
assert False, "Must be implemented by subclass"
|
|
111
|
-
|
|
112
|
-
def compile(self) -> int:
|
|
113
|
-
cmds = self.compile_commands()
|
|
114
|
-
self.run_dir.mkdir(parents=True, exist_ok=True)
|
|
115
|
-
with (self.run_dir / "compile_stdout.log").open("w") as stdout, (
|
|
116
|
-
self.run_dir / "compile_stderr.log").open("w") as stderr:
|
|
117
|
-
for cmd in cmds:
|
|
118
|
-
stderr.write(" ".join(cmd) + "\n")
|
|
119
|
-
cp = subprocess.run(cmd,
|
|
120
|
-
env=Simulator.get_env(),
|
|
121
|
-
capture_output=True,
|
|
122
|
-
text=True)
|
|
123
|
-
stdout.write(cp.stdout)
|
|
124
|
-
stderr.write(cp.stderr)
|
|
125
|
-
if cp.returncode != 0:
|
|
126
|
-
print("====== Compilation failure:")
|
|
127
|
-
if self.UsesStderr:
|
|
128
|
-
print(cp.stderr)
|
|
129
|
-
else:
|
|
130
|
-
print(cp.stdout)
|
|
131
|
-
return cp.returncode
|
|
132
|
-
return 0
|
|
133
|
-
|
|
134
|
-
def run_command(self, gui: bool) -> List[str]:
|
|
135
|
-
"""Return the command to run the simulation."""
|
|
136
|
-
assert False, "Must be implemented by subclass"
|
|
137
|
-
|
|
138
|
-
def run(self,
|
|
139
|
-
inner_command: str,
|
|
140
|
-
gui: bool = False,
|
|
141
|
-
server_only: bool = False) -> int:
|
|
142
|
-
"""Start the simulation then run the command specified. Kill the simulation
|
|
143
|
-
when the command exits."""
|
|
144
|
-
|
|
145
|
-
# 'simProc' is accessed in the finally block. Declare it here to avoid
|
|
146
|
-
# syntax errors in that block.
|
|
147
|
-
simProc = None
|
|
148
|
-
try:
|
|
149
|
-
# Open log files
|
|
150
|
-
self.run_dir.mkdir(parents=True, exist_ok=True)
|
|
151
|
-
simStdout = open(self.run_dir / "sim_stdout.log", "w")
|
|
152
|
-
simStderr = open(self.run_dir / "sim_stderr.log", "w")
|
|
153
|
-
|
|
154
|
-
# Erase the config file if it exists. We don't want to read
|
|
155
|
-
# an old config.
|
|
156
|
-
portFileName = self.run_dir / "cosim.cfg"
|
|
157
|
-
if os.path.exists(portFileName):
|
|
158
|
-
os.remove(portFileName)
|
|
159
|
-
|
|
160
|
-
# Run the simulation.
|
|
161
|
-
simEnv = Simulator.get_env()
|
|
162
|
-
if self.debug:
|
|
163
|
-
simEnv["COSIM_DEBUG_FILE"] = "cosim_debug.log"
|
|
164
|
-
if "DEBUG_PERIOD" not in simEnv:
|
|
165
|
-
# Slow the simulation down to one tick per millisecond.
|
|
166
|
-
simEnv["DEBUG_PERIOD"] = "1"
|
|
167
|
-
simProc = subprocess.Popen(self.run_command(gui),
|
|
168
|
-
stdout=simStdout,
|
|
169
|
-
stderr=simStderr,
|
|
170
|
-
env=simEnv,
|
|
171
|
-
cwd=self.run_dir,
|
|
172
|
-
preexec_fn=os.setsid)
|
|
173
|
-
simStderr.close()
|
|
174
|
-
simStdout.close()
|
|
175
|
-
|
|
176
|
-
# Get the port which the simulation RPC selected.
|
|
177
|
-
checkCount = 0
|
|
178
|
-
while (not os.path.exists(portFileName)) and \
|
|
179
|
-
simProc.poll() is None:
|
|
180
|
-
time.sleep(0.1)
|
|
181
|
-
checkCount += 1
|
|
182
|
-
if checkCount > 200 and not gui:
|
|
183
|
-
raise Exception(f"Cosim never wrote cfg file: {portFileName}")
|
|
184
|
-
port = -1
|
|
185
|
-
while port < 0:
|
|
186
|
-
portFile = open(portFileName, "r")
|
|
187
|
-
for line in portFile.readlines():
|
|
188
|
-
m = re.match("port: (\\d+)", line)
|
|
189
|
-
if m is not None:
|
|
190
|
-
port = int(m.group(1))
|
|
191
|
-
portFile.close()
|
|
192
|
-
|
|
193
|
-
# Wait for the simulation to start accepting RPC connections.
|
|
194
|
-
checkCount = 0
|
|
195
|
-
while not is_port_open(port):
|
|
196
|
-
checkCount += 1
|
|
197
|
-
if checkCount > 200:
|
|
198
|
-
raise Exception(f"Cosim RPC port ({port}) never opened")
|
|
199
|
-
if simProc.poll() is not None:
|
|
200
|
-
raise Exception("Simulation exited early")
|
|
201
|
-
time.sleep(0.05)
|
|
202
|
-
|
|
203
|
-
if server_only:
|
|
204
|
-
# wait for user input to kill the server
|
|
205
|
-
input(
|
|
206
|
-
f"Running in server-only mode on port {port} - Press anything to kill the server..."
|
|
207
|
-
)
|
|
208
|
-
return 0
|
|
209
|
-
else:
|
|
210
|
-
# Run the inner command, passing the connection info via environment vars.
|
|
211
|
-
testEnv = os.environ.copy()
|
|
212
|
-
testEnv["ESI_COSIM_PORT"] = str(port)
|
|
213
|
-
testEnv["ESI_COSIM_HOST"] = "localhost"
|
|
214
|
-
return subprocess.run(inner_command, cwd=os.getcwd(),
|
|
215
|
-
env=testEnv).returncode
|
|
216
|
-
finally:
|
|
217
|
-
# Make sure to stop the simulation no matter what.
|
|
218
|
-
if simProc:
|
|
219
|
-
os.killpg(os.getpgid(simProc.pid), signal.SIGINT)
|
|
220
|
-
# Allow the simulation time to flush its outputs.
|
|
221
|
-
try:
|
|
222
|
-
simProc.wait(timeout=1.0)
|
|
223
|
-
except subprocess.TimeoutExpired:
|
|
224
|
-
# If the simulation doesn't exit of its own free will, kill it.
|
|
225
|
-
simProc.kill()
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
class Verilator(Simulator):
|
|
229
|
-
"""Run and compile funcs for Verilator."""
|
|
230
|
-
|
|
231
|
-
DefaultDriver = CosimCollateralDir / "driver.cpp"
|
|
232
|
-
|
|
233
|
-
def __init__(self, sources: SourceFiles, run_dir: Path, debug: bool):
|
|
234
|
-
super().__init__(sources, run_dir, debug)
|
|
235
|
-
|
|
236
|
-
self.verilator = "verilator"
|
|
237
|
-
if "VERILATOR_PATH" in os.environ:
|
|
238
|
-
self.verilator = os.environ["VERILATOR_PATH"]
|
|
239
|
-
|
|
240
|
-
def compile_commands(self) -> List[List[str]]:
|
|
241
|
-
cmd: List[str] = [
|
|
242
|
-
self.verilator,
|
|
243
|
-
"--cc",
|
|
244
|
-
"--top-module",
|
|
245
|
-
self.sources.top,
|
|
246
|
-
"-DSIMULATION",
|
|
247
|
-
"-Wno-TIMESCALEMOD",
|
|
248
|
-
"-Wno-fatal",
|
|
249
|
-
"-sv",
|
|
250
|
-
"--build",
|
|
251
|
-
"--exe",
|
|
252
|
-
"--assert",
|
|
253
|
-
str(Verilator.DefaultDriver),
|
|
254
|
-
]
|
|
255
|
-
cflags = [
|
|
256
|
-
"-DTOP_MODULE=" + self.sources.top,
|
|
257
|
-
]
|
|
258
|
-
if self.debug:
|
|
259
|
-
cmd += [
|
|
260
|
-
"--trace", "--trace-params", "--trace-structs", "--trace-underscore"
|
|
261
|
-
]
|
|
262
|
-
cflags.append("-DTRACE")
|
|
263
|
-
if len(cflags) > 0:
|
|
264
|
-
cmd += ["-CFLAGS", " ".join(cflags)]
|
|
265
|
-
if len(self.sources.dpi_so) > 0:
|
|
266
|
-
cmd += ["-LDFLAGS", " ".join(["-l" + so for so in self.sources.dpi_so])]
|
|
267
|
-
cmd += [str(p) for p in self.sources.rtl_sources]
|
|
268
|
-
return [cmd]
|
|
269
|
-
|
|
270
|
-
def run_command(self, gui: bool):
|
|
271
|
-
if gui:
|
|
272
|
-
raise RuntimeError("Verilator does not support GUI mode.")
|
|
273
|
-
exe = Path.cwd() / "obj_dir" / ("V" + self.sources.top)
|
|
274
|
-
return [str(exe)]
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
class Questa(Simulator):
|
|
278
|
-
"""Run and compile funcs for Questasim."""
|
|
279
|
-
|
|
280
|
-
DefaultDriver = CosimCollateralDir / "driver.sv"
|
|
281
|
-
|
|
282
|
-
# Questa doesn't use stderr for error messages. Everything goes to stdout.
|
|
283
|
-
UsesStderr = False
|
|
284
|
-
|
|
285
|
-
def internal_compile_commands(self) -> List[str]:
|
|
286
|
-
cmds = [
|
|
287
|
-
"onerror { quit -f -code 1 }",
|
|
288
|
-
]
|
|
289
|
-
sources = self.sources.rtl_sources
|
|
290
|
-
sources.append(Questa.DefaultDriver)
|
|
291
|
-
for src in sources:
|
|
292
|
-
cmds.append(f"vlog -incr +acc -sv +define+TOP_MODULE={self.sources.top}"
|
|
293
|
-
f" +define+SIMULATION {str(src)}")
|
|
294
|
-
cmds.append(f"vopt -incr driver -o driver_opt +acc")
|
|
295
|
-
return cmds
|
|
296
|
-
|
|
297
|
-
def compile_commands(self) -> List[List[str]]:
|
|
298
|
-
with open("compile.do", "w") as f:
|
|
299
|
-
for cmd in self.internal_compile_commands():
|
|
300
|
-
f.write(cmd)
|
|
301
|
-
f.write("\n")
|
|
302
|
-
f.write("quit\n")
|
|
303
|
-
return [
|
|
304
|
-
["vsim", "-batch", "-do", "compile.do"],
|
|
305
|
-
]
|
|
306
|
-
|
|
307
|
-
def run_command(self, gui: bool) -> List[str]:
|
|
308
|
-
vsim = "vsim"
|
|
309
|
-
# Note: vsim exit codes say nothing about the test run's pass/fail even
|
|
310
|
-
# if $fatal is encountered in the simulation.
|
|
311
|
-
if gui:
|
|
312
|
-
cmd = [
|
|
313
|
-
vsim,
|
|
314
|
-
"driver_opt",
|
|
315
|
-
]
|
|
316
|
-
else:
|
|
317
|
-
cmd = [
|
|
318
|
-
vsim,
|
|
319
|
-
"driver_opt",
|
|
320
|
-
"-batch",
|
|
321
|
-
"-do",
|
|
322
|
-
"run -all",
|
|
323
|
-
]
|
|
324
|
-
for lib in self.sources.dpi_so_paths():
|
|
325
|
-
svLib = os.path.splitext(lib)[0]
|
|
326
|
-
cmd.append("-sv_lib")
|
|
327
|
-
cmd.append(svLib)
|
|
328
|
-
return cmd
|
|
329
|
-
|
|
330
|
-
def run(self,
|
|
331
|
-
inner_command: str,
|
|
332
|
-
gui: bool = False,
|
|
333
|
-
server_only: bool = False) -> int:
|
|
334
|
-
"""Override the Simulator.run() to add a soft link in the run directory (to
|
|
335
|
-
the work directory) before running vsim the usual way."""
|
|
336
|
-
|
|
337
|
-
# Create a soft link to the work directory.
|
|
338
|
-
workDir = self.run_dir / "work"
|
|
339
|
-
if not workDir.exists():
|
|
340
|
-
os.symlink(Path(os.getcwd()) / "work", workDir)
|
|
341
|
-
|
|
342
|
-
# Run the simulation.
|
|
343
|
-
return super().run(inner_command, gui, server_only=server_only)
|
|
22
|
+
from esiaccel.cosim.questa import Questa
|
|
23
|
+
from esiaccel.cosim.verilator import Verilator
|
|
24
|
+
from esiaccel.cosim.simulator import SourceFiles
|
|
344
25
|
|
|
345
26
|
|
|
346
27
|
def __main__(args):
|
|
Binary file
|
esiaccel/esiquery.exe
CHANGED
|
Binary file
|
|
@@ -86,13 +86,6 @@ public:
|
|
|
86
86
|
/// Disconnect from the accelerator cleanly.
|
|
87
87
|
virtual void disconnect();
|
|
88
88
|
|
|
89
|
-
// While building the design, keep around a std::map of active services
|
|
90
|
-
// indexed by the service name. When a new service is encountered during
|
|
91
|
-
// descent, add it to the table (perhaps overwriting one). Modifications to
|
|
92
|
-
// the table only apply to the current branch, so copy this and update it at
|
|
93
|
-
// each level of the tree.
|
|
94
|
-
using ServiceTable = std::map<std::string, services::Service *>;
|
|
95
|
-
|
|
96
89
|
/// Return a pointer to the accelerator 'service' thread (or threads). If the
|
|
97
90
|
/// thread(s) are not running, they will be started when this method is
|
|
98
91
|
/// called. `std::thread` is used. If users don't want the runtime to spin up
|
|
@@ -165,7 +158,7 @@ private:
|
|
|
165
158
|
|
|
166
159
|
/// Cache services via a unique_ptr so they get free'd automatically when
|
|
167
160
|
/// Accelerator objects get deconstructed.
|
|
168
|
-
using ServiceCacheKey = std::tuple<
|
|
161
|
+
using ServiceCacheKey = std::tuple<std::string, AppIDPath>;
|
|
169
162
|
std::map<ServiceCacheKey, std::unique_ptr<Service>> serviceCache;
|
|
170
163
|
|
|
171
164
|
std::unique_ptr<AcceleratorServiceThread> serviceThread;
|
|
@@ -176,12 +169,6 @@ private:
|
|
|
176
169
|
|
|
177
170
|
namespace registry {
|
|
178
171
|
|
|
179
|
-
// Connect to an ESI accelerator given a backend name and connection specifier.
|
|
180
|
-
// Alternatively, instantiate the backend directly (if you're using C++).
|
|
181
|
-
std::unique_ptr<AcceleratorConnection> connect(Context &ctxt,
|
|
182
|
-
const std::string &backend,
|
|
183
|
-
const std::string &connection);
|
|
184
|
-
|
|
185
172
|
namespace internal {
|
|
186
173
|
|
|
187
174
|
/// Backends can register themselves to be connected via a connection string.
|
esiaccel/include/esi/CLI.h
CHANGED
|
@@ -51,19 +51,19 @@ public:
|
|
|
51
51
|
ctxt = Context::withLogger<ConsoleLogger>(Logger::Level::Debug);
|
|
52
52
|
else if (verbose)
|
|
53
53
|
ctxt = Context::withLogger<ConsoleLogger>(Logger::Level::Info);
|
|
54
|
+
else
|
|
55
|
+
ctxt = Context::withLogger<ConsoleLogger>(Logger::Level::Warning);
|
|
54
56
|
return 0;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/// Connect to the accelerator using the specified backend and connection.
|
|
58
|
-
|
|
59
|
-
return ctxt.connect(backend, connStr);
|
|
60
|
-
}
|
|
60
|
+
AcceleratorConnection *connect() { return ctxt->connect(backend, connStr); }
|
|
61
61
|
|
|
62
62
|
/// Get the context.
|
|
63
|
-
Context &getContext() { return ctxt; }
|
|
63
|
+
Context &getContext() { return *ctxt; }
|
|
64
64
|
|
|
65
65
|
protected:
|
|
66
|
-
Context ctxt;
|
|
66
|
+
std::unique_ptr<Context> ctxt;
|
|
67
67
|
|
|
68
68
|
std::string backend;
|
|
69
69
|
std::string connStr;
|
esiaccel/include/esi/Common.h
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
#include <cstdint>
|
|
21
21
|
#include <map>
|
|
22
22
|
#include <optional>
|
|
23
|
+
#include <span>
|
|
23
24
|
#include <stdexcept>
|
|
24
25
|
#include <string>
|
|
25
26
|
#include <vector>
|
|
@@ -42,6 +43,13 @@ struct AppID {
|
|
|
42
43
|
return name == other.name && idx == other.idx;
|
|
43
44
|
}
|
|
44
45
|
bool operator!=(const AppID &other) const { return !(*this == other); }
|
|
46
|
+
friend std::ostream &operator<<(std::ostream &os, const AppID &id);
|
|
47
|
+
|
|
48
|
+
std::string toString() const {
|
|
49
|
+
if (idx.has_value())
|
|
50
|
+
return name + "[" + std::to_string(idx.value()) + "]";
|
|
51
|
+
return name;
|
|
52
|
+
}
|
|
45
53
|
};
|
|
46
54
|
bool operator<(const AppID &a, const AppID &b);
|
|
47
55
|
|
|
@@ -52,6 +60,7 @@ public:
|
|
|
52
60
|
AppIDPath operator+(const AppIDPath &b) const;
|
|
53
61
|
AppIDPath parent() const;
|
|
54
62
|
std::string toStr() const;
|
|
63
|
+
friend std::ostream &operator<<(std::ostream &os, const AppIDPath &path);
|
|
55
64
|
};
|
|
56
65
|
bool operator<(const AppIDPath &a, const AppIDPath &b);
|
|
57
66
|
|
|
@@ -105,6 +114,8 @@ class MessageData {
|
|
|
105
114
|
public:
|
|
106
115
|
/// Adopts the data vector buffer.
|
|
107
116
|
MessageData() = default;
|
|
117
|
+
MessageData(std::span<const uint8_t> data)
|
|
118
|
+
: data(data.data(), data.data() + data.size()) {}
|
|
108
119
|
MessageData(std::vector<uint8_t> &data) : data(std::move(data)) {}
|
|
109
120
|
MessageData(std::vector<uint8_t> &&data) : data(std::move(data)) {}
|
|
110
121
|
MessageData(const uint8_t *data, size_t size) : data(data, data + size) {}
|
|
@@ -115,11 +126,17 @@ public:
|
|
|
115
126
|
/// Get the data as a vector of bytes.
|
|
116
127
|
const std::vector<uint8_t> &getData() const { return data; }
|
|
117
128
|
|
|
129
|
+
/// Implicit conversion to a vector/span of bytes, to play nice with other
|
|
130
|
+
/// APIs that accept bytearray-like things.
|
|
131
|
+
operator const std::vector<uint8_t> &() const { return data; }
|
|
132
|
+
operator std::span<const uint8_t>() const { return data; }
|
|
133
|
+
|
|
118
134
|
/// Move the data out of this object.
|
|
119
135
|
std::vector<uint8_t> takeData() { return std::move(data); }
|
|
120
136
|
|
|
121
137
|
/// Get the size of the data in bytes.
|
|
122
138
|
size_t getSize() const { return data.size(); }
|
|
139
|
+
size_t size() const { return getSize(); }
|
|
123
140
|
|
|
124
141
|
/// Returns true if this message contains no data.
|
|
125
142
|
bool empty() const { return data.empty(); }
|
|
@@ -152,7 +169,6 @@ private:
|
|
|
152
169
|
} // namespace esi
|
|
153
170
|
|
|
154
171
|
std::ostream &operator<<(std::ostream &, const esi::ModuleInfo &);
|
|
155
|
-
std::ostream &operator<<(std::ostream &, const esi::AppID &);
|
|
156
172
|
|
|
157
173
|
//===----------------------------------------------------------------------===//
|
|
158
174
|
// Functions which should be in the standard library.
|
esiaccel/include/esi/Context.h
CHANGED
|
@@ -20,23 +20,30 @@
|
|
|
20
20
|
#include "esi/Types.h"
|
|
21
21
|
|
|
22
22
|
#include <exception>
|
|
23
|
+
#include <map>
|
|
23
24
|
#include <memory>
|
|
24
|
-
#include <
|
|
25
|
+
#include <vector>
|
|
25
26
|
|
|
26
27
|
namespace esi {
|
|
27
28
|
class AcceleratorConnection;
|
|
28
29
|
|
|
29
30
|
/// AcceleratorConnections, Accelerators, and Manifests must all share a
|
|
30
|
-
/// context. It owns all the types, uniquifying them.
|
|
31
|
+
/// context. It owns all the types, uniquifying them. It also owns the
|
|
32
|
+
/// connections (which own the Accelerators). When it is destroyed, all
|
|
33
|
+
/// connections are disconnected and the objects are destroyed.
|
|
31
34
|
class Context {
|
|
32
35
|
public:
|
|
33
|
-
Context()
|
|
34
|
-
Context(std::unique_ptr<Logger> logger)
|
|
36
|
+
Context();
|
|
37
|
+
Context(std::unique_ptr<Logger> logger);
|
|
38
|
+
~Context();
|
|
39
|
+
|
|
40
|
+
/// Disconnect from all accelerators associated with this context.
|
|
41
|
+
void disconnectAll();
|
|
35
42
|
|
|
36
43
|
/// Create a context with a specific logger type.
|
|
37
44
|
template <typename T, typename... Args>
|
|
38
|
-
static Context withLogger(Args &&...args) {
|
|
39
|
-
return Context(std::make_unique<T>(args...));
|
|
45
|
+
static std::unique_ptr<Context> withLogger(Args &&...args) {
|
|
46
|
+
return std::make_unique<Context>(std::make_unique<T>(args...));
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/// Resolve a type id to the type.
|
|
@@ -49,9 +56,9 @@ public:
|
|
|
49
56
|
/// Register a type with the context. Takes ownership of the pointer type.
|
|
50
57
|
void registerType(Type *type);
|
|
51
58
|
|
|
52
|
-
/// Connect to an accelerator backend.
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
/// Connect to an accelerator backend. Retains ownership internally and
|
|
60
|
+
/// returns a non-owning pointer.
|
|
61
|
+
AcceleratorConnection *connect(std::string backend, std::string connection);
|
|
55
62
|
|
|
56
63
|
/// Register a logger with the accelerator. Assumes ownership of the logger.
|
|
57
64
|
void setLogger(std::unique_ptr<Logger> logger) {
|
|
@@ -63,6 +70,7 @@ public:
|
|
|
63
70
|
|
|
64
71
|
private:
|
|
65
72
|
std::unique_ptr<Logger> logger;
|
|
73
|
+
std::vector<std::unique_ptr<AcceleratorConnection>> connections;
|
|
66
74
|
|
|
67
75
|
private:
|
|
68
76
|
using TypeCache = std::map<Type::ID, std::unique_ptr<Type>>;
|
esiaccel/include/esi/Manifest.h
CHANGED
|
@@ -65,8 +65,6 @@ private:
|
|
|
65
65
|
|
|
66
66
|
} // namespace esi
|
|
67
67
|
|
|
68
|
-
std::ostream &operator<<(std::ostream &os, const esi::AppID &id);
|
|
69
|
-
std::ostream &operator<<(std::ostream &, const esi::AppIDPath &);
|
|
70
68
|
std::ostream &operator<<(std::ostream &, const esi::ModuleInfo &);
|
|
71
69
|
|
|
72
70
|
#endif // ESI_MANIFEST_H
|
esiaccel/include/esi/Services.h
CHANGED
|
@@ -25,10 +25,21 @@
|
|
|
25
25
|
#include "esi/Ports.h"
|
|
26
26
|
|
|
27
27
|
#include <cstdint>
|
|
28
|
+
#include <list>
|
|
28
29
|
|
|
29
30
|
namespace esi {
|
|
30
31
|
class AcceleratorConnection;
|
|
31
32
|
class Engine;
|
|
33
|
+
namespace services {
|
|
34
|
+
class Service;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// While building the design, keep around a std::map of active services indexed
|
|
38
|
+
// by the service name. When a new service is encountered during descent, add it
|
|
39
|
+
// to the table (perhaps overwriting one). Modifications to the table only apply
|
|
40
|
+
// to the current branch, so copy this and update it at each level of the tree.
|
|
41
|
+
using ServiceTable = std::map<std::string, services::Service *>;
|
|
42
|
+
|
|
32
43
|
namespace services {
|
|
33
44
|
|
|
34
45
|
/// Add a custom interface to a service client at a particular point in the
|
|
@@ -271,13 +282,20 @@ public:
|
|
|
271
282
|
void connect();
|
|
272
283
|
std::future<MessageData> call(const MessageData &arg);
|
|
273
284
|
|
|
285
|
+
const esi::Type *getArgType() const {
|
|
286
|
+
return dynamic_cast<const ChannelType *>(type->findChannel("arg").first)
|
|
287
|
+
->getInner();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const esi::Type *getResultType() const {
|
|
291
|
+
return dynamic_cast<const ChannelType *>(
|
|
292
|
+
type->findChannel("result").first)
|
|
293
|
+
->getInner();
|
|
294
|
+
}
|
|
295
|
+
|
|
274
296
|
virtual std::optional<std::string> toString() const override {
|
|
275
|
-
const esi::Type *argType =
|
|
276
|
-
|
|
277
|
-
->getInner();
|
|
278
|
-
const esi::Type *resultType =
|
|
279
|
-
dynamic_cast<const ChannelType *>(type->findChannel("result").first)
|
|
280
|
-
->getInner();
|
|
297
|
+
const esi::Type *argType = getArgType();
|
|
298
|
+
const esi::Type *resultType = getResultType();
|
|
281
299
|
return "function " + resultType->getID() + "(" + argType->getID() + ")";
|
|
282
300
|
}
|
|
283
301
|
|
|
@@ -320,13 +338,20 @@ public:
|
|
|
320
338
|
void connect(std::function<MessageData(const MessageData &)> callback,
|
|
321
339
|
bool quick = false);
|
|
322
340
|
|
|
341
|
+
const esi::Type *getArgType() const {
|
|
342
|
+
return dynamic_cast<const ChannelType *>(type->findChannel("arg").first)
|
|
343
|
+
->getInner();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const esi::Type *getResultType() const {
|
|
347
|
+
return dynamic_cast<const ChannelType *>(
|
|
348
|
+
type->findChannel("result").first)
|
|
349
|
+
->getInner();
|
|
350
|
+
}
|
|
351
|
+
|
|
323
352
|
virtual std::optional<std::string> toString() const override {
|
|
324
|
-
const esi::Type *argType =
|
|
325
|
-
|
|
326
|
-
->getInner();
|
|
327
|
-
const esi::Type *resultType =
|
|
328
|
-
dynamic_cast<const ChannelType *>(type->findChannel("result").first)
|
|
329
|
-
->getInner();
|
|
353
|
+
const esi::Type *argType = getArgType();
|
|
354
|
+
const esi::Type *resultType = getResultType();
|
|
330
355
|
return "callback " + resultType->getID() + "(" + argType->getID() + ")";
|
|
331
356
|
}
|
|
332
357
|
|
|
@@ -351,18 +376,23 @@ public:
|
|
|
351
376
|
virtual std::string getServiceSymbol() const override;
|
|
352
377
|
virtual BundlePort *getPort(AppIDPath id,
|
|
353
378
|
const BundleType *type) const override;
|
|
379
|
+
virtual Service *getChildService(Service::Type service, AppIDPath id = {},
|
|
380
|
+
std::string implName = {},
|
|
381
|
+
ServiceImplDetails details = {},
|
|
382
|
+
HWClientDetails clients = {}) override;
|
|
383
|
+
MMIO::MMIORegion *getMMIORegion() const;
|
|
354
384
|
|
|
355
385
|
/// A telemetry port which gets attached to a service port.
|
|
356
|
-
class
|
|
386
|
+
class Metric : public ServicePort {
|
|
357
387
|
friend class TelemetryService;
|
|
358
|
-
|
|
388
|
+
Metric(AppID id, const BundleType *type, PortMap channels,
|
|
389
|
+
const TelemetryService *telemetryService,
|
|
390
|
+
std::optional<uint64_t> offset);
|
|
359
391
|
|
|
360
392
|
public:
|
|
361
|
-
static Telemetry *get(AppID id, BundleType *type, WriteChannelPort &get,
|
|
362
|
-
ReadChannelPort &data);
|
|
363
|
-
|
|
364
393
|
void connect();
|
|
365
394
|
std::future<MessageData> read();
|
|
395
|
+
uint64_t readInt();
|
|
366
396
|
|
|
367
397
|
virtual std::optional<std::string> toString() const override {
|
|
368
398
|
const esi::Type *dataType =
|
|
@@ -372,16 +402,24 @@ public:
|
|
|
372
402
|
}
|
|
373
403
|
|
|
374
404
|
private:
|
|
375
|
-
|
|
376
|
-
|
|
405
|
+
const TelemetryService *telemetryService;
|
|
406
|
+
MMIO::MMIORegion *mmio;
|
|
407
|
+
std::optional<uint64_t> offset;
|
|
377
408
|
};
|
|
378
409
|
|
|
379
|
-
|
|
380
|
-
|
|
410
|
+
std::map<AppIDPath, Metric *> getTelemetryPorts() {
|
|
411
|
+
std::map<AppIDPath, Metric *> ports;
|
|
412
|
+
getTelemetryPorts(ports);
|
|
413
|
+
return ports;
|
|
381
414
|
}
|
|
415
|
+
void getTelemetryPorts(std::map<AppIDPath, Metric *> &ports);
|
|
382
416
|
|
|
383
417
|
private:
|
|
384
|
-
|
|
418
|
+
AppIDPath id;
|
|
419
|
+
mutable MMIO::MMIORegion *mmio;
|
|
420
|
+
std::map<AppIDPath, uint64_t> portAddressAssignments;
|
|
421
|
+
mutable std::map<AppIDPath, Metric *> telemetryPorts;
|
|
422
|
+
std::list<TelemetryService *> children;
|
|
385
423
|
};
|
|
386
424
|
|
|
387
425
|
/// Registry of services which can be instantiated directly by the Accelerator
|