CIRCT 23.0.0git
Loading...
Searching...
No Matches
verilator.py
Go to the documentation of this file.
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
5import os
6import shutil
7from pathlib import Path
8from typing import List, Optional, Callable, Dict
9
10from .simulator import CosimCollateralDir, Simulator, SourceFiles
11
12
14 """Run and compile funcs for Verilator.
15
16 Calls ``verilator_bin`` directly (bypassing the Perl wrapper) to generate
17 C++ from RTL, then builds the simulation executable with CMake + Ninja.
18 Falls back to ``make`` when cmake/ninja are not available."""
19
20 DefaultDriver = CosimCollateralDir / "driver.cpp"
21
23 self,
24 sources: SourceFiles,
25 run_dir: Path,
26 debug: bool,
27 save_waveform: bool = False,
28 run_stdout_callback: Optional[Callable[[str], None]] = None,
29 run_stderr_callback: Optional[Callable[[str], None]] = None,
30 compile_stdout_callback: Optional[Callable[[str], None]] = None,
31 compile_stderr_callback: Optional[Callable[[str], None]] = None,
32 make_default_logs: bool = True,
33 macro_definitions: Optional[Dict[str, str]] = None,
34 ):
35 super().__init__(
36 sources=sources,
37 run_dir=run_dir,
38 debug=debug,
39 save_waveform=save_waveform,
40 run_stdout_callback=run_stdout_callback,
41 run_stderr_callback=run_stderr_callback,
42 compile_stdout_callback=compile_stdout_callback,
43 compile_stderr_callback=compile_stderr_callback,
44 make_default_logs=make_default_logs,
45 macro_definitions=macro_definitions,
46 )
47 self.verilator_bin = "verilator_bin"
48 if "VERILATOR_PATH" in os.environ:
49 vpath = os.environ["VERILATOR_PATH"]
50 # Backwards compatibility: if the env var points to the Perl wrapper,
51 # redirect to verilator_bin.
52 basename = Path(vpath).stem
53 if basename == "verilator":
54 self.verilator_bin = str(Path(vpath).parent / "verilator_bin")
55 else:
56 self.verilator_bin = vpath
57
58 def _find_verilator_root(self) -> Path:
59 """Locate VERILATOR_ROOT for runtime includes and sources.
60
61 Checks the ``VERILATOR_ROOT`` environment variable first, then attempts
62 to derive the root from the location of ``verilator_bin``. Supports both
63 source-tree layouts (``$ROOT/include/verilated.h``) and system package
64 layouts (``$PREFIX/share/verilator/include/verilated.h``)."""
65 if "VERILATOR_ROOT" in os.environ:
66 root = Path(os.environ["VERILATOR_ROOT"])
67 if root.is_dir():
68 return root
69 # verilator_bin is typically in $PREFIX/bin/
70 verilator_bin_path = shutil.which(self.verilator_bin)
71 if verilator_bin_path:
72 prefix = Path(verilator_bin_path).resolve().parent.parent
73 # Source-tree layout: $VERILATOR_ROOT/bin/verilator_bin
74 if (prefix / "include" / "verilated.h").exists():
75 return prefix
76 # System package layout: $PREFIX/share/verilator/include/verilated.h
77 pkg_root = prefix / "share" / "verilator"
78 if (pkg_root / "include" / "verilated.h").exists():
79 return pkg_root
80 raise RuntimeError(
81 "Cannot find VERILATOR_ROOT. Set the VERILATOR_ROOT environment "
82 "variable or ensure verilator_bin is in PATH.")
83
84 @property
85 def _use_cmake(self) -> bool:
86 """True when both cmake and ninja are available on PATH."""
87 return shutil.which("cmake") is not None and \
88 shutil.which("ninja") is not None
89
90 def compile_commands(self) -> List[List[str]]:
91 """Return the commands for the full compile flow.
92
93 When cmake and ninja are available the returned list contains three
94 commands run sequentially:
95 1. ``verilator_bin`` – generates C++ from RTL.
96 2. ``cmake`` – configures the C++ build (Ninja generator).
97 3. ``ninja`` – builds the simulation executable.
98
99 Otherwise falls back to two commands:
100 1. ``verilator_bin --exe`` – generates C++ and a Makefile.
101 2. ``make`` – builds via the generated Makefile.
102 """
103 cmd: List[str] = [
104 self.verilator_bin,
105 "--cc",
106 ]
107
108 if self.macro_definitions:
109 cmd += [
110 f"+define+{k}={v}" if v is not None else f"+define+{k}"
111 for k, v in self.macro_definitions.items()
112 ]
113
114 cmd += [
115 "--top-module",
116 self.sources.top,
117 "-DSIMULATION",
118 "-Wno-TIMESCALEMOD",
119 "-Wno-fatal",
120 "-sv",
121 "-j",
122 "0",
123 "--output-split",
124 "--autoflush",
125 "--assert",
126 ]
127 if self.debug:
128 cmd += [
129 "--trace-fst", "--trace-params", "--trace-structs",
130 "--trace-underscore"
131 ]
132
133 if self._use_cmake:
134 cmd += [str(p) for p in self.sources.rtl_sources]
135 build_dir = str(Path.cwd() / "obj_dir" / "cmake_build")
136 cmake_cmd = ["cmake", "-G", "Ninja", "-S", build_dir, "-B", build_dir]
137 ninja_cmd = ["ninja", "-C", build_dir]
138 return [cmd, cmake_cmd, ninja_cmd]
139
140 # -- make fallback --
141 # Let verilator generate a Makefile with --exe so it includes the
142 # driver, CFLAGS, and LDFLAGS directly.
143 cmd += ["--exe", str(Verilator.DefaultDriver)]
144 cflags = ["-DTOP_MODULE=" + self.sources.top]
145 if self.debug:
146 cflags.append("-DTRACE")
147 cmd += ["-CFLAGS", " ".join(cflags)]
148 if self.sources.dpi_so:
149 cmd += ["-LDFLAGS", " ".join(["-l" + so for so in self.sources.dpi_so])]
150 cmd += [str(p) for p in self.sources.rtl_sources]
151 top = self.sources.top
152 make_cmd = ["make", "-C", "obj_dir", "-f", f"V{top}.mk", "-j"]
153 return [cmd, make_cmd]
154
155 def _write_cmake(self, obj_dir: Path) -> Path:
156 """Write a CMakeLists.txt for building the verilated simulation.
157
158 Returns the path to the CMake build directory."""
159 verilator_root = self._find_verilator_root()
160 include_dir = verilator_root / "include"
161 exe_name = "V" + self.sources.top
162
163 runtime_sources = [
164 include_dir / "verilated.cpp",
165 include_dir / "verilated_threads.cpp",
166 ]
167 if self.sources.dpi_so:
168 runtime_sources.append(include_dir / "verilated_dpi.cpp")
169 if self.debug:
170 runtime_sources.append(include_dir / "verilated_fst_c.cpp")
171
172 rt_src = "\n ".join(str(s) for s in runtime_sources)
173 driver = str(Verilator.DefaultDriver)
174 inc = str(include_dir)
175 vltstd = str(include_dir / "vltstd")
176
177 defs = [f"TOP_MODULE={self.sources.top}"]
178 if self.debug:
179 defs.append("TRACE")
180 defs_str = "\n ".join(defs)
181
182 # Link DPI shared objects by full path.
183 dpi_link = ""
184 if self.sources.dpi_so:
185 dpi_paths = self.sources.dpi_so_paths()
186 dpi_link = "\n ".join(str(p) for p in dpi_paths)
187
188 content = f"""\
189cmake_minimum_required(VERSION 3.20)
190project({exe_name} CXX)
191
192file(GLOB GENERATED_SOURCES "${{CMAKE_CURRENT_SOURCE_DIR}}/../*.cpp")
193
194add_executable({exe_name}
195 ${{GENERATED_SOURCES}}
196 {rt_src}
197 {driver}
198)
199
200target_include_directories({exe_name} PRIVATE
201 {inc}
202 {vltstd}
203 ${{CMAKE_CURRENT_SOURCE_DIR}}/..
204)
205
206target_compile_definitions({exe_name} PRIVATE
207 {defs_str}
208)
209
210find_package(Threads REQUIRED)
211target_link_libraries({exe_name} PRIVATE
212 Threads::Threads
213 {dpi_link}
214)
215"""
216 build_dir = obj_dir / "cmake_build"
217 build_dir.mkdir(parents=True, exist_ok=True)
218 (build_dir / "CMakeLists.txt").write_text(content)
219 return build_dir
220
221 def compile(self) -> int:
222 """Set VERILATOR_ROOT, write the CMakeLists.txt (if using cmake), then
223 delegate to the base class which runs all commands from
224 :meth:`compile_commands`."""
225 verilator_root = self._find_verilator_root()
226 os.environ["VERILATOR_ROOT"] = str(verilator_root)
227 if self._use_cmake:
228 self._write_cmake(Path.cwd() / "obj_dir")
229 return super().compile()
230
231 @property
232 def waveform_extension(self) -> str:
233 """Verilator's C++ driver uses ``VerilatedFstC`` — FST format."""
234 return ".fst"
235
236 def run_command(self, gui: bool):
237 if gui:
238 raise RuntimeError("Verilator does not support GUI mode.")
239 exe_name = "V" + self.sources.top
240 if self._use_cmake:
241 exe = Path.cwd() / "obj_dir" / "cmake_build" / exe_name
242 else:
243 exe = Path.cwd() / "obj_dir" / exe_name
244 return [str(exe)]
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
str waveform_extension(self)
Definition verilator.py:232
bool _use_cmake(self)
Definition verilator.py:85
Path _write_cmake(self, Path obj_dir)
Definition verilator.py:155
run_command(self, bool gui)
Definition verilator.py:236
Path _find_verilator_root(self)
Definition verilator.py:58
List[List[str]] compile_commands(self)
Definition verilator.py:90
__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)
Definition verilator.py:34