esiaccel 0.0.10__cp310-cp310-win_amd64.whl → 0.1.5.dev406__cp310-cp310-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.
Potentially problematic release.
This version of esiaccel might be problematic. Click here for more details.
- 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 +10 -1
- esiaccel/abseil_dll.dll +0 -0
- esiaccel/accelerator.py +15 -1
- esiaccel/cares.dll +0 -0
- esiaccel/cmake/esiaccelConfig.cmake +1 -1
- esiaccel/codegen.py +197 -0
- esiaccel/cosim/Cosim_DpiPkg.sv +85 -0
- esiaccel/cosim/Cosim_Endpoint.sv +218 -0
- esiaccel/cosim/Cosim_Manifest.sv +32 -0
- esiaccel/cosim/driver.cpp +131 -0
- esiaccel/cosim/driver.sv +74 -0
- esiaccel/cosim/questa.py +141 -0
- esiaccel/cosim/simulator.py +382 -0
- esiaccel/cosim/verilator.py +92 -0
- esiaccel/esi-cosim.py +104 -0
- esiaccel/esiCppAccel.cp310-win_amd64.pyd +0 -0
- esiaccel/esiquery.exe +0 -0
- esiaccel/include/esi/Accelerator.h +33 -9
- esiaccel/include/esi/CLI.h +77 -0
- esiaccel/include/esi/Common.h +59 -10
- esiaccel/include/esi/Context.h +21 -1
- esiaccel/include/esi/Design.h +14 -3
- esiaccel/include/esi/Engines.h +124 -0
- esiaccel/include/esi/Logging.h +231 -0
- esiaccel/include/esi/Manifest.h +0 -2
- esiaccel/include/esi/Ports.h +54 -7
- esiaccel/include/esi/Services.h +225 -22
- esiaccel/include/esi/Types.h +103 -5
- esiaccel/include/esi/Utils.h +5 -0
- esiaccel/include/esi/Values.h +313 -0
- esiaccel/include/esi/backends/Cosim.h +85 -0
- esiaccel/include/esi/backends/RpcServer.h +55 -0
- esiaccel/include/esi/backends/Trace.h +8 -5
- 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 +174 -33
- esiaccel/utils.py +27 -3
- esiaccel/zlib1.dll +0 -0
- {esiaccel-0.0.10.dist-info → esiaccel-0.1.5.dev406.dist-info}/METADATA +3 -3
- esiaccel-0.1.5.dev406.dist-info/RECORD +54 -0
- {esiaccel-0.0.10.dist-info → esiaccel-0.1.5.dev406.dist-info}/WHEEL +1 -1
- {esiaccel-0.0.10.dist-info → esiaccel-0.1.5.dev406.dist-info}/entry_points.txt +1 -0
- esiaccel/bin/ESICppRuntime.dll +0 -0
- esiaccel/bin/esiquery.exe +0 -0
- esiaccel/bin/zlib1.dll +0 -0
- esiaccel-0.0.10.dist-info/RECORD +0 -28
- {esiaccel-0.0.10.dist-info → esiaccel-0.1.5.dev406.dist-info/licenses}/LICENSE +0 -0
- {esiaccel-0.0.10.dist-info → esiaccel-0.1.5.dev406.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,382 @@
|
|
|
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
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import signal
|
|
8
|
+
import socket
|
|
9
|
+
import subprocess
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Dict, List, Optional, Callable, IO
|
|
13
|
+
import threading
|
|
14
|
+
|
|
15
|
+
_thisdir = Path(__file__).parent
|
|
16
|
+
CosimCollateralDir = _thisdir
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_port_open(port) -> bool:
|
|
20
|
+
"""Check if a TCP port is open locally."""
|
|
21
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
22
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
|
23
|
+
sock.close()
|
|
24
|
+
return True if result == 0 else False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SourceFiles:
|
|
28
|
+
|
|
29
|
+
def __init__(self, top: str) -> None:
|
|
30
|
+
# User source files.
|
|
31
|
+
self.user: List[Path] = []
|
|
32
|
+
# DPI shared objects.
|
|
33
|
+
self.dpi_so: List[str] = ["EsiCosimDpiServer"]
|
|
34
|
+
# DPI SV files.
|
|
35
|
+
self.dpi_sv: List[Path] = [
|
|
36
|
+
CosimCollateralDir / "Cosim_DpiPkg.sv",
|
|
37
|
+
CosimCollateralDir / "Cosim_Endpoint.sv",
|
|
38
|
+
CosimCollateralDir / "Cosim_Manifest.sv",
|
|
39
|
+
]
|
|
40
|
+
# Name of the top module.
|
|
41
|
+
self.top = top
|
|
42
|
+
|
|
43
|
+
def add_file(self, file: Path):
|
|
44
|
+
"""Add a single RTL file to the source list."""
|
|
45
|
+
if file.is_file():
|
|
46
|
+
self.user.append(file)
|
|
47
|
+
else:
|
|
48
|
+
raise FileNotFoundError(f"File {file} does not exist")
|
|
49
|
+
|
|
50
|
+
def add_dir(self, dir: Path):
|
|
51
|
+
"""Add all the RTL files in a directory to the source list."""
|
|
52
|
+
for file in sorted(dir.iterdir()):
|
|
53
|
+
if file.is_file() and (file.suffix == ".sv" or file.suffix == ".v"):
|
|
54
|
+
self.user.append(file)
|
|
55
|
+
elif file.is_dir():
|
|
56
|
+
self.add_dir(file)
|
|
57
|
+
|
|
58
|
+
def dpi_so_paths(self) -> List[Path]:
|
|
59
|
+
"""Return a list of all the DPI shared object files."""
|
|
60
|
+
|
|
61
|
+
def find_so(name: str) -> Path:
|
|
62
|
+
for path in Simulator.get_env().get("LD_LIBRARY_PATH", "").split(":"):
|
|
63
|
+
if os.name == "nt":
|
|
64
|
+
so = Path(path) / f"{name}.dll"
|
|
65
|
+
else:
|
|
66
|
+
so = Path(path) / f"lib{name}.so"
|
|
67
|
+
if so.exists():
|
|
68
|
+
return so
|
|
69
|
+
raise FileNotFoundError(f"Could not find {name}.so in LD_LIBRARY_PATH")
|
|
70
|
+
|
|
71
|
+
return [find_so(name) for name in self.dpi_so]
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def rtl_sources(self) -> List[Path]:
|
|
75
|
+
"""Return a list of all the RTL source files."""
|
|
76
|
+
return self.dpi_sv + self.user
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class SimProcess:
|
|
80
|
+
|
|
81
|
+
def __init__(self,
|
|
82
|
+
proc: subprocess.Popen,
|
|
83
|
+
port: int,
|
|
84
|
+
threads: Optional[List[threading.Thread]] = None,
|
|
85
|
+
gui: bool = False):
|
|
86
|
+
self.proc = proc
|
|
87
|
+
self.port = port
|
|
88
|
+
self.threads: List[threading.Thread] = threads or []
|
|
89
|
+
self.gui = gui
|
|
90
|
+
|
|
91
|
+
def force_stop(self):
|
|
92
|
+
"""Make sure to stop the simulation no matter what."""
|
|
93
|
+
if self.proc:
|
|
94
|
+
os.killpg(os.getpgid(self.proc.pid), signal.SIGINT)
|
|
95
|
+
# Allow the simulation time to flush its outputs.
|
|
96
|
+
try:
|
|
97
|
+
self.proc.wait(timeout=1.0)
|
|
98
|
+
except subprocess.TimeoutExpired:
|
|
99
|
+
# If the simulation doesn't exit of its own free will, kill it.
|
|
100
|
+
self.proc.kill()
|
|
101
|
+
|
|
102
|
+
# Join reader threads (they should exit once pipes are closed).
|
|
103
|
+
for t in self.threads:
|
|
104
|
+
t.join()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Simulator:
|
|
108
|
+
|
|
109
|
+
# Some RTL simulators don't use stderr for error messages. Everything goes to
|
|
110
|
+
# stdout. Boo! They should feel bad about this. Also, they can specify that
|
|
111
|
+
# broken behavior by overriding this.
|
|
112
|
+
UsesStderr = True
|
|
113
|
+
|
|
114
|
+
def __init__(self,
|
|
115
|
+
sources: SourceFiles,
|
|
116
|
+
run_dir: Path,
|
|
117
|
+
debug: bool,
|
|
118
|
+
run_stdout_callback: Optional[Callable[[str], None]] = None,
|
|
119
|
+
run_stderr_callback: Optional[Callable[[str], None]] = None,
|
|
120
|
+
compile_stdout_callback: Optional[Callable[[str], None]] = None,
|
|
121
|
+
compile_stderr_callback: Optional[Callable[[str], None]] = None,
|
|
122
|
+
make_default_logs: bool = True,
|
|
123
|
+
macro_definitions: Optional[Dict[str, str]] = None):
|
|
124
|
+
"""Simulator base class.
|
|
125
|
+
|
|
126
|
+
Optional sinks can be provided for capturing output. If not provided,
|
|
127
|
+
the simulator will write to log files in `run_dir`.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
sources: SourceFiles describing RTL/DPI inputs.
|
|
131
|
+
run_dir: Directory where build/run artifacts are placed.
|
|
132
|
+
debug: Enable cosim debug mode.
|
|
133
|
+
run_stdout_callback: Line-based callback for runtime stdout.
|
|
134
|
+
run_stderr_callback: Line-based callback for runtime stderr.
|
|
135
|
+
compile_stdout_callback: Line-based callback for compile stdout.
|
|
136
|
+
compile_stderr_callback: Line-based callback for compile stderr.
|
|
137
|
+
make_default_logs: If True and corresponding callback is not supplied,
|
|
138
|
+
create log file and emit via internally-created callback.
|
|
139
|
+
macro_definitions: Optional dictionary of macro definitions to be defined
|
|
140
|
+
during compilation.
|
|
141
|
+
"""
|
|
142
|
+
self.sources = sources
|
|
143
|
+
self.run_dir = run_dir
|
|
144
|
+
self.debug = debug
|
|
145
|
+
self.macro_definitions = macro_definitions
|
|
146
|
+
|
|
147
|
+
# Unified list of any log file handles we opened.
|
|
148
|
+
self._default_files: List[IO[str]] = []
|
|
149
|
+
|
|
150
|
+
def _ensure_default(cb: Optional[Callable[[str], None]], filename: str):
|
|
151
|
+
"""Return (callback, file_handle_or_None) with optional file creation.
|
|
152
|
+
|
|
153
|
+
Behavior:
|
|
154
|
+
* If a callback is provided, return it unchanged with no file.
|
|
155
|
+
* If no callback and make_default_logs is False, return (None, None).
|
|
156
|
+
* If no callback and make_default_logs is True, create a log file and
|
|
157
|
+
return a writer callback plus the opened file handle.
|
|
158
|
+
"""
|
|
159
|
+
if cb is not None:
|
|
160
|
+
return cb, None
|
|
161
|
+
if not make_default_logs:
|
|
162
|
+
return None, None
|
|
163
|
+
p = self.run_dir / filename
|
|
164
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
logf = p.open("w+")
|
|
166
|
+
self._default_files.append(logf)
|
|
167
|
+
|
|
168
|
+
def _writer(line: str, _lf=logf):
|
|
169
|
+
_lf.write(line + "\n")
|
|
170
|
+
_lf.flush()
|
|
171
|
+
|
|
172
|
+
return _writer, logf
|
|
173
|
+
|
|
174
|
+
# Initialize all four (compile/run stdout/stderr) uniformly.
|
|
175
|
+
self._compile_stdout_cb, self._compile_stdout_log = _ensure_default(
|
|
176
|
+
compile_stdout_callback, 'compile_stdout.log')
|
|
177
|
+
self._compile_stderr_cb, self._compile_stderr_log = _ensure_default(
|
|
178
|
+
compile_stderr_callback, 'compile_stderr.log')
|
|
179
|
+
self._run_stdout_cb, self._run_stdout_log = _ensure_default(
|
|
180
|
+
run_stdout_callback, 'sim_stdout.log')
|
|
181
|
+
self._run_stderr_cb, self._run_stderr_log = _ensure_default(
|
|
182
|
+
run_stderr_callback, 'sim_stderr.log')
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def get_env() -> Dict[str, str]:
|
|
186
|
+
"""Get the environment variables to locate shared objects."""
|
|
187
|
+
|
|
188
|
+
env = os.environ.copy()
|
|
189
|
+
env["LIBRARY_PATH"] = env.get("LIBRARY_PATH", "") + ":" + str(
|
|
190
|
+
_thisdir.parent / "lib")
|
|
191
|
+
env["LD_LIBRARY_PATH"] = env.get("LD_LIBRARY_PATH", "") + ":" + str(
|
|
192
|
+
_thisdir.parent / "lib")
|
|
193
|
+
return env
|
|
194
|
+
|
|
195
|
+
def compile_commands(self) -> List[List[str]]:
|
|
196
|
+
"""Compile the sources. Returns the exit code of the simulation compiler."""
|
|
197
|
+
assert False, "Must be implemented by subclass"
|
|
198
|
+
|
|
199
|
+
def compile(self) -> int:
|
|
200
|
+
cmds = self.compile_commands()
|
|
201
|
+
self.run_dir.mkdir(parents=True, exist_ok=True)
|
|
202
|
+
for cmd in cmds:
|
|
203
|
+
ret = self._start_process_with_callbacks(
|
|
204
|
+
cmd,
|
|
205
|
+
env=Simulator.get_env(),
|
|
206
|
+
cwd=None,
|
|
207
|
+
stdout_cb=self._compile_stdout_cb,
|
|
208
|
+
stderr_cb=self._compile_stderr_cb,
|
|
209
|
+
wait=True)
|
|
210
|
+
if isinstance(ret, int) and ret != 0:
|
|
211
|
+
print("====== Compilation failure")
|
|
212
|
+
|
|
213
|
+
# If we have the default file loggers, print the compilation logs to
|
|
214
|
+
# console. Else, assume that the user has already captured them.
|
|
215
|
+
if self.UsesStderr:
|
|
216
|
+
if self._compile_stderr_log is not None:
|
|
217
|
+
self._compile_stderr_log.seek(0)
|
|
218
|
+
print(self._compile_stderr_log.read())
|
|
219
|
+
else:
|
|
220
|
+
if self._compile_stdout_log is not None:
|
|
221
|
+
self._compile_stdout_log.seek(0)
|
|
222
|
+
print(self._compile_stdout_log.read())
|
|
223
|
+
|
|
224
|
+
return ret
|
|
225
|
+
return 0
|
|
226
|
+
|
|
227
|
+
def run_command(self, gui: bool) -> List[str]:
|
|
228
|
+
"""Return the command to run the simulation."""
|
|
229
|
+
assert False, "Must be implemented by subclass"
|
|
230
|
+
|
|
231
|
+
def run_proc(self, gui: bool = False) -> SimProcess:
|
|
232
|
+
"""Run the simulation process. Returns the Popen object and the port which
|
|
233
|
+
the simulation is listening on.
|
|
234
|
+
|
|
235
|
+
If user-provided stdout/stderr sinks were supplied in the constructor,
|
|
236
|
+
they are used. Otherwise, log files are created in `run_dir`.
|
|
237
|
+
"""
|
|
238
|
+
self.run_dir.mkdir(parents=True, exist_ok=True)
|
|
239
|
+
|
|
240
|
+
env_gui = os.environ.get("COSIM_GUI", "0")
|
|
241
|
+
if env_gui != "0":
|
|
242
|
+
gui = True
|
|
243
|
+
|
|
244
|
+
# Erase the config file if it exists. We don't want to read
|
|
245
|
+
# an old config.
|
|
246
|
+
portFileName = self.run_dir / "cosim.cfg"
|
|
247
|
+
if os.path.exists(portFileName):
|
|
248
|
+
os.remove(portFileName)
|
|
249
|
+
|
|
250
|
+
# Run the simulation.
|
|
251
|
+
simEnv = Simulator.get_env()
|
|
252
|
+
if self.debug:
|
|
253
|
+
simEnv["COSIM_DEBUG_FILE"] = "cosim_debug.log"
|
|
254
|
+
if "DEBUG_PERIOD" not in simEnv:
|
|
255
|
+
# Slow the simulation down to one tick per millisecond.
|
|
256
|
+
simEnv["DEBUG_PERIOD"] = "1"
|
|
257
|
+
rcmd = self.run_command(gui)
|
|
258
|
+
# Start process with asynchronous output capture.
|
|
259
|
+
proc, threads = self._start_process_with_callbacks(
|
|
260
|
+
rcmd,
|
|
261
|
+
env=simEnv,
|
|
262
|
+
cwd=self.run_dir,
|
|
263
|
+
stdout_cb=self._run_stdout_cb,
|
|
264
|
+
stderr_cb=self._run_stderr_cb,
|
|
265
|
+
wait=False)
|
|
266
|
+
|
|
267
|
+
# Get the port which the simulation RPC selected.
|
|
268
|
+
checkCount = 0
|
|
269
|
+
while (not os.path.exists(portFileName)) and \
|
|
270
|
+
proc.poll() is None:
|
|
271
|
+
time.sleep(0.1)
|
|
272
|
+
checkCount += 1
|
|
273
|
+
if checkCount > 500 and not gui:
|
|
274
|
+
raise Exception(f"Cosim never wrote cfg file: {portFileName}")
|
|
275
|
+
port = -1
|
|
276
|
+
while port < 0:
|
|
277
|
+
portFile = open(portFileName, "r")
|
|
278
|
+
for line in portFile.readlines():
|
|
279
|
+
m = re.match("port: (\\d+)", line)
|
|
280
|
+
if m is not None:
|
|
281
|
+
port = int(m.group(1))
|
|
282
|
+
portFile.close()
|
|
283
|
+
|
|
284
|
+
# Wait for the simulation to start accepting RPC connections.
|
|
285
|
+
checkCount = 0
|
|
286
|
+
while not is_port_open(port):
|
|
287
|
+
checkCount += 1
|
|
288
|
+
if checkCount > 200:
|
|
289
|
+
raise Exception(f"Cosim RPC port ({port}) never opened")
|
|
290
|
+
if proc.poll() is not None:
|
|
291
|
+
raise Exception("Simulation exited early")
|
|
292
|
+
time.sleep(0.05)
|
|
293
|
+
return SimProcess(proc=proc, port=port, threads=threads, gui=gui)
|
|
294
|
+
|
|
295
|
+
def _start_process_with_callbacks(
|
|
296
|
+
self, cmd: List[str], env: Optional[Dict[str, str]], cwd: Optional[Path],
|
|
297
|
+
stdout_cb: Optional[Callable[[str],
|
|
298
|
+
None]], stderr_cb: Optional[Callable[[str],
|
|
299
|
+
None]],
|
|
300
|
+
wait: bool) -> int | tuple[subprocess.Popen, List[threading.Thread]]:
|
|
301
|
+
"""Start a subprocess and stream its stdout/stderr to callbacks.
|
|
302
|
+
|
|
303
|
+
If wait is True, blocks until process completes and returns its exit code.
|
|
304
|
+
If wait is False, returns the Popen object (threads keep streaming).
|
|
305
|
+
"""
|
|
306
|
+
if os.name == "posix":
|
|
307
|
+
proc = subprocess.Popen(cmd,
|
|
308
|
+
stdout=subprocess.PIPE,
|
|
309
|
+
stderr=subprocess.PIPE,
|
|
310
|
+
env=env,
|
|
311
|
+
cwd=cwd,
|
|
312
|
+
text=True,
|
|
313
|
+
preexec_fn=os.setsid)
|
|
314
|
+
else: # windows
|
|
315
|
+
proc = subprocess.Popen(cmd,
|
|
316
|
+
stdout=subprocess.PIPE,
|
|
317
|
+
stderr=subprocess.PIPE,
|
|
318
|
+
env=env,
|
|
319
|
+
cwd=cwd,
|
|
320
|
+
text=True,
|
|
321
|
+
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
|
|
322
|
+
|
|
323
|
+
def _reader(pipe, cb):
|
|
324
|
+
if pipe is None:
|
|
325
|
+
return
|
|
326
|
+
for raw in pipe:
|
|
327
|
+
if raw.endswith('\n'):
|
|
328
|
+
raw = raw[:-1]
|
|
329
|
+
if cb:
|
|
330
|
+
try:
|
|
331
|
+
cb(raw)
|
|
332
|
+
except Exception as e:
|
|
333
|
+
print(f"Exception in simulator output callback: {e}")
|
|
334
|
+
|
|
335
|
+
threads: List[threading.Thread] = [
|
|
336
|
+
threading.Thread(target=_reader,
|
|
337
|
+
args=(proc.stdout, stdout_cb),
|
|
338
|
+
daemon=True),
|
|
339
|
+
threading.Thread(target=_reader,
|
|
340
|
+
args=(proc.stderr, stderr_cb),
|
|
341
|
+
daemon=True),
|
|
342
|
+
]
|
|
343
|
+
for t in threads:
|
|
344
|
+
t.start()
|
|
345
|
+
if wait:
|
|
346
|
+
for t in threads:
|
|
347
|
+
t.join()
|
|
348
|
+
return proc.wait()
|
|
349
|
+
return proc, threads
|
|
350
|
+
|
|
351
|
+
def run(self,
|
|
352
|
+
inner_command: str,
|
|
353
|
+
gui: bool = False,
|
|
354
|
+
server_only: bool = False) -> int:
|
|
355
|
+
"""Start the simulation then run the command specified. Kill the simulation
|
|
356
|
+
when the command exits."""
|
|
357
|
+
|
|
358
|
+
# 'simProc' is accessed in the finally block. Declare it here to avoid
|
|
359
|
+
# syntax errors in that block.
|
|
360
|
+
simProc = None
|
|
361
|
+
try:
|
|
362
|
+
simProc = self.run_proc(gui=gui)
|
|
363
|
+
if server_only:
|
|
364
|
+
# wait for user input to kill the server
|
|
365
|
+
input(
|
|
366
|
+
f"Running in server-only mode on port {simProc.port} - Press anything to kill the server..."
|
|
367
|
+
)
|
|
368
|
+
return 0
|
|
369
|
+
else:
|
|
370
|
+
# Run the inner command, passing the connection info via environment vars.
|
|
371
|
+
testEnv = os.environ.copy()
|
|
372
|
+
testEnv["ESI_COSIM_PORT"] = str(simProc.port)
|
|
373
|
+
testEnv["ESI_COSIM_HOST"] = "localhost"
|
|
374
|
+
ret = subprocess.run(inner_command, cwd=os.getcwd(),
|
|
375
|
+
env=testEnv).returncode
|
|
376
|
+
if simProc.gui:
|
|
377
|
+
print("GUI mode - waiting for simulator to exit...")
|
|
378
|
+
simProc.proc.wait()
|
|
379
|
+
return ret
|
|
380
|
+
finally:
|
|
381
|
+
if simProc and simProc.proc.poll() is None:
|
|
382
|
+
simProc.force_stop()
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Optional, Callable, Dict
|
|
8
|
+
|
|
9
|
+
from .simulator import CosimCollateralDir, Simulator, SourceFiles
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Verilator(Simulator):
|
|
13
|
+
"""Run and compile funcs for Verilator."""
|
|
14
|
+
|
|
15
|
+
DefaultDriver = CosimCollateralDir / "driver.cpp"
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
sources: SourceFiles,
|
|
20
|
+
run_dir: Path,
|
|
21
|
+
debug: bool,
|
|
22
|
+
run_stdout_callback: Optional[Callable[[str], None]] = None,
|
|
23
|
+
run_stderr_callback: Optional[Callable[[str], None]] = None,
|
|
24
|
+
compile_stdout_callback: Optional[Callable[[str], None]] = None,
|
|
25
|
+
compile_stderr_callback: Optional[Callable[[str], None]] = None,
|
|
26
|
+
make_default_logs: bool = True,
|
|
27
|
+
macro_definitions: Optional[Dict[str, str]] = None,
|
|
28
|
+
):
|
|
29
|
+
super().__init__(
|
|
30
|
+
sources=sources,
|
|
31
|
+
run_dir=run_dir,
|
|
32
|
+
debug=debug,
|
|
33
|
+
run_stdout_callback=run_stdout_callback,
|
|
34
|
+
run_stderr_callback=run_stderr_callback,
|
|
35
|
+
compile_stdout_callback=compile_stdout_callback,
|
|
36
|
+
compile_stderr_callback=compile_stderr_callback,
|
|
37
|
+
make_default_logs=make_default_logs,
|
|
38
|
+
macro_definitions=macro_definitions,
|
|
39
|
+
)
|
|
40
|
+
self.verilator = "verilator"
|
|
41
|
+
if "VERILATOR_PATH" in os.environ:
|
|
42
|
+
self.verilator = os.environ["VERILATOR_PATH"]
|
|
43
|
+
|
|
44
|
+
def compile_commands(self) -> List[List[str]]:
|
|
45
|
+
cmd: List[str] = [
|
|
46
|
+
self.verilator,
|
|
47
|
+
"--cc",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
if self.macro_definitions:
|
|
51
|
+
cmd += [
|
|
52
|
+
f"+define+{k}={v}" if v is not None else f"+define+{k}"
|
|
53
|
+
for k, v in self.macro_definitions.items()
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
cmd += [
|
|
57
|
+
"--top-module",
|
|
58
|
+
self.sources.top,
|
|
59
|
+
"-DSIMULATION",
|
|
60
|
+
"-Wno-TIMESCALEMOD",
|
|
61
|
+
"-Wno-fatal",
|
|
62
|
+
"-sv",
|
|
63
|
+
"--exe",
|
|
64
|
+
"--build",
|
|
65
|
+
"-j",
|
|
66
|
+
"0",
|
|
67
|
+
"--output-split",
|
|
68
|
+
"--autoflush",
|
|
69
|
+
"--assert",
|
|
70
|
+
str(Verilator.DefaultDriver),
|
|
71
|
+
]
|
|
72
|
+
cflags = [
|
|
73
|
+
"-DTOP_MODULE=" + self.sources.top,
|
|
74
|
+
]
|
|
75
|
+
if self.debug:
|
|
76
|
+
cmd += [
|
|
77
|
+
"--trace-fst", "--trace-params", "--trace-structs",
|
|
78
|
+
"--trace-underscore"
|
|
79
|
+
]
|
|
80
|
+
cflags.append("-DTRACE")
|
|
81
|
+
if len(cflags) > 0:
|
|
82
|
+
cmd += ["-CFLAGS", " ".join(cflags)]
|
|
83
|
+
if len(self.sources.dpi_so) > 0:
|
|
84
|
+
cmd += ["-LDFLAGS", " ".join(["-l" + so for so in self.sources.dpi_so])]
|
|
85
|
+
cmd += [str(p) for p in self.sources.rtl_sources]
|
|
86
|
+
return [cmd]
|
|
87
|
+
|
|
88
|
+
def run_command(self, gui: bool):
|
|
89
|
+
if gui:
|
|
90
|
+
raise RuntimeError("Verilator does not support GUI mode.")
|
|
91
|
+
exe = Path.cwd() / "obj_dir" / ("V" + self.sources.top)
|
|
92
|
+
return [str(exe)]
|
esiaccel/esi-cosim.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# ===- esi-cosim.py - ESI cosimulation launch utility --------*- python -*-===//
|
|
4
|
+
#
|
|
5
|
+
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
6
|
+
# See https://llvm.org/LICENSE.txt for license information.
|
|
7
|
+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
8
|
+
#
|
|
9
|
+
# ===----------------------------------------------------------------------===//
|
|
10
|
+
#
|
|
11
|
+
# Utility script to start a simulation and launch a command to interact with it
|
|
12
|
+
# via ESI cosimulation.
|
|
13
|
+
#
|
|
14
|
+
# ===----------------------------------------------------------------------===//
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
import sys
|
|
19
|
+
import textwrap
|
|
20
|
+
from typing import Dict, List
|
|
21
|
+
|
|
22
|
+
from esiaccel.cosim.questa import Questa
|
|
23
|
+
from esiaccel.cosim.verilator import Verilator
|
|
24
|
+
from esiaccel.cosim.simulator import SourceFiles
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def __main__(args):
|
|
28
|
+
argparser = argparse.ArgumentParser(
|
|
29
|
+
description="Wrap a 'inner_cmd' in an ESI cosimulation environment.",
|
|
30
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
31
|
+
epilog=textwrap.dedent("""
|
|
32
|
+
Notes:
|
|
33
|
+
- For Verilator, libEsiCosimDpiServer.so must be in the dynamic
|
|
34
|
+
library runtime search path (LD_LIBRARY_PATH) and link time path
|
|
35
|
+
(LIBRARY_PATH). If it is installed to a standard location (e.g.
|
|
36
|
+
/usr/lib), this should be handled automatically.
|
|
37
|
+
- This script needs to sit in the same directory as the ESI support
|
|
38
|
+
SystemVerilog (e.g. Cosim_DpiPkg.sv, Cosim_MMIO.sv, etc.). It can,
|
|
39
|
+
however, be soft linked to a different location.
|
|
40
|
+
- The simulator executable(s) must be in your PATH.
|
|
41
|
+
"""))
|
|
42
|
+
|
|
43
|
+
argparser.add_argument(
|
|
44
|
+
"--sim",
|
|
45
|
+
type=str,
|
|
46
|
+
default="verilator",
|
|
47
|
+
help="Name of the RTL simulator to use or path to an executable.")
|
|
48
|
+
argparser.add_argument("--rundir",
|
|
49
|
+
default="run",
|
|
50
|
+
help="Directory in which simulation should be run.")
|
|
51
|
+
argparser.add_argument(
|
|
52
|
+
"--top",
|
|
53
|
+
default="ESI_Cosim_Top",
|
|
54
|
+
help="Name of the 'top' module to use in the simulation.")
|
|
55
|
+
argparser.add_argument("--no-compile",
|
|
56
|
+
action="store_true",
|
|
57
|
+
help="Do not run the compile.")
|
|
58
|
+
argparser.add_argument("--debug",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Enable debug output.")
|
|
61
|
+
argparser.add_argument("--gui",
|
|
62
|
+
action="store_true",
|
|
63
|
+
help="Run the simulator in GUI mode (if supported).")
|
|
64
|
+
argparser.add_argument("--source",
|
|
65
|
+
help="Directories containing the source files.",
|
|
66
|
+
default="hw")
|
|
67
|
+
|
|
68
|
+
argparser.add_argument("inner_cmd",
|
|
69
|
+
nargs=argparse.REMAINDER,
|
|
70
|
+
help="Command to run in the simulation environment.")
|
|
71
|
+
|
|
72
|
+
argparser.add_argument(
|
|
73
|
+
"--server-only",
|
|
74
|
+
action="store_true",
|
|
75
|
+
help="Only run the cosim server, and do not run any inner command.")
|
|
76
|
+
|
|
77
|
+
if len(args) <= 1:
|
|
78
|
+
argparser.print_help()
|
|
79
|
+
return
|
|
80
|
+
args = argparser.parse_args(args[1:])
|
|
81
|
+
|
|
82
|
+
sources = SourceFiles(args.top)
|
|
83
|
+
sources.add_dir(Path(args.source))
|
|
84
|
+
|
|
85
|
+
if args.sim == "verilator":
|
|
86
|
+
sim = Verilator(sources, Path(args.rundir), args.debug)
|
|
87
|
+
elif args.sim == "questa":
|
|
88
|
+
sim = Questa(sources, Path(args.rundir), args.debug)
|
|
89
|
+
else:
|
|
90
|
+
print("Unknown simulator: " + args.sim)
|
|
91
|
+
print("Supported simulators: ")
|
|
92
|
+
print(" - verilator")
|
|
93
|
+
print(" - questa")
|
|
94
|
+
return 1
|
|
95
|
+
|
|
96
|
+
if not args.no_compile:
|
|
97
|
+
rc = sim.compile()
|
|
98
|
+
if rc != 0:
|
|
99
|
+
return rc
|
|
100
|
+
return sim.run(args.inner_cmd[1:], gui=args.gui, server_only=args.server_only)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == '__main__':
|
|
104
|
+
sys.exit(__main__(sys.argv))
|
|
Binary file
|
esiaccel/esiquery.exe
ADDED
|
Binary file
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
#include "esi/Context.h"
|
|
25
25
|
#include "esi/Design.h"
|
|
26
|
+
#include "esi/Engines.h"
|
|
26
27
|
#include "esi/Manifest.h"
|
|
27
28
|
#include "esi/Ports.h"
|
|
28
29
|
#include "esi/Services.h"
|
|
@@ -80,14 +81,10 @@ public:
|
|
|
80
81
|
AcceleratorConnection(Context &ctxt);
|
|
81
82
|
virtual ~AcceleratorConnection();
|
|
82
83
|
Context &getCtxt() const { return ctxt; }
|
|
84
|
+
Logger &getLogger() const { return ctxt.getLogger(); }
|
|
83
85
|
|
|
84
86
|
/// Disconnect from the accelerator cleanly.
|
|
85
|
-
void disconnect();
|
|
86
|
-
|
|
87
|
-
/// Request the host side channel ports for a particular instance (identified
|
|
88
|
-
/// by the AppID path). For convenience, provide the bundle type.
|
|
89
|
-
virtual std::map<std::string, ChannelPort &>
|
|
90
|
-
requestChannelsFor(AppIDPath, const BundleType *) = 0;
|
|
87
|
+
virtual void disconnect();
|
|
91
88
|
|
|
92
89
|
/// Return a pointer to the accelerator 'service' thread (or threads). If the
|
|
93
90
|
/// thread(s) are not running, they will be started when this method is
|
|
@@ -118,7 +115,30 @@ public:
|
|
|
118
115
|
/// accelerator to this connection. Returns a raw pointer to the object.
|
|
119
116
|
Accelerator *takeOwnership(std::unique_ptr<Accelerator> accel);
|
|
120
117
|
|
|
118
|
+
/// Create a new engine for channel communication with the accelerator. The
|
|
119
|
+
/// default is to call the global `createEngine` to get an engine which has
|
|
120
|
+
/// registered itself. Individual accelerator connection backends can override
|
|
121
|
+
/// this to customize behavior.
|
|
122
|
+
virtual void createEngine(const std::string &engineTypeName, AppIDPath idPath,
|
|
123
|
+
const ServiceImplDetails &details,
|
|
124
|
+
const HWClientDetails &clients);
|
|
125
|
+
virtual const BundleEngineMap &getEngineMapFor(AppIDPath id) {
|
|
126
|
+
return clientEngines[id];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Accelerator &getAccelerator() {
|
|
130
|
+
if (!ownedAccelerator)
|
|
131
|
+
throw std::runtime_error(
|
|
132
|
+
"AcceleratorConnection does not own an accelerator");
|
|
133
|
+
return *ownedAccelerator;
|
|
134
|
+
}
|
|
135
|
+
|
|
121
136
|
protected:
|
|
137
|
+
/// If `createEngine` is overridden, this method should be called to register
|
|
138
|
+
/// the engine and all of the channels it services.
|
|
139
|
+
void registerEngine(AppIDPath idPath, std::unique_ptr<Engine> engine,
|
|
140
|
+
const HWClientDetails &clients);
|
|
141
|
+
|
|
122
142
|
/// Called by `getServiceImpl` exclusively. It wraps the pointer returned by
|
|
123
143
|
/// this in a unique_ptr and caches it. Separate this from the
|
|
124
144
|
/// wrapping/caching since wrapping/caching is an implementation detail.
|
|
@@ -127,6 +147,11 @@ protected:
|
|
|
127
147
|
const ServiceImplDetails &details,
|
|
128
148
|
const HWClientDetails &clients) = 0;
|
|
129
149
|
|
|
150
|
+
/// Collection of owned engines.
|
|
151
|
+
std::map<AppIDPath, std::unique_ptr<Engine>> ownedEngines;
|
|
152
|
+
/// Mapping of clients to their servicing engines.
|
|
153
|
+
std::map<AppIDPath, BundleEngineMap> clientEngines;
|
|
154
|
+
|
|
130
155
|
private:
|
|
131
156
|
/// ESI accelerator context.
|
|
132
157
|
Context &ctxt;
|
|
@@ -138,9 +163,8 @@ private:
|
|
|
138
163
|
|
|
139
164
|
std::unique_ptr<AcceleratorServiceThread> serviceThread;
|
|
140
165
|
|
|
141
|
-
///
|
|
142
|
-
|
|
143
|
-
std::vector<std::unique_ptr<Accelerator>> ownedAccelerators;
|
|
166
|
+
/// Accelerator object owned by this connection.
|
|
167
|
+
std::unique_ptr<Accelerator> ownedAccelerator;
|
|
144
168
|
};
|
|
145
169
|
|
|
146
170
|
namespace registry {
|