sim/verilator: catch ctrl-c on exit and revert default termios settings
[litex.git] / litex / build / sim / verilator.py
1 # This file is Copyright (c) 2015-2016 Florent Kermarrec <florent@enjoy-digital.fr>
2 # 2017 Pierre-Olivier Vauboin <po@lambdaconcept.com>
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):
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(void **out)
73 {
74 Vdut *dut;
75
76 dut = new Vdut;
77
78 """
79 for args in platform.sim_requested:
80 content += _generate_sim_cpp_struct(*args)
81
82 content += """\
83 *out=dut;
84 }
85 """
86 tools.write_to_file("dut_init.cpp", content)
87
88
89 def _generate_sim_variables(include_paths):
90 include = ""
91 for path in include_paths:
92 include += "-I"+path+" "
93
94 content = """\
95 SRC_DIR = {}
96 INC_DIR = {}
97 """.format(core_directory, include)
98 tools.write_to_file("variables.mak", content)
99
100
101 def _generate_sim_config(config):
102 content = config.get_json()
103 tools.write_to_file("sim_config.js", content)
104
105
106 def _build_sim(platform, build_name, verbose):
107 makefile = os.path.join(core_directory, 'Makefile')
108 build_script_contents = """\
109 rm -rf obj_dir/
110 make -C . -f {}
111 mkdir -p modules && cp obj_dir/*.so modules
112 """.format(makefile)
113 build_script_file = "build_" + build_name + ".sh"
114 tools.write_to_file(build_script_file, build_script_contents, force_unix=True)
115
116 p = subprocess.Popen(["bash", build_script_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
117 output, _ = p.communicate()
118 output = output.decode('utf-8')
119 if p.returncode != 0:
120 error_messages = []
121 for l in output.splitlines():
122 if verbose or "error" in l.lower():
123 error_messages.append(l)
124 raise OSError("Subprocess failed with {}\n{}".format(p.returncode, "\n".join(error_messages)))
125 if verbose:
126 print(output)
127
128
129 def _run_sim(build_name):
130 run_script_contents = """\
131 sudo obj_dir/Vdut
132 """
133 run_script_file = "run_" + build_name + ".sh"
134 tools.write_to_file(run_script_file, run_script_contents, force_unix=True)
135 if sys.platform != "win32":
136 import termios
137 termios_settings = termios.tcgetattr(sys.stdin.fileno())
138 try:
139 r = subprocess.call(["bash", run_script_file])
140 if r != 0:
141 raise OSError("Subprocess failed")
142 except:
143 if sys.platform != "win32":
144 termios.tcsetattr(sys.stdin.fileno(), termios.TCSAFLUSH, termios_settings)
145
146
147 class SimVerilatorToolchain:
148 def build(self, platform, fragment, build_dir="build", build_name="dut",
149 toolchain_path=None, serial="console", run=True, verbose=True,
150 sim_config=None):
151 os.makedirs(build_dir, exist_ok=True)
152 os.chdir(build_dir)
153
154 if not isinstance(fragment, _Fragment):
155 fragment = fragment.get_fragment()
156 platform.finalize(fragment)
157
158 v_output = platform.get_verilog(fragment, name=build_name)
159 named_sc, named_pc = platform.resolve_signals(v_output.ns)
160 v_output.write(build_name + ".v")
161
162 include_paths = []
163 for source in platform.sources:
164 path = os.path.dirname(source[0]).replace("\\", "\/")
165 if path not in include_paths:
166 include_paths.append(path)
167 include_paths += platform.verilog_include_paths
168 _generate_sim_h(platform)
169 _generate_sim_cpp(platform)
170 _generate_sim_variables(include_paths)
171 if sim_config:
172 _generate_sim_config(sim_config)
173 _build_sim(platform, build_name, verbose)
174
175 if run:
176 _run_sim(build_name)
177
178 os.chdir("..")
179
180 return v_output.ns