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"
22 VerilatorBinNotFound = (
23 "Cannot find verilator_bin. Set VERILATOR_PATH to an absolute path "
24 "or ensure verilator_bin is in PATH.")
25 VerilatorRootNotFound = (
26 "Cannot find VERILATOR_ROOT. Set the VERILATOR_ROOT environment "
27 "variable or ensure verilator_bin is in PATH.")
28 VerilatorPathInvalid = (
29 "VERILATOR_PATH does not point to a valid verilator_bin executable.")
30 VerilatorRootInvalid = (
31 "VERILATOR_ROOT does not point to a Verilator root containing "
32 "include/verilated.h.")
39 save_waveform: bool =
False,
40 run_stdout_callback: Optional[Callable[[str],
None]] =
None,
41 run_stderr_callback: Optional[Callable[[str],
None]] =
None,
42 compile_stdout_callback: Optional[Callable[[str],
None]] =
None,
43 compile_stderr_callback: Optional[Callable[[str],
None]] =
None,
44 make_default_logs: bool =
True,
45 macro_definitions: Optional[Dict[str, str]] =
None,
51 save_waveform=save_waveform,
52 run_stdout_callback=run_stdout_callback,
53 run_stderr_callback=run_stderr_callback,
54 compile_stdout_callback=compile_stdout_callback,
55 compile_stderr_callback=compile_stderr_callback,
56 make_default_logs=make_default_logs,
57 macro_definitions=macro_definitions,
62 vpath = Verilator._find_verilator_bin()
69 """Locate the ``verilator_bin`` executable.
71 When ``VERILATOR_PATH`` is set it must point to a valid executable;
72 otherwise a ``RuntimeError`` is raised. Without it, ``verilator_bin`` is
73 looked up on ``PATH``. Returns ``None`` when nothing is found."""
75 def check_path(path: Path | str |
None) -> Optional[Path]:
76 if isinstance(path, str):
78 if path
is not None and path.exists()
and path.is_file():
82 if "VERILATOR_PATH" in os.environ:
83 vpath = Path(os.environ[
"VERILATOR_PATH"])
84 if vpath.stem ==
"verilator":
85 vpath = vpath.parent /
"verilator_bin"
86 checked = check_path(vpath)
90 return check_path(shutil.which(
"verilator_bin"))
94 """Locate the Verilator root containing ``include/verilated.h``.
96 When ``VERILATOR_ROOT`` is set it must contain ``include/verilated.h``;
97 otherwise a ``RuntimeError`` is raised. Without it, the packaged root
98 (``$PREFIX/share/verilator``) is derived from the ``verilator_bin``
99 location. Returns ``None`` when nothing is found."""
100 if "VERILATOR_ROOT" in os.environ:
101 root = Path(os.environ[
"VERILATOR_ROOT"])
102 if (root /
"include" /
"verilated.h").exists():
106 verilator_bin = Verilator._find_verilator_bin()
107 if verilator_bin
is None:
112 pkg_root = verilator_bin.parent.parent /
"share" /
"verilator"
113 if (pkg_root /
"include" /
"verilated.h").exists():
120 """True when both cmake and ninja are available on PATH."""
121 return shutil.which(
"cmake")
is not None and \
122 shutil.which(
"ninja")
is not None
125 """Return the compile steps for the full compile flow.
127 When cmake and ninja are available the returned list contains four
129 1. ``verilator_bin`` – generates C++ from RTL.
130 2. Python callback – generates the CMakeLists.txt from the depfile.
131 3. ``cmake`` – configures the C++ build (Ninja generator).
132 4. ``ninja`` – builds the simulation executable.
134 Otherwise falls back to two commands:
135 1. ``verilator_bin --exe`` – generates C++ and a Makefile.
136 2. ``make`` – builds via the generated Makefile.
139 if verilator_bin
is None:
142 if verilator_root
is None:
144 os.environ[
"VERILATOR_ROOT"] = str(verilator_root)
146 verilator_cmd: List[str] = [
153 f
"+define+{k}={v}" if v
is not None else f
"+define+{k}"
174 "--trace-underscore",
178 verilator_cmd += [str(p)
for p
in self.
sources.rtl_sources]
179 build_dir = str(Path.cwd() /
"obj_dir" /
"cmake_build")
185 "cmake",
"-G",
"Ninja",
"-DCMAKE_BUILD_TYPE=Release",
"-S", build_dir,
191 vcpkg_root = os.environ.get(
"VCPKG_ROOT")
or os.environ.get(
192 "VCPKG_INSTALLATION_ROOT")
195 vcpkg_root) /
"scripts" /
"buildsystems" /
"vcpkg.cmake"
196 if toolchain.exists():
197 cmake_cmd.append(f
"-DCMAKE_TOOLCHAIN_FILE={toolchain}")
198 ninja_cmd = [
"ninja",
"-C", build_dir]
206 verilator_cmd += [
"--exe", str(Verilator.DefaultDriver)]
207 cflags = [
"-DTOP_MODULE=" + self.
sources.top]
209 cflags.append(
"-DTRACE")
210 verilator_cmd += [
"-CFLAGS",
" ".join(cflags)]
212 dpi_so_paths = self.
sources.dpi_so_paths()
215 " ".join([
"-l" + so
for so
in self.
sources.dpi_so]) +
" " +
216 " ".join([
"-L" + so.parent.as_posix()
for so
in dpi_so_paths]),
218 verilator_cmd += [str(p)
for p
in self.
sources.rtl_sources]
220 make_cmd = [
"make",
"-C",
"obj_dir",
"-f", f
"V{top}.mk",
"-j"]
221 return [verilator_cmd, make_cmd]
224 return obj_dir / f
"V{self.sources.top}__ver.d"
227 depfile_contents = depfile.read_text().replace(
"\\\n",
" ")
228 separator = re.search(
r":\s", depfile_contents)
229 if separator
is None:
230 raise RuntimeError(f
"Malformed Verilator depfile: {depfile}")
231 return [(Path.cwd() / path).
resolve()
232 for path
in depfile_contents[:separator.start()].split()]
235 obj_dir = Path.cwd() /
"obj_dir"
238 generated_sources = [
239 path
for path
in generated_targets
if path.suffix ==
".cpp"
242 (path
for path
in generated_targets
if path.name.endswith(
"__pch.h")),
244 self.
_write_cmake(obj_dir, generated_sources, pch_header)
248 generated_sources = [
250 if path.suffix ==
".cpp"
252 if not generated_sources:
254 f
"No generated C++ sources found in depfile: {depfile}")
255 return generated_sources
259 generated_sources: List[Path],
260 pch_header: Optional[Path] =
None) -> Path:
261 """Write a CMakeLists.txt for building the verilated simulation.
263 Returns the path to the CMake build directory."""
266 if verilator_root
is None:
268 include_dir = verilator_root /
"include"
269 exe_name =
"V" + self.
sources.top
271 if os.name ==
"nt" and all(source.exists()
for source
in generated_sources):
277 short_source_dir = obj_dir /
"cmake_src"
278 if short_source_dir.exists():
279 shutil.rmtree(short_source_dir)
280 short_source_dir.mkdir(parents=
True)
281 shortened_sources = []
282 for index, source
in enumerate(generated_sources):
283 shortened_source = short_source_dir / f
"vsrc_{index}.cpp"
284 shutil.copy2(source, shortened_source)
285 shortened_sources.append(shortened_source)
286 generated_sources = shortened_sources
289 include_dir /
"verilated.cpp",
290 include_dir /
"verilated_threads.cpp",
294 runtime_sources.append(include_dir /
"verilated_dpi.cpp")
296 runtime_sources.append(include_dir /
"verilated_fst_c.cpp")
298 random_cpp = include_dir /
"verilated_random.cpp"
299 if random_cpp.exists():
300 runtime_sources.append(random_cpp)
302 generated_src =
"\n ".join(
303 source.as_posix()
for source
in generated_sources)
304 rt_src =
"\n ".join(s.as_posix()
for s
in runtime_sources)
305 driver = Path(Verilator.DefaultDriver).as_posix()
306 inc = include_dir.as_posix()
307 vltstd = (include_dir /
"vltstd").as_posix()
309 defs = [f
"TOP_MODULE={self.sources.top}"]
312 defs_str =
"\n ".join(defs)
319 dpi_paths = self.
sources.dpi_link_paths()
320 dpi_link =
"\n ".join(p.as_posix()
for p
in dpi_paths)
323 if pch_header
is not None:
324 runtime_and_driver =
"\n ".join(
325 [source.as_posix()
for source
in runtime_sources] + [driver])
327target_precompile_headers({exe_name} PRIVATE
328 {pch_header.as_posix()}
331set_source_files_properties(
333 PROPERTIES SKIP_PRECOMPILE_HEADERS ON
339 zlib_find =
"find_package(ZLIB REQUIRED)"
340 zlib_link =
"ZLIB::ZLIB"
346cmake_minimum_required(VERSION 3.20)
347project({exe_name} CXX)
349set(CMAKE_CXX_STANDARD 17)
350set(CMAKE_CXX_STANDARD_REQUIRED ON)
353 add_compile_options(/EHsc /bigobj)
356find_package(Threads REQUIRED)
358add_executable({exe_name}
364target_include_directories({exe_name} PRIVATE
367 ${{CMAKE_CURRENT_SOURCE_DIR}}/..
370target_compile_definitions({exe_name} PRIVATE
375target_link_libraries({exe_name} PRIVATE
381 build_dir = obj_dir /
"cmake_build"
382 build_dir.mkdir(parents=
True, exist_ok=
True)
383 (build_dir /
"CMakeLists.txt").write_text(content)
388 """Verilator's C++ driver uses ``VerilatedFstC`` — FST format."""
393 raise RuntimeError(
"Verilator does not support GUI mode.")
394 exe_name =
"V" + self.
sources.top
398 exe = Path.cwd() /
"obj_dir" /
"cmake_build" / exe_name
400 exe = Path.cwd() /
"obj_dir" / exe_name
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
str waveform_extension(self)
Optional[Path] _find_verilator_root()
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)
_write_cmake_from_depfile
run_command(self, bool gui)
List[Simulator.CompileStep] compile_commands(self)
Optional[Path] _find_verilator_bin()
__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)