build: assume vendor tools are in the PATH and remove automatic sourcing, source...
[litex.git] / litex / build / sim / verilator.py
1 # This file is Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2017 Pierre-Olivier Vauboin <po@lambdaconcept>
3 # License: BSD
4
5 import os
6 import sys
7 import subprocess
8
9 from migen.fhdl.structure import _Fragment
10
11 from litex.build import tools
12 from litex.build.generic_platform import *
13
14
15 sim_directory = os.path.abspath(os.path.dirname(__file__))
16 core_directory = os.path.join(sim_directory, 'core')
17
18
19 def _generate_sim_h_struct(name, index, siglist):
20 content = ''
21
22 content += 'struct pad_s {}{}[] = {{\n'.format(name, index)
23 for signame, sigbits, dummy in siglist:
24 content += ' {{ (char*)"{}", {}, NULL }},\n'.format(signame, sigbits)
25 content += ' { NULL, 0, NULL }\n'
26 content += '};\n\n'
27
28 return content
29
30
31 def _generate_sim_h(platform):
32 content = """\
33 #ifndef __SIM_CORE_H_
34 #define __SIM_CORE_H_
35 #include "pads.h"
36
37 """
38 for args in platform.sim_requested:
39 content += _generate_sim_h_struct(*args)
40
41 content += """\
42 #ifndef __cplusplus
43 void litex_sim_init(void **out);
44 #endif
45
46 #endif /* __SIM_CORE_H_ */
47 """
48 tools.write_to_file("dut_header.h", content)
49
50
51 def _generate_sim_cpp_struct(name, index, siglist):
52 content = ''
53
54 for i, (signame, sigbits, sigfname) in enumerate(siglist):
55 content += ' {}{}[{}].signal = &dut->{};\n'.format(name, index, i, sigfname)
56
57 idx_int = 0 if not index else int(index)
58 content += ' litex_sim_register_pads({}{}, (char*)"{}", {});\n\n'.format(name, index, name, idx_int)
59
60 return content
61
62
63 def _generate_sim_cpp(platform, trace=False, trace_start=0, trace_end=-1):
64 content = """\
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include "Vdut.h"
69 #include <verilated.h>
70 #include "dut_header.h"
71
72 extern "C" void litex_sim_init_tracer(void *vdut, long start, long end);
73 extern "C" void litex_sim_tracer_dump();
74
75 extern "C" void litex_sim_dump()
76 {
77 """
78 if trace:
79 content += """\
80 litex_sim_tracer_dump();
81 """
82 content += """\
83 }}
84
85 extern "C" void litex_sim_init(void **out)
86 {{
87 Vdut *dut;
88
89 dut = new Vdut;
90
91 litex_sim_init_tracer(dut, {}, {});
92
93 """.format(trace_start, trace_end)
94 for args in platform.sim_requested:
95 content += _generate_sim_cpp_struct(*args)
96
97 content += """\
98 *out=dut;
99 }
100 """
101 tools.write_to_file("dut_init.cpp", content)
102
103
104 def _generate_sim_variables(include_paths):
105 include = ""
106 for path in include_paths:
107 include += "-I"+path+" "
108 content = """\
109 SRC_DIR = {}
110 INC_DIR = {}
111 """.format(core_directory, include)
112 tools.write_to_file("variables.mak", content)
113
114
115 def _generate_sim_config(config):
116 content = config.get_json()
117 tools.write_to_file("sim_config.js", content)
118
119
120 def _build_sim(build_name, sources, threads, coverage, opt_level="O3", trace_fst=False):
121 makefile = os.path.join(core_directory, 'Makefile')
122 cc_srcs = []
123 for filename, language, library in sources:
124 cc_srcs.append("--cc " + filename + " ")
125 build_script_contents = """\
126 rm -rf obj_dir/
127 make -C . -f {} {} {} {} {} {}
128 mkdir -p modules && cp obj_dir/*.so modules
129 """.format(makefile,
130 "CC_SRCS=\"{}\"".format("".join(cc_srcs)),
131 "THREADS={}".format(threads) if int(threads) > 1 else "",
132 "COVERAGE=1" if coverage else "",
133 "OPT_LEVEL={}".format(opt_level),
134 "TRACE_FST=1" if trace_fst else "",
135 )
136 build_script_file = "build_" + build_name + ".sh"
137 tools.write_to_file(build_script_file, build_script_contents, force_unix=True)
138
139 def _compile_sim(build_name, verbose):
140 build_script_file = "build_" + build_name + ".sh"
141 p = subprocess.Popen(["bash", build_script_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
142 output, _ = p.communicate()
143 output = output.decode('utf-8')
144 if p.returncode != 0:
145 error_messages = []
146 for l in output.splitlines():
147 if verbose or "error" in l.lower():
148 error_messages.append(l)
149 raise OSError("Subprocess failed with {}\n{}".format(p.returncode, "\n".join(error_messages)))
150 if verbose:
151 print(output)
152
153 def _run_sim(build_name, as_root=False):
154 run_script_contents = "sudo " if as_root else ""
155 run_script_contents += "obj_dir/Vdut"
156 run_script_file = "run_" + build_name + ".sh"
157 tools.write_to_file(run_script_file, run_script_contents, force_unix=True)
158 if sys.platform != "win32":
159 import termios
160 termios_settings = termios.tcgetattr(sys.stdin.fileno())
161 try:
162 r = subprocess.call(["bash", run_script_file])
163 if r != 0:
164 raise OSError("Subprocess failed")
165 except:
166 pass
167 if sys.platform != "win32":
168 termios.tcsetattr(sys.stdin.fileno(), termios.TCSAFLUSH, termios_settings)
169
170
171 class SimVerilatorToolchain:
172 def build(self, platform, fragment, build_dir="build", build_name="dut",
173 serial="console", build=True, run=True, threads=1,
174 verbose=True, sim_config=None, coverage=False, opt_level="O0",
175 trace=False, trace_fst=False, trace_start=0, trace_end=-1):
176
177 # create build directory
178 os.makedirs(build_dir, exist_ok=True)
179 os.chdir(build_dir)
180
181 if build:
182 # finalize design
183 if not isinstance(fragment, _Fragment):
184 fragment = fragment.get_fragment()
185 platform.finalize(fragment)
186
187 # generate top module
188 top_output = platform.get_verilog(fragment,
189 name=build_name, dummy_signal=False, regular_comb=False, blocking_assign=True)
190 named_sc, named_pc = platform.resolve_signals(top_output.ns)
191 top_file = build_name + ".v"
192 top_output.write(top_file)
193 platform.add_source(top_file)
194
195 # generate cpp header/main/variables
196 _generate_sim_h(platform)
197 _generate_sim_cpp(platform, trace, trace_start, trace_end)
198 _generate_sim_variables(platform.verilog_include_paths)
199
200 # generate sim config
201 if sim_config:
202 _generate_sim_config(sim_config)
203
204 # build
205 _build_sim(build_name, platform.sources, threads, coverage, opt_level, trace_fst)
206
207 # run
208 if run:
209 _compile_sim(build_name, verbose)
210 run_as_root = False
211 if sim_config.has_module("ethernet"):
212 run_as_root = True
213 if sim_config.has_module("xgmii_ethernet"):
214 run_as_root = True
215 _run_sim(build_name, as_root=run_as_root)
216
217 os.chdir("../../")
218
219 if build:
220 return top_output.ns