Make verilator build output error messages.
[litex.git] / litex / build / sim / verilator.py
1 # This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
2 # License: BSD
3
4 import os
5 import subprocess
6
7 from litex.gen.fhdl.structure import _Fragment
8 from litex.build import tools
9 from litex.build.generic_platform import *
10
11
12 sim_directory = os.path.abspath(os.path.dirname(__file__))
13
14
15 def _build_tb(platform, vns, serial, template):
16 def io_name(resource, subsignal=None):
17 res = platform.lookup_request(resource)
18 if subsignal is not None:
19 res = getattr(res, subsignal)
20 return vns.get_name(res)
21
22 ios = """
23 #define SYS_CLK dut->{sys_clk}
24 """.format(sys_clk=io_name("sys_clk"))
25
26 if serial == "pty":
27 ios += "#define WITH_SERIAL_PTY"
28 elif serial == "console":
29 pass
30 else:
31 raise ValueError
32 try:
33 ios += """
34 #define SERIAL_SOURCE_VALID dut->{serial_source_valid}
35 #define SERIAL_SOURCE_READY dut->{serial_source_ready}
36 #define SERIAL_SOURCE_DATA dut->{serial_source_data}
37
38 #define SERIAL_SINK_VALID dut->{serial_sink_valid}
39 #define SERIAL_SINK_READY dut->{serial_sink_ready}
40 #define SERIAL_SINK_DATA dut->{serial_sink_data}
41 """.format(
42 serial_source_valid=io_name("serial", "source_valid"),
43 serial_source_ready=io_name("serial", "source_ready"),
44 serial_source_data=io_name("serial", "source_data"),
45
46 serial_sink_valid=io_name("serial", "sink_valid"),
47 serial_sink_ready=io_name("serial", "sink_ready"),
48 serial_sink_data=io_name("serial", "sink_data"),
49 )
50 except:
51 pass
52
53 try:
54 ios += """
55 #define ETH_SOURCE_VALID dut->{eth_source_valid}
56 #define ETH_SOURCE_READY dut->{eth_source_ready}
57 #define ETH_SOURCE_DATA dut->{eth_source_data}
58
59 #define ETH_SINK_VALID dut->{eth_sink_valid}
60 #define ETH_SINK_READY dut->{eth_sink_ready}
61 #define ETH_SINK_DATA dut->{eth_sink_data}
62 """.format(
63 eth_source_valid=io_name("eth", "source_valid"),
64 eth_source_ready=io_name("eth", "source_ready"),
65 eth_source_data=io_name("eth", "source_data"),
66
67 eth_sink_valid=io_name("eth", "sink_valid"),
68 eth_sink_ready=io_name("eth", "sink_ready"),
69 eth_sink_data=io_name("eth", "sink_data"),
70 )
71 except:
72 pass
73
74 content = ""
75 f = open(template, "r")
76 done = False
77 for l in f:
78 content += l
79 if "/* ios */" in l and not done:
80 content += ios
81 done = True
82
83 f.close()
84 tools.write_to_file("dut_tb.cpp", content)
85
86
87 def _build_sim(platform, vns, build_name, include_paths, serial, verbose):
88 include = ""
89 for path in include_paths:
90 include += "-I"+path+" "
91
92 build_script_contents = """# Autogenerated by LiteX
93 rm -rf obj_dir/
94 verilator {disable_warnings} -O3 --cc dut.v --exe dut_tb.cpp -LDFLAGS "-lpthread" -trace {include}
95 make -j -C obj_dir/ -f Vdut.mk Vdut
96
97 """.format(
98 disable_warnings="-Wno-fatal",
99 include=include)
100 build_script_file = "build_" + build_name + ".sh"
101 tools.write_to_file(build_script_file, build_script_contents, force_unix=True)
102
103 _build_tb(platform, vns, serial, os.path.join(sim_directory, "dut_tb.cpp"))
104 p = subprocess.Popen(["bash", build_script_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
105 output, _ = p.communicate()
106 output = output.decode('utf-8')
107 if p.returncode != 0:
108 error_messages = []
109 for l in output.splitlines():
110 if verbose or "error" in l.lower():
111 error_messages.append(l)
112 raise OSError("Subprocess failed with {}\n{}".format(p.returncode, "\n".join(error_messages)))
113 if verbose:
114 print(output)
115
116
117 def _run_sim(build_name):
118 run_script_contents = """obj_dir/Vdut
119 """
120 run_script_file = "run_" + build_name + ".sh"
121 tools.write_to_file(run_script_file, run_script_contents, force_unix=True)
122 r = subprocess.call(["bash", run_script_file])
123 if r != 0:
124 raise OSError("Subprocess failed")
125
126
127 class SimVerilatorToolchain:
128 def build(self, platform, fragment, build_dir="build", build_name="top",
129 toolchain_path=None, serial="console", run=True, verbose=False):
130 tools.mkdir_noerror(build_dir)
131 os.chdir(build_dir)
132
133 if not isinstance(fragment, _Fragment):
134 fragment = fragment.get_fragment()
135 platform.finalize(fragment)
136
137 v_output = platform.get_verilog(fragment)
138 named_sc, named_pc = platform.resolve_signals(v_output.ns)
139 v_output.write("dut.v")
140
141 include_paths = []
142 for source in platform.sources:
143 path = os.path.dirname(source[0]).replace("\\", "\/")
144 if path not in include_paths:
145 include_paths.append(path)
146 include_paths += platform.verilog_include_paths
147 _build_sim(platform, v_output.ns, build_name, include_paths, serial, verbose)
148
149 if run:
150 _run_sim(build_name)
151
152 os.chdir("..")
153
154 return v_output.ns