xref: /XiangShan/scripts/xiangshan.py (revision 54cc3a061d25ca2e64b2828dd96ba54d6f2f2cf2)
1#***************************************************************************************
2# Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
3# Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences
4# Copyright (c) 2020-2021 Peng Cheng Laboratory
5#
6# XiangShan is licensed under Mulan PSL v2.
7# You can use this software according to the terms and conditions of the Mulan PSL v2.
8# You may obtain a copy of Mulan PSL v2 at:
9#          http://license.coscl.org.cn/MulanPSL2
10#
11# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14#
15# See the Mulan PSL v2 for more details.
16#***************************************************************************************
17
18# Simple version of xiangshan python wrapper
19
20import argparse
21import json
22import os
23import random
24import signal
25import subprocess
26import sys
27import time
28
29import psutil
30
31
32def load_all_gcpt(gcpt_path, json_path):
33    all_gcpt = []
34    with open(json_path) as f:
35        data = json.load(f)
36    for benchspec in data:
37        for point in data[benchspec]:
38            weight = data[benchspec][point]
39            gcpt = os.path.join(gcpt_path, "_".join([benchspec, point, weight]))
40            bin_dir = os.path.join(gcpt, "0")
41            bin_file = list(os.listdir(bin_dir))
42            assert(len(bin_file) == 1)
43            bin_path = os.path.join(bin_dir, bin_file[0])
44            assert(os.path.isfile(bin_path))
45            all_gcpt.append(bin_path)
46    return all_gcpt
47
48class XSArgs(object):
49    script_path = os.path.realpath(__file__)
50    # default path to the repositories
51    noop_home = os.path.join(os.path.dirname(script_path), "..")
52    nemu_home = os.path.join(noop_home, "../NEMU")
53    am_home = os.path.join(noop_home, "../nexus-am")
54    dramsim3_home = os.path.join(noop_home, "../DRAMsim3")
55    rvtest_home = os.path.join(noop_home, "../riscv-tests")
56    default_wave_home = os.path.join(noop_home, "build")
57    wave_home   = default_wave_home
58
59    def __init__(self, args):
60        # all path environment variables that should be set
61        all_path = [
62            # (python argument, environment variable, default, target function)
63            (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
64            (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
65            (args.am, "AM_HOME", self.am_home, self.set_am_home),
66            (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
67            (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
68        ]
69        for (arg_in, env, default, set_func) in all_path:
70            set_func(self.__extract_path(arg_in, env, default))
71        # Chisel arguments
72        self.enable_log = args.enable_log
73        self.num_cores = args.num_cores
74        # Makefile arguments
75        self.threads = args.threads
76        self.with_dramsim3 = 1 if args.with_dramsim3 else None
77        self.is_release = 1 if args.release else None
78        self.is_spike = "Spike" if args.spike else None
79        self.trace = 1 if args.trace or not args.disable_fork and not args.trace_fst else None
80        self.trace_fst = "fst" if args.trace_fst else None
81        self.config = args.config
82        self.is_mfc = 1 if args.mfc else None
83        self.emu_optimize = args.emu_optimize
84        self.xprop = 1 if args.xprop else None
85        self.with_chiseldb = 0 if args.no_db else None
86        # emu arguments
87        self.max_instr = args.max_instr
88        self.ram_size = args.ram_size
89        self.seed = random.randint(0, 9999)
90        self.numa = args.numa
91        self.diff = args.diff
92        if args.spike and "nemu" in args.diff:
93            self.diff = self.diff.replace("nemu-interpreter", "spike")
94        self.fork = not args.disable_fork
95        self.disable_diff = args.no_diff
96        self.disable_db = args.no_db
97        # wave dump path
98        if args.wave_dump is not None:
99            self.set_wave_home(args.wave_dump)
100        else:
101            self.set_wave_home(self.default_wave_home)
102
103    def get_env_variables(self):
104        all_env = {
105            "NOOP_HOME"    : self.noop_home,
106            "NEMU_HOME"    : self.nemu_home,
107            "WAVE_HOME"    : self.wave_home,
108            "AM_HOME"      : self.am_home,
109            "DRAMSIM3_HOME": self.dramsim3_home,
110            "MODULEPATH": "/usr/share/Modules/modulefiles:/etc/modulefiles"
111        }
112        return all_env
113
114    def get_chisel_args(self, prefix=None):
115        chisel_args = [
116            (self.enable_log, "enable-log")
117        ]
118        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
119        if prefix is not None:
120            args = map(lambda x: prefix + x, args)
121        return args
122
123    def get_makefile_args(self):
124        makefile_args = [
125            (self.threads,       "EMU_THREADS"),
126            (self.with_dramsim3, "WITH_DRAMSIM3"),
127            (self.is_release,    "RELEASE"),
128            (self.is_spike,      "REF"),
129            (self.trace,         "EMU_TRACE"),
130            (self.trace_fst,     "EMU_TRACE"),
131            (self.config,        "CONFIG"),
132            (self.num_cores,     "NUM_CORES"),
133            (self.is_mfc,        "MFC"),
134            (self.emu_optimize,  "EMU_OPTIMIZE"),
135            (self.xprop,         "ENABLE_XPROP"),
136            (self.with_chiseldb, "WITH_CHISELDB")
137        ]
138        args = filter(lambda arg: arg[0] is not None, makefile_args)
139        return args
140
141    def get_emu_args(self):
142        emu_args = [
143            (self.max_instr, "max-instr"),
144            (self.diff,      "diff"),
145            (self.seed,      "seed"),
146            (self.ram_size,  "ram-size"),
147        ]
148        args = filter(lambda arg: arg[0] is not None, emu_args)
149        return args
150
151    def show(self):
152        print("Extra environment variables:")
153        env = self.get_env_variables()
154        for env_name in env:
155            print(f"{env_name}: {env[env_name]}")
156        print()
157        print("Chisel arguments:")
158        print(" ".join(self.get_chisel_args()))
159        print()
160        print("Makefile arguments:")
161        for val, name in self.get_makefile_args():
162            print(f"{name}={val}")
163        print()
164        print("emu arguments:")
165        for val, name in self.get_emu_args():
166            print(f"--{name} {val}")
167        print()
168
169    def __extract_path(self, path, env=None, default=None):
170        if path is None and env is not None:
171            path = os.getenv(env)
172        if path is None and default is not None:
173            path = default
174        path = os.path.realpath(path)
175        return path
176
177    def set_noop_home(self, path):
178        self.noop_home = path
179
180    def set_nemu_home(self, path):
181        self.nemu_home = path
182
183    def set_am_home(self, path):
184        self.am_home = path
185
186    def set_dramsim3_home(self, path):
187        self.dramsim3_home = path
188
189    def set_rvtest_home(self, path):
190        self.rvtest_home = path
191
192    def set_wave_home(self, path):
193        print(f"set wave home to {path}")
194        self.wave_home = path
195
196# XiangShan environment
197class XiangShan(object):
198    def __init__(self, args):
199        self.args = XSArgs(args)
200        self.timeout = args.timeout
201
202    def show(self):
203        self.args.show()
204
205    def make_clean(self):
206        print("Clean up CI workspace")
207        self.show()
208        return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean')
209        return return_code
210
211    def generate_verilog(self):
212        print("Generating XiangShan verilog with the following configurations:")
213        self.show()
214        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
215        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
216        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
217        return return_code
218
219    def generate_sim_verilog(self):
220        print("Generating XiangShan sim-verilog with the following configurations:")
221        self.show()
222        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
223        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
224        return_code = self.__exec_cmd(f'make -C $NOOP_HOME sim-verilog SIM_ARGS="{sim_args}" {make_args}')
225        return return_code
226
227    def build_emu(self):
228        print("Building XiangShan emu with the following configurations:")
229        self.show()
230        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
231        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
232        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
233        return return_code
234
235    def build_simv(self):
236        print("Building XiangShan simv with the following configurations")
237        self.show()
238        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
239        # TODO: make the following commands grouped as unseen scripts
240        return_code = self.__exec_cmd(f'\
241            eval `/usr/bin/modulecmd zsh load license`;\
242            eval `/usr/bin/modulecmd zsh load synopsys/vcs/Q-2020.03-SP2`;\
243            eval `/usr/bin/modulecmd zsh load synopsys/verdi/S-2021.09-SP1`;\
244            VERDI_HOME=/nfs/tools/synopsys/verdi/S-2021.09-SP1 \
245            make -C $NOOP_HOME simv {make_args} CONSIDER_FSDB=1')  # set CONSIDER_FSDB for compatibility
246        return return_code
247
248    def run_emu(self, workload):
249        print("Running XiangShan emu with the following configurations:")
250        self.show()
251        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
252        print("workload:", workload)
253        numa_args = ""
254        if self.args.numa:
255            numa_info = get_free_cores(self.args.threads)
256            numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}"
257        fork_args = "--enable-fork" if self.args.fork else ""
258        diff_args = "--no-diff" if self.args.disable_diff else ""
259        chiseldb_args = "--dump-db" if not self.args.disable_db else ""
260        return_code = self.__exec_cmd(f'ulimit -s {32 * 1024}; {numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args} {fork_args} {diff_args} {chiseldb_args}')
261        return return_code
262
263    def run_simv(self, workload):
264        print("Running XiangShan simv with the following configurations:")
265        self.show()
266        diff_args = "$NOOP_HOME/"+ args.diff
267        assert_args = "-assert finish_maxfail=30 -assert global_finish_maxfail=10000"
268        return_code = self.__exec_cmd(f'cd $NOOP_HOME/build && ./simv +workload={workload} +diff={diff_args} +dump-wave=fsdb {assert_args}')
269        return return_code
270
271    def run(self, args):
272        if args.ci is not None:
273            return self.run_ci(args.ci)
274        if args.ci_vcs is not None:
275            return self.run_ci_vcs(args.ci_vcs)
276        actions = [
277            (args.generate, lambda _ : self.generate_verilog()),
278            (args.vcs_gen, lambda _ : self.generate_sim_verilog()),
279            (args.build, lambda _ : self.build_emu()),
280            (args.vcs_build, lambda _ : self.build_simv()),
281            (args.workload, lambda args: self.run_emu(args.workload)),
282            (args.clean, lambda _ : self.make_clean())
283        ]
284        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
285        for i, action in enumerate(valid_actions):
286            print(f"Action {i}:")
287            ret = action(args)
288            if ret:
289                return ret
290        return 0
291
292    def __exec_cmd(self, cmd):
293        env = dict(os.environ)
294        env.update(self.args.get_env_variables())
295        print("subprocess call cmd:", cmd)
296        start = time.time()
297        proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid)
298        try:
299            return_code = proc.wait(self.timeout)
300            end = time.time()
301            print(f"Elapsed time: {end - start} seconds")
302            return return_code
303        except (KeyboardInterrupt, subprocess.TimeoutExpired):
304            os.killpg(os.getpgid(proc.pid), signal.SIGINT)
305            print(f"KeyboardInterrupt or TimeoutExpired.")
306            return 0
307
308    def __get_ci_cputest(self, name=None):
309        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
310        cputest = os.listdir(base_dir)
311        cputest = filter(lambda x: x.endswith(".bin"), cputest)
312        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
313        return cputest
314
315    def __get_ci_rvtest(self, name=None):
316        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
317        riscv_tests = os.listdir(base_dir)
318        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
319        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
320        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
321        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
322        return riscv_tests
323
324    def __get_ci_misc(self, name=None):
325        base_dir = "/nfs/home/share/ci-workloads"
326        workloads = [
327            "bitmanip/bitMisc.bin",
328            "crypto/crypto-riscv64-noop.bin",
329            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
330            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
331            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
332            "ext_intr/amtest-riscv64-xs.bin",
333            "cache-alias/aliastest-riscv64-xs.bin",
334            "Svinval/rv64mi-p-svinval.bin",
335            "pmp/pmp.riscv.bin",
336            "pmp/pmp-am/amtest-riscv64-xs.bin",
337            "pmp/hugepage-pmp-atom/amtest-riscv64-xs.bin",
338            "asid/asid.bin",
339            "isa_misc/xret_clear_mprv.bin",
340            "isa_misc/satp_ppn.bin",
341            "cache-management/softprefetchtest-riscv64-xs.bin"
342        ]
343        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
344        return misc_tests
345
346    def __get_ci_mc(self, name=None):
347        base_dir = "/nfs/home/share/ci-workloads"
348        workloads = [
349            "dualcoretest/ldvio-riscv64-xs.bin"
350        ]
351        mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
352        return mc_tests
353
354    def __get_ci_nodiff(self, name=None):
355        base_dir = "/nfs/home/share/ci-workloads"
356        workloads = [
357            "cache-management/cacheoptest-riscv64-xs.bin"
358        ]
359        tests = map(lambda x: os.path.join(base_dir, x), workloads)
360        return tests
361
362    def __am_apps_path(self, bench):
363        filename = f"{bench}-riscv64-noop.bin"
364        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
365
366    def __get_ci_workloads(self, name):
367        workloads = {
368            "linux-hello": "bbl.bin",
369            "linux-hello-smp": "bbl.bin",
370            "linux-hello-opensbi": "fw_payload.bin",
371            "linux-hello-smp-opensbi": "fw_payload.bin",
372            "linux-hello-new": "bbl.bin",
373            "linux-hello-smp-new": "bbl.bin",
374            "povray": "_700480000000_.gz",
375            "mcf": "_17520000000_.gz",
376            "xalancbmk": "_266100000000_.gz",
377            "gcc": "_39720000000_.gz",
378            "namd": "_434640000000_.gz",
379            "milc": "_103620000000_.gz",
380            "lbm": "_140840000000_.gz",
381            "gromacs": "_275480000000_.gz",
382            "wrf": "_1916220000000_.gz",
383            "astar": "_122060000000_.gz"
384        }
385        if name in workloads:
386            return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
387        # select a random SPEC checkpoint
388        assert(name == "random")
389        all_cpt = [
390            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
391            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
392            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
393            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
394            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
395            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
396            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
397            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
398        ]
399        all_json = [
400            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
401            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
402            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
403            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
404            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
405            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
406            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
407            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
408        ]
409        assert(len(all_cpt) == len(all_json))
410        cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
411        all_gcpt = load_all_gcpt(cpt_path, json_path)
412        return [random.choice(all_gcpt)]
413
414    def run_ci(self, test):
415        all_tests = {
416            "cputest": self.__get_ci_cputest,
417            "riscv-tests": self.__get_ci_rvtest,
418            "misc-tests": self.__get_ci_misc,
419            "mc-tests": self.__get_ci_mc,
420            "nodiff-tests": self.__get_ci_nodiff,
421            "microbench": self.__am_apps_path,
422            "coremark": self.__am_apps_path
423        }
424        for target in all_tests.get(test, self.__get_ci_workloads)(test):
425            print(target)
426            ret = self.run_emu(target)
427            if ret:
428                if self.args.default_wave_home != self.args.wave_home:
429                    print("copy wave file to " + self.args.wave_home)
430                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
431                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
432                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
433                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
434                return ret
435        return 0
436
437    def run_ci_vcs(self, test):
438        all_tests = {
439            "cputest": self.__get_ci_cputest,
440            "riscv-tests": self.__get_ci_rvtest,
441            "misc-tests": self.__get_ci_misc,
442            "mc-tests": self.__get_ci_mc,
443            "nodiff-tests": self.__get_ci_nodiff,
444            "microbench": self.__am_apps_path,
445            "coremark": self.__am_apps_path
446        }
447        for target in all_tests.get(test, self.__get_ci_workloads)(test):
448            print(target)
449            ret = self.run_simv(target)
450            if ret:
451                if self.args.default_wave_home != self.args.wave_home:
452                    print("copy wave file to " + self.args.wave_home)
453                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME")
454                    self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME")
455                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
456                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
457                return ret
458        return 0
459
460def get_free_cores(n):
461    while True:
462        # To avoid potential conflicts, we allow CI to use SMT.
463        num_logical_core = psutil.cpu_count(logical=False)
464        core_usage = psutil.cpu_percent(interval=1, percpu=True)
465        num_window = num_logical_core // n
466        for i in range(num_window):
467            window_usage = core_usage[i * n : i * n + n]
468            if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage):
469                return (((i * n) % 128)// 64, i * n, i * n + n - 1)
470        print(f"No free {n} cores found. CPU usage: {core_usage}\n")
471
472if __name__ == "__main__":
473    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
474    parser.add_argument('workload', nargs='?', type=str, default="",
475                        help='input workload file in binary format')
476    # actions
477    parser.add_argument('--build', action='store_true', help='build XS emu')
478    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
479    parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs')
480    parser.add_argument('--vcs-build', action='store_true', help='build XS simv')
481    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
482    parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv')
483    parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
484    parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
485    # environment variables
486    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
487    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
488    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
489    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
490    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
491    # chisel arguments
492    parser.add_argument('--enable-log', action='store_true', help='enable log')
493    parser.add_argument('--num-cores', type=int, help='number of cores')
494    # makefile arguments
495    parser.add_argument('--release', action='store_true', help='enable release')
496    parser.add_argument('--spike', action='store_true', help='enable spike diff')
497    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
498    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
499    parser.add_argument('--trace', action='store_true', help='enable vcd waveform')
500    parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform')
501    parser.add_argument('--config', nargs='?', type=str, help='config')
502    parser.add_argument('--mfc', action='store_true', help='use mfc')
503    parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter')
504    parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs')
505    # emu arguments
506    parser.add_argument('--numa', action='store_true', help='use numactl')
507    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
508    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
509    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
510    parser.add_argument('--no-diff', action='store_true', help='disable difftest')
511    parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
512    # both makefile and emu arguments
513    parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump')
514
515    args = parser.parse_args()
516
517    xs = XiangShan(args)
518    ret = xs.run(args)
519
520    sys.exit(ret)
521