1"""Unit tests for the Verilator cosim backend.
3These tests exercise the command-generation and CMake-template logic of the
4Verilator class *without* requiring the compiled ``esiCppAccel`` C++
5extension. We achieve this by inserting a ``MagicMock`` for the extension
6module before the real package is imported.
12from pathlib
import Path
13from unittest
import mock
14from unittest.mock
import MagicMock
22_accel_mock = MagicMock()
23sys.modules[
"esiaccel.esiCppAccel"] = _accel_mock
26from esiaccel.cosim.verilator
import Verilator
27from esiaccel.cosim.simulator
import SourceFiles
35 """Create a Verilator instance with minimal setup."""
36 sources = SourceFiles(top)
37 if dpi_so
is not None:
38 sources.dpi_so = dpi_so
43 make_default_logs=
False,
44 macro_definitions=macros,
52 cmds = v.compile_commands()
53 assert cmds[0][0] ==
"verilator_bin"
57 cmds = v.compile_commands()
61 assert cmds[1][0] ==
"cmake"
62 assert "-G" in cmds[1]
and "Ninja" in cmds[1]
63 assert cmds[2][0] ==
"ninja"
66 """When using cmake, --exe and --build should not appear."""
69 pytest.skip(
"cmake+ninja not available")
70 cmd = v.compile_commands()[0]
71 assert "--exe" not in cmd
72 assert "--build" not in cmd
75 """When using cmake, -CFLAGS and -LDFLAGS should not appear."""
78 pytest.skip(
"cmake+ninja not available")
79 cmd = v.compile_commands()[0]
80 assert "-CFLAGS" not in cmd
81 assert "-LDFLAGS" not in cmd
84 """When using cmake, driver.cpp should not be in the verilator command."""
87 pytest.skip(
"cmake+ninja not available")
88 cmd = v.compile_commands()[0]
89 assert not any(
"driver.cpp" in str(c)
for c
in cmd)
93 cmd = v.compile_commands()[0]
94 assert "--trace-fst" in cmd
95 assert "--trace-params" in cmd
98 with mock.patch.dict(os.environ,
99 {
"VERILATOR_PATH":
"/custom/verilator_bin"}):
101 assert v.verilator_bin ==
"/custom/verilator_bin"
104 with mock.patch.dict(os.environ, {
"VERILATOR_PATH":
"/usr/bin/verilator"}):
106 assert v.verilator_bin == str(Path(
"/usr/bin/verilator_bin"))
110 cmd = v.compile_commands()[0]
111 assert "+define+FOO=BAR" in cmd
112 assert "+define+BAZ" in cmd
116 """Tests for the make fallback when cmake/ninja are not available."""
119 """Create a Verilator instance that thinks cmake/ninja are missing."""
123 @pytest.fixture(autouse=True)
125 """Patch shutil.which so cmake and ninja appear absent."""
126 original_which = shutil.which
128 def _which_no_cmake(name, *args, **kwargs):
129 if name
in (
"cmake",
"ninja"):
131 return original_which(name, *args, **kwargs)
133 with mock.patch(
"shutil.which", side_effect=_which_no_cmake):
138 cmds = v.compile_commands()
139 assert len(cmds) == 2
140 assert cmds[1][0] ==
"make"
144 cmd = v.compile_commands()[0]
145 assert "--exe" in cmd
149 cmd = v.compile_commands()[0]
150 assert "-CFLAGS" in cmd
151 idx = cmd.index(
"-CFLAGS")
152 assert "-DTOP_MODULE=TestTop" in cmd[idx + 1]
156 cmd = v.compile_commands()[0]
157 assert any(
"driver.cpp" in str(c)
for c
in cmd)
161 cmd = v.compile_commands()[0]
162 assert "-LDFLAGS" in cmd
163 idx = cmd.index(
"-LDFLAGS")
164 assert "-lEsiCosimDpiServer" in cmd[idx + 1]
168 cmd = v.compile_commands()[0]
169 assert "-LDFLAGS" not in cmd
173 cmd = v.compile_commands()[0]
174 idx = cmd.index(
"-CFLAGS")
175 assert "-DTRACE" in cmd[idx + 1]
179 cmds = v.compile_commands()
181 assert make_cmd[0] ==
"make"
182 assert "-C" in make_cmd
183 assert "obj_dir" in make_cmd
184 assert "-f" in make_cmd
185 assert "VMyTop.mk" in make_cmd
189 with mock.patch.object(Path,
"cwd", return_value=tmp_path):
190 cmd = v.run_command(gui=
False)
191 assert cmd == [str(tmp_path /
"obj_dir" /
"VMyTop")]
197 root = tmp_path /
"verilator"
199 (root /
"include").mkdir()
200 (root /
"include" /
"verilated.h").touch()
201 with mock.patch.dict(os.environ, {
"VERILATOR_ROOT": str(root)}):
203 assert v._find_verilator_root() == root
206 root = tmp_path /
"verilator"
207 (root /
"bin").mkdir(parents=
True)
208 (root /
"include").mkdir()
209 (root /
"include" /
"verilated.h").touch()
210 fake_bin = root /
"bin" /
"verilator_bin"
212 fake_bin.chmod(0o755)
213 with mock.patch.dict(os.environ, {}, clear=
False):
214 os.environ.pop(
"VERILATOR_ROOT",
None)
215 with mock.patch(
"shutil.which", return_value=str(fake_bin)):
217 found = v._find_verilator_root()
221 with mock.patch.dict(os.environ, {}, clear=
False):
222 os.environ.pop(
"VERILATOR_ROOT",
None)
223 with mock.patch(
"shutil.which", return_value=
None):
225 with pytest.raises(RuntimeError):
226 v._find_verilator_root()
232 obj_dir = tmp_path /
"obj_dir"
234 root = tmp_path /
"verilator"
235 (root /
"include").mkdir(parents=
True)
236 (root /
"include" /
"verilated.h").touch()
237 with mock.patch.dict(os.environ, {
"VERILATOR_ROOT": str(root)}):
239 build_dir = v._write_cmake(obj_dir)
240 assert (build_dir /
"CMakeLists.txt").exists()
241 content = (build_dir /
"CMakeLists.txt").read_text()
242 assert "VTestTop" in content
243 assert "verilated.cpp" in content
244 assert "verilated_threads.cpp" in content
245 assert "driver.cpp" in content
248 obj_dir = tmp_path /
"obj_dir"
250 root = tmp_path /
"verilator"
251 (root /
"include").mkdir(parents=
True)
252 (root /
"include" /
"verilated.h").touch()
253 with mock.patch.dict(os.environ, {
"VERILATOR_ROOT": str(root)}):
255 build_dir = v._write_cmake(obj_dir)
256 content = (build_dir /
"CMakeLists.txt").read_text()
257 assert "verilated_fst_c.cpp" in content
258 assert "TRACE" in content
266 pytest.skip(
"cmake+ninja not available")
267 with mock.patch.object(Path,
"cwd", return_value=tmp_path):
268 cmd = v.run_command(gui=
False)
269 assert cmd == [str(tmp_path /
"obj_dir" /
"cmake_build" /
"VMyTop")]
test_macro_definitions(self, tmp_path)
test_respects_verilator_path_env(self, tmp_path)
test_driver_not_in_verilator_cmd_cmake(self, tmp_path)
test_trace_flags_in_debug(self, tmp_path)
test_uses_verilator_bin(self, tmp_path)
test_no_cflags_or_ldflags_cmake(self, tmp_path)
test_no_exe_or_build_flags_cmake(self, tmp_path)
test_cmake_and_ninja_commands(self, tmp_path)
test_verilator_path_redirects_perl_wrapper(self, tmp_path)
test_from_env(self, tmp_path)
test_from_bin_in_path(self, tmp_path)
test_raises_when_not_found(self, tmp_path)
test_fallback_has_cflags(self, tmp_path)
test_fallback_exe_path(self, tmp_path)
test_fallback_trace_cflags_in_debug(self, tmp_path)
test_fallback_has_driver(self, tmp_path)
_make_no_cmake(self, tmp_path, **kwargs)
test_fallback_no_ldflags_without_dpi(self, tmp_path)
test_fallback_uses_make(self, tmp_path)
test_fallback_has_exe_flag(self, tmp_path)
test_fallback_make_command(self, tmp_path)
test_fallback_has_ldflags_with_dpi(self, tmp_path)
test_exe_path_cmake(self, tmp_path)
test_generates_cmake(self, tmp_path)
test_trace_sources_in_debug(self, tmp_path)
_make_verilator(run_dir, top="TestTop", debug=False, dpi_so=None, macros=None)