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>
9 from migen
.fhdl
.structure
import _Fragment
11 from litex
.build
import tools
12 from litex
.build
.generic_platform
import *
15 sim_directory
= os
.path
.abspath(os
.path
.dirname(__file__
))
16 core_directory
= os
.path
.join(sim_directory
, 'core')
19 def _generate_sim_h_struct(name
, index
, siglist
):
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'
31 def _generate_sim_h(platform
):
38 for args
in platform
.sim_requested
:
39 content
+= _generate_sim_h_struct(*args
)
43 void litex_sim_init(void **out);
46 #endif /* __SIM_CORE_H_ */
48 tools
.write_to_file("dut_header.h", content
)
51 def _generate_sim_cpp_struct(name
, index
, siglist
):
54 for i
, (signame
, sigbits
, sigfname
) in enumerate(siglist
):
55 content
+= ' {}{}[{}].signal = &dut->{};\n'.format(name
, index
, i
, sigfname
)
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
)
63 def _generate_sim_cpp(platform
, trace
=False, trace_start
=0, trace_end
=-1):
69 #include <verilated.h>
70 #include "dut_header.h"
72 extern "C" void litex_sim_init_tracer(void *vdut, long start, long end);
73 extern "C" void litex_sim_tracer_dump();
75 extern "C" void litex_sim_dump()
80 litex_sim_tracer_dump();
85 extern "C" void litex_sim_init(void **out)
91 litex_sim_init_tracer(dut, {}, {});
93 """.format(trace_start
, trace_end
)
94 for args
in platform
.sim_requested
:
95 content
+= _generate_sim_cpp_struct(*args
)
101 tools
.write_to_file("dut_init.cpp", content
)
104 def _generate_sim_variables(include_paths
):
106 for path
in include_paths
:
107 include
+= "-I"+path
+" "
111 """.format(core_directory
, include
)
112 tools
.write_to_file("variables.mak", content
)
115 def _generate_sim_config(config
):
116 content
= config
.get_json()
117 tools
.write_to_file("sim_config.js", content
)
120 def _build_sim(build_name
, sources
, threads
, coverage
, opt_level
="O3", trace_fst
=False):
121 makefile
= os
.path
.join(core_directory
, 'Makefile')
123 for filename
, language
, library
in sources
:
124 cc_srcs
.append("--cc " + filename
+ " ")
125 build_script_contents
= """\
127 make -C . -f {} {} {} {} {} {}
128 mkdir -p modules && cp obj_dir/*.so modules
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 "",
136 build_script_file
= "build_" + build_name
+ ".sh"
137 tools
.write_to_file(build_script_file
, build_script_contents
, force_unix
=True)
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:
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
)))
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":
160 termios_settings
= termios
.tcgetattr(sys
.stdin
.fileno())
162 r
= subprocess
.call(["bash", run_script_file
])
164 raise OSError("Subprocess failed")
167 if sys
.platform
!= "win32":
168 termios
.tcsetattr(sys
.stdin
.fileno(), termios
.TCSAFLUSH
, termios_settings
)
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):
177 # create build directory
178 os
.makedirs(build_dir
, exist_ok
=True)
183 if not isinstance(fragment
, _Fragment
):
184 fragment
= fragment
.get_fragment()
185 platform
.finalize(fragment
)
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
)
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
)
200 # generate sim config
202 _generate_sim_config(sim_config
)
205 _build_sim(build_name
, platform
.sources
, threads
, coverage
, opt_level
, trace_fst
)
209 _compile_sim(build_name
, verbose
)
211 if sim_config
.has_module("ethernet"):
213 if sim_config
.has_module("xgmii_ethernet"):
215 _run_sim(build_name
, as_root
=run_as_root
)