8from pathlib
import Path
9from typing
import List, Optional, Callable, Dict
11from .simulator
import CosimCollateralDir, Simulator, SourceFiles
15 """Run and compile funcs for Verilator.
17 Calls ``verilator_bin`` directly (bypassing the Perl wrapper) to generate
18 C++ from RTL, then builds the simulation executable with CMake + Ninja.
19 Falls back to ``make`` when cmake/ninja are not available."""
21 DefaultDriver = CosimCollateralDir /
"driver.cpp"
28 save_waveform: bool =
False,
29 run_stdout_callback: Optional[Callable[[str],
None]] =
None,
30 run_stderr_callback: Optional[Callable[[str],
None]] =
None,
31 compile_stdout_callback: Optional[Callable[[str],
None]] =
None,
32 compile_stderr_callback: Optional[Callable[[str],
None]] =
None,
33 make_default_logs: bool =
True,
34 macro_definitions: Optional[Dict[str, str]] =
None,
40 save_waveform=save_waveform,
41 run_stdout_callback=run_stdout_callback,
42 run_stderr_callback=run_stderr_callback,
43 compile_stdout_callback=compile_stdout_callback,
44 compile_stderr_callback=compile_stderr_callback,
45 make_default_logs=make_default_logs,
46 macro_definitions=macro_definitions,
49 if "VERILATOR_PATH" in os.environ:
50 vpath = os.environ[
"VERILATOR_PATH"]
53 basename = Path(vpath).stem
54 if basename ==
"verilator":
55 self.
verilator_bin = str(Path(vpath).parent /
"verilator_bin")
61 if verilator_bin_path:
62 return str(Path(verilator_bin_path).
resolve())
65 if verilator_bin_path.is_file():
66 return str(verilator_bin_path.resolve())
72 "Cannot find verilator_bin. Set VERILATOR_PATH to an absolute path "
73 "or ensure verilator_bin is in PATH.")
76 """Locate VERILATOR_ROOT for runtime includes and sources.
78 Checks the ``VERILATOR_ROOT`` environment variable first, then attempts
79 to derive the root from the location of ``verilator_bin``. Supports both
80 source-tree layouts (``$ROOT/include/verilated.h``) and system package
81 layouts (``$PREFIX/share/verilator/include/verilated.h``)."""
82 if "VERILATOR_ROOT" in os.environ:
83 root = Path(os.environ[
"VERILATOR_ROOT"])
86 if verilator_bin
is None:
89 verilator_bin_path = Path(verilator_bin)
90 if not verilator_bin_path.is_file():
91 resolved = shutil.which(verilator_bin)
94 "Cannot find VERILATOR_ROOT. Set the VERILATOR_ROOT environment "
95 "variable or ensure verilator_bin is in PATH.")
96 verilator_bin_path = Path(resolved)
99 prefix = verilator_bin_path.resolve().parent.parent
101 if (prefix /
"include" /
"verilated.h").exists():
104 pkg_root = prefix /
"share" /
"verilator"
105 if (pkg_root /
"include" /
"verilated.h").exists():
108 "Cannot find VERILATOR_ROOT. Set the VERILATOR_ROOT environment "
109 "variable or ensure verilator_bin is in PATH.")
113 """True when both cmake and ninja are available on PATH."""
114 return shutil.which(
"cmake")
is not None and \
115 shutil.which(
"ninja")
is not None
118 """Return the compile steps for the full compile flow.
120 When cmake and ninja are available the returned list contains four
122 1. ``verilator_bin`` – generates C++ from RTL.
123 2. Python callback – generates the CMakeLists.txt from the depfile.
124 3. ``cmake`` – configures the C++ build (Ninja generator).
125 4. ``ninja`` – builds the simulation executable.
127 Otherwise falls back to two commands:
128 1. ``verilator_bin --exe`` – generates C++ and a Makefile.
129 2. ``make`` – builds via the generated Makefile.
132 os.environ[
"VERILATOR_ROOT"] = str(
135 verilator_cmd: List[str] = [
142 f
"+define+{k}={v}" if v
is not None else f
"+define+{k}"
163 "--trace-underscore",
167 verilator_cmd += [str(p)
for p
in self.
sources.rtl_sources]
168 build_dir = str(Path.cwd() /
"obj_dir" /
"cmake_build")
169 cmake_cmd = [
"cmake",
"-G",
"Ninja",
"-S", build_dir,
"-B", build_dir]
170 ninja_cmd = [
"ninja",
"-C", build_dir]
178 verilator_cmd += [
"--exe", str(Verilator.DefaultDriver)]
179 cflags = [
"-DTOP_MODULE=" + self.
sources.top]
181 cflags.append(
"-DTRACE")
182 verilator_cmd += [
"-CFLAGS",
" ".join(cflags)]
184 dpi_so_paths = self.
sources.dpi_so_paths()
187 " ".join([
"-l" + so
for so
in self.
sources.dpi_so]) +
" " +
188 " ".join([
"-L" + so.parent.as_posix()
for so
in dpi_so_paths]),
190 verilator_cmd += [str(p)
for p
in self.
sources.rtl_sources]
192 make_cmd = [
"make",
"-C",
"obj_dir",
"-f", f
"V{top}.mk",
"-j"]
193 return [verilator_cmd, make_cmd]
196 return obj_dir / f
"V{self.sources.top}__ver.d"
199 depfile_contents = depfile.read_text().replace(
"\\\n",
" ")
200 separator = re.search(
r":\s", depfile_contents)
201 if separator
is None:
202 raise RuntimeError(f
"Malformed Verilator depfile: {depfile}")
203 return [(Path.cwd() / path).
resolve()
204 for path
in depfile_contents[:separator.start()].split()]
207 obj_dir = Path.cwd() /
"obj_dir"
210 generated_sources = [
211 path
for path
in generated_targets
if path.suffix ==
".cpp"
214 (path
for path
in generated_targets
if path.name.endswith(
"__pch.h")),
216 self.
_write_cmake(obj_dir, generated_sources, pch_header)
220 generated_sources = [
222 if path.suffix ==
".cpp"
224 if not generated_sources:
226 f
"No generated C++ sources found in depfile: {depfile}")
227 return generated_sources
231 generated_sources: List[Path],
232 pch_header: Optional[Path] =
None) -> Path:
233 """Write a CMakeLists.txt for building the verilated simulation.
235 Returns the path to the CMake build directory."""
238 include_dir = verilator_root /
"include"
239 exe_name =
"V" + self.
sources.top
242 include_dir /
"verilated.cpp",
243 include_dir /
"verilated_threads.cpp",
246 runtime_sources.append(include_dir /
"verilated_dpi.cpp")
248 runtime_sources.append(include_dir /
"verilated_fst_c.cpp")
250 random_cpp = include_dir /
"verilated_random.cpp"
251 if random_cpp.exists():
252 runtime_sources.append(random_cpp)
254 generated_src =
"\n ".join(str(source)
for source
in generated_sources)
255 rt_src =
"\n ".join(str(s)
for s
in runtime_sources)
256 driver = str(Verilator.DefaultDriver)
257 inc = str(include_dir)
258 vltstd = str(include_dir /
"vltstd")
260 defs = [f
"TOP_MODULE={self.sources.top}"]
263 defs_str =
"\n ".join(defs)
268 dpi_paths = self.
sources.dpi_so_paths()
269 dpi_link =
"\n ".join(str(p)
for p
in dpi_paths)
272 if pch_header
is not None:
273 runtime_and_driver =
"\n ".join(
274 [str(source)
for source
in runtime_sources] + [driver])
276target_precompile_headers({exe_name} PRIVATE
280set_source_files_properties(
282 PROPERTIES SKIP_PRECOMPILE_HEADERS ON
287cmake_minimum_required(VERSION 3.20)
288project({exe_name} CXX)
290add_executable({exe_name}
296target_include_directories({exe_name} PRIVATE
299 ${{CMAKE_CURRENT_SOURCE_DIR}}/..
302target_compile_definitions({exe_name} PRIVATE
307find_package(Threads REQUIRED)
308find_package(ZLIB REQUIRED)
309target_link_libraries({exe_name} PRIVATE
315 build_dir = obj_dir /
"cmake_build"
316 build_dir.mkdir(parents=
True, exist_ok=
True)
317 (build_dir /
"CMakeLists.txt").write_text(content)
322 """Verilator's C++ driver uses ``VerilatedFstC`` — FST format."""
327 raise RuntimeError(
"Verilator does not support GUI mode.")
328 exe_name =
"V" + self.
sources.top
330 exe = Path.cwd() /
"obj_dir" /
"cmake_build" / exe_name
332 exe = Path.cwd() /
"obj_dir" / exe_name
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
str waveform_extension(self)
List[Path] _generated_targets(self, Path depfile)
int _write_cmake_from_depfile(self)
Path _write_cmake(self, Path obj_dir, List[Path] generated_sources, Optional[Path] pch_header=None)
Path _depfile_path(self, Path obj_dir)
List[Path] _generated_cpp_sources(self, Path depfile)
str _find_verilator_bin(self, bool required=True)
_write_cmake_from_depfile
run_command(self, bool gui)
List[Simulator.CompileStep] compile_commands(self)
Path _find_verilator_root(self, Optional[str] verilator_bin=None)
__init__(self, SourceFiles sources, Path run_dir, bool debug, bool save_waveform=False, Optional[Callable[[str], None]] run_stdout_callback=None, Optional[Callable[[str], None]] run_stderr_callback=None, Optional[Callable[[str], None]] compile_stdout_callback=None, Optional[Callable[[str], None]] compile_stderr_callback=None, bool make_default_logs=True, Optional[Dict[str, str]] macro_definitions=None)