18from pathlib
import Path
26from typing
import Dict, List
28_thisdir = Path(__file__).parent
29CosimCollateralDir = _thisdir.parent /
"cosim"
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))
37 return True if result == 0
else False
44 self.user: List[Path] = []
46 self.dpi_so: List[str] = [
"EsiCosimDpiServer"]
48 self.dpi_sv: List[Path] = [
49 CosimCollateralDir /
"Cosim_DpiPkg.sv",
50 CosimCollateralDir /
"Cosim_Endpoint.sv",
51 CosimCollateralDir /
"Cosim_Manifest.sv",
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"):
65 """Return a list of all the DPI shared object files."""
67 def find_so(name: str) -> Path:
68 for path
in Simulator.get_env().get(
"LD_LIBRARY_PATH",
"").split(
":"):
70 so = Path(path) / f
"{name}.dll"
72 so = Path(path) / f
"lib{name}.so"
75 raise FileNotFoundError(f
"Could not find {name}.so in LD_LIBRARY_PATH")
77 return [find_so(name)
for name
in self.dpi_so]
81 """Return a list of all the RTL source files."""
82 return self.dpi_sv + self.user
92 def __init__(self, sources: SourceFiles, run_dir: Path, debug: bool):
99 """Get the environment variables to locate shared objects."""
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")
109 """Compile the sources. Returns the exit code of the simulation compiler."""
110 assert False,
"Must be implemented by subclass"
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:
118 stderr.write(
" ".join(cmd) +
"\n")
119 cp = subprocess.run(cmd,
120 env=Simulator.get_env(),
123 stdout.write(cp.stdout)
124 stderr.write(cp.stderr)
125 if cp.returncode != 0:
126 print(
"====== Compilation failure:")
135 """Return the command to run the simulation."""
136 assert False,
"Must be implemented by subclass"
141 server_only: bool =
False) -> int:
142 """Start the simulation then run the command specified. Kill the simulation
143 when the command exits."""
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")
156 portFileName = self.
run_dir /
"cosim.cfg"
157 if os.path.exists(portFileName):
158 os.remove(portFileName)
161 simEnv = Simulator.get_env()
163 simEnv[
"COSIM_DEBUG_FILE"] =
"cosim_debug.log"
164 if "DEBUG_PERIOD" not in simEnv:
166 simEnv[
"DEBUG_PERIOD"] =
"1"
172 preexec_fn=os.setsid)
178 while (
not os.path.exists(portFileName))
and \
179 simProc.poll()
is None:
182 if checkCount > 200
and not gui:
183 raise Exception(f
"Cosim never wrote cfg file: {portFileName}")
186 portFile = open(portFileName,
"r")
187 for line
in portFile.readlines():
188 m = re.match(
"port: (\\d+)", line)
190 port = int(m.group(1))
198 raise Exception(f
"Cosim RPC port ({port}) never opened")
199 if simProc.poll()
is not None:
200 raise Exception(
"Simulation exited early")
206 f
"Running in server-only mode on port {port} - Press anything to kill the server..."
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
219 os.killpg(os.getpgid(simProc.pid), signal.SIGINT)
222 simProc.wait(timeout=1.0)
223 except subprocess.TimeoutExpired:
229 """Run and compile funcs for Verilator."""
231 DefaultDriver = CosimCollateralDir /
"driver.cpp"
233 def __init__(self, sources: SourceFiles, run_dir: Path, debug: bool):
234 super().
__init__(sources, run_dir, debug)
237 if "VERILATOR_PATH" in os.environ:
238 self.
verilator = os.environ[
"VERILATOR_PATH"]
253 str(Verilator.DefaultDriver),
256 "-DTOP_MODULE=" + self.
sources.top,
260 "--trace",
"--trace-params",
"--trace-structs",
"--trace-underscore"
262 cflags.append(
"-DTRACE")
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]
272 raise RuntimeError(
"Verilator does not support GUI mode.")
273 exe = Path.cwd() /
"obj_dir" / (
"V" + self.
sources.top)
278 """Run and compile funcs for Questasim."""
280 DefaultDriver = CosimCollateralDir /
"driver.sv"
287 "onerror { quit -f -code 1 }",
289 sources = self.
sources.rtl_sources
290 sources.append(Questa.DefaultDriver)
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")
298 with open(
"compile.do",
"w")
as f:
304 [
"vsim",
"-batch",
"-do",
"compile.do"],
324 for lib
in self.
sources.dpi_so_paths():
325 svLib = os.path.splitext(lib)[0]
326 cmd.append(
"-sv_lib")
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."""
338 workDir = self.
run_dir /
"work"
339 if not workDir.exists():
340 os.symlink(Path(os.getcwd()) /
"work", workDir)
343 return super().
run(inner_command, gui, server_only=server_only)
347 argparser = argparse.ArgumentParser(
348 description=
"Wrap a 'inner_cmd' in an ESI cosimulation environment.",
349 formatter_class=argparse.RawDescriptionHelpFormatter,
350 epilog=textwrap.dedent(
"""
352 - For Verilator, libEsiCosimDpiServer.so must be in the dynamic
353 library runtime search path (LD_LIBRARY_PATH) and link time path
354 (LIBRARY_PATH). If it is installed to a standard location (e.g.
355 /usr/lib), this should be handled automatically.
356 - This script needs to sit in the same directory as the ESI support
357 SystemVerilog (e.g. Cosim_DpiPkg.sv, Cosim_MMIO.sv, etc.). It can,
358 however, be soft linked to a different location.
359 - The simulator executable(s) must be in your PATH.
362 argparser.add_argument(
366 help=
"Name of the RTL simulator to use or path to an executable.")
367 argparser.add_argument(
"--rundir",
369 help=
"Directory in which simulation should be run.")
370 argparser.add_argument(
372 default=
"ESI_Cosim_Top",
373 help=
"Name of the 'top' module to use in the simulation.")
374 argparser.add_argument(
"--no-compile",
376 help=
"Do not run the compile.")
377 argparser.add_argument(
"--debug",
379 help=
"Enable debug output.")
380 argparser.add_argument(
"--gui",
382 help=
"Run the simulator in GUI mode (if supported).")
383 argparser.add_argument(
"--source",
384 help=
"Directories containing the source files.",
387 argparser.add_argument(
"inner_cmd",
388 nargs=argparse.REMAINDER,
389 help=
"Command to run in the simulation environment.")
391 argparser.add_argument(
394 help=
"Only run the cosim server, and do not run any inner command.")
397 argparser.print_help()
399 args = argparser.parse_args(args[1:])
402 sources.add_dir(Path(args.source))
404 if args.sim ==
"verilator":
405 sim =
Verilator(sources, Path(args.rundir), args.debug)
406 elif args.sim ==
"questa":
407 sim =
Questa(sources, Path(args.rundir), args.debug)
409 print(
"Unknown simulator: " + args.sim)
410 print(
"Supported simulators: ")
411 print(
" - verilator")
415 if not args.no_compile:
419 return sim.run(args.inner_cmd[1:], gui=args.gui, server_only=args.server_only)
422if __name__ ==
'__main__':
static void print(TypedAttr val, llvm::raw_ostream &os)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
List[List[str]] compile_commands(self)
int run(self, str inner_command, bool gui=False, bool server_only=False)
List[str] internal_compile_commands(self)
List[str] run_command(self, bool gui)
int run(self, str inner_command, bool gui=False, bool server_only=False)
List[str] run_command(self, bool gui)
__init__(self, SourceFiles sources, Path run_dir, bool debug)
List[List[str]] compile_commands(self)
List[Path] rtl_sources(self)
List[Path] dpi_so_paths(self)
None __init__(self, str top)
run_command(self, bool gui)
List[List[str]] compile_commands(self)
__init__(self, SourceFiles sources, Path run_dir, bool debug)