xref: /XiangShan/scripts/xiangshan.py (revision bb2f3f51dd67f6e16e0cc1ffe43368c9fc7e4aef)
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
28import shlex
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        self.pgo = args.pgo
98        self.pgo_max_cycle = args.pgo_max_cycle
99        self.pgo_emu_args = args.pgo_emu_args
100        self.llvm_profdata = args.llvm_profdata
101        # wave dump path
102        if args.wave_dump is not None:
103            self.set_wave_home(args.wave_dump)
104        else:
105            self.set_wave_home(self.default_wave_home)
106
107    def get_env_variables(self):
108        all_env = {
109            "NOOP_HOME"    : self.noop_home,
110            "NEMU_HOME"    : self.nemu_home,
111            "WAVE_HOME"    : self.wave_home,
112            "AM_HOME"      : self.am_home,
113            "DRAMSIM3_HOME": self.dramsim3_home,
114            "MODULEPATH": "/usr/share/Modules/modulefiles:/etc/modulefiles"
115        }
116        return all_env
117
118    def get_chisel_args(self, prefix=None):
119        chisel_args = [
120            (self.enable_log, "enable-log")
121        ]
122        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
123        if prefix is not None:
124            args = map(lambda x: prefix + x, args)
125        return args
126
127    def get_makefile_args(self):
128        makefile_args = [
129            (self.threads,       "EMU_THREADS"),
130            (self.with_dramsim3, "WITH_DRAMSIM3"),
131            (self.is_release,    "RELEASE"),
132            (self.is_spike,      "REF"),
133            (self.trace,         "EMU_TRACE"),
134            (self.trace_fst,     "EMU_TRACE"),
135            (self.config,        "CONFIG"),
136            (self.num_cores,     "NUM_CORES"),
137            (self.is_mfc,        "MFC"),
138            (self.emu_optimize,  "EMU_OPTIMIZE"),
139            (self.xprop,         "ENABLE_XPROP"),
140            (self.with_chiseldb, "WITH_CHISELDB"),
141            (self.pgo,           "PGO_WORKLOAD"),
142            (self.pgo_max_cycle, "PGO_MAX_CYCLE"),
143            (self.pgo_emu_args,  "PGO_EMU_ARGS"),
144            (self.llvm_profdata, "LLVM_PROFDATA"),
145        ]
146        args = filter(lambda arg: arg[0] is not None, makefile_args)
147        args = [(shlex.quote(str(arg[0])), arg[1]) for arg in args] # shell escape
148        return args
149
150    def get_emu_args(self):
151        emu_args = [
152            (self.max_instr, "max-instr"),
153            (self.diff,      "diff"),
154            (self.seed,      "seed"),
155            (self.ram_size,  "ram-size"),
156        ]
157        args = filter(lambda arg: arg[0] is not None, emu_args)
158        return args
159
160    def show(self):
161        print("Extra environment variables:")
162        env = self.get_env_variables()
163        for env_name in env:
164            print(f"{env_name}: {env[env_name]}")
165        print()
166        print("Chisel arguments:")
167        print(" ".join(self.get_chisel_args()))
168        print()
169        print("Makefile arguments:")
170        for val, name in self.get_makefile_args():
171            print(f"{name}={val}")
172        print()
173        print("emu arguments:")
174        for val, name in self.get_emu_args():
175            print(f"--{name} {val}")
176        print()
177
178    def __extract_path(self, path, env=None, default=None):
179        if path is None and env is not None:
180            path = os.getenv(env)
181        if path is None and default is not None:
182            path = default
183        path = os.path.realpath(path)
184        return path
185
186    def set_noop_home(self, path):
187        self.noop_home = path
188
189    def set_nemu_home(self, path):
190        self.nemu_home = path
191
192    def set_am_home(self, path):
193        self.am_home = path
194
195    def set_dramsim3_home(self, path):
196        self.dramsim3_home = path
197
198    def set_rvtest_home(self, path):
199        self.rvtest_home = path
200
201    def set_wave_home(self, path):
202        print(f"set wave home to {path}")
203        self.wave_home = path
204
205# XiangShan environment
206class XiangShan(object):
207    def __init__(self, args):
208        self.args = XSArgs(args)
209        self.timeout = args.timeout
210
211    def show(self):
212        self.args.show()
213
214    def make_clean(self):
215        print("Clean up CI workspace")
216        self.show()
217        return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean')
218        return return_code
219
220    def generate_verilog(self):
221        print("Generating XiangShan verilog with the following configurations:")
222        self.show()
223        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
224        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
225        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
226        return return_code
227
228    def generate_sim_verilog(self):
229        print("Generating XiangShan sim-verilog with the following configurations:")
230        self.show()
231        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
232        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
233        return_code = self.__exec_cmd(f'make -C $NOOP_HOME sim-verilog SIM_ARGS="{sim_args}" {make_args}')
234        return return_code
235
236    def build_emu(self):
237        print("Building XiangShan emu with the following configurations:")
238        self.show()
239        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
240        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
241        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
242        return return_code
243
244    def build_simv(self):
245        print("Building XiangShan simv with the following configurations")
246        self.show()
247        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
248        # TODO: make the following commands grouped as unseen scripts
249        return_code = self.__exec_cmd(f'\
250            eval `/usr/bin/modulecmd zsh load license`;\
251            eval `/usr/bin/modulecmd zsh load synopsys/vcs/Q-2020.03-SP2`;\
252            eval `/usr/bin/modulecmd zsh load synopsys/verdi/S-2021.09-SP1`;\
253            VERDI_HOME=/nfs/tools/synopsys/verdi/S-2021.09-SP1 \
254            make -C $NOOP_HOME simv {make_args} CONSIDER_FSDB=1')  # set CONSIDER_FSDB for compatibility
255        return return_code
256
257    def run_emu(self, workload):
258        print("Running XiangShan emu with the following configurations:")
259        self.show()
260        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
261        print("workload:", workload)
262        numa_args = ""
263        if self.args.numa:
264            numa_info = get_free_cores(self.args.threads)
265            numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}"
266        fork_args = "--enable-fork" if self.args.fork else ""
267        diff_args = "--no-diff" if self.args.disable_diff else ""
268        chiseldb_args = "--dump-db" if not self.args.disable_db else ""
269        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}')
270        return return_code
271
272    def run_simv(self, workload):
273        print("Running XiangShan simv with the following configurations:")
274        self.show()
275        diff_args = "$NOOP_HOME/"+ args.diff
276        assert_args = "-assert finish_maxfail=30 -assert global_finish_maxfail=10000"
277        self.__exec_cmd(f'cd $NOOP_HOME/build && ./simv +workload={workload} +diff={diff_args} +dump-wave=fsdb {assert_args} | tee simv.log')
278        with open(f"{self.args.noop_home}/build/simv.log") as f:
279            if "HIT GOOD TRAP" in f.read():
280                return 0
281        return 1
282
283    def run(self, args):
284        if args.ci is not None:
285            return self.run_ci(args.ci)
286        if args.ci_vcs is not None:
287            return self.run_ci_vcs(args.ci_vcs)
288        actions = [
289            (args.generate, lambda _ : self.generate_verilog()),
290            (args.vcs_gen, lambda _ : self.generate_sim_verilog()),
291            (args.build, lambda _ : self.build_emu()),
292            (args.vcs_build, lambda _ : self.build_simv()),
293            (args.workload, lambda args: self.run_emu(args.workload)),
294            (args.clean, lambda _ : self.make_clean())
295        ]
296        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
297        for i, action in enumerate(valid_actions):
298            print(f"Action {i}:")
299            ret = action(args)
300            if ret:
301                return ret
302        return 0
303
304    def __exec_cmd(self, cmd):
305        env = dict(os.environ)
306        env.update(self.args.get_env_variables())
307        print("subprocess call cmd:", cmd)
308        start = time.time()
309        proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid)
310        try:
311            return_code = proc.wait(self.timeout)
312            end = time.time()
313            print(f"Elapsed time: {end - start} seconds")
314            return return_code
315        except (KeyboardInterrupt, subprocess.TimeoutExpired):
316            os.killpg(os.getpgid(proc.pid), signal.SIGINT)
317            print(f"KeyboardInterrupt or TimeoutExpired.")
318            return 0
319
320    def __get_ci_cputest(self, name=None):
321        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
322        cputest = os.listdir(base_dir)
323        cputest = filter(lambda x: x.endswith(".bin"), cputest)
324        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
325        return cputest
326
327    def __get_ci_rvtest(self, name=None):
328        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
329        riscv_tests = os.listdir(base_dir)
330        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
331        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
332        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
333        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
334        return riscv_tests
335
336    def __get_ci_misc(self, name=None):
337        base_dir = "/nfs/home/share/ci-workloads"
338        workloads = [
339            "bitmanip/bitMisc.bin",
340            "crypto/crypto-riscv64-noop.bin",
341            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
342            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
343            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
344            "ext_intr/amtest-riscv64-xs.bin",
345            "cache-alias/aliastest-riscv64-xs.bin",
346            "Svinval/rv64mi-p-svinval.bin",
347            "pmp/pmp.riscv.bin",
348            "pmp/pmp-am/amtest-riscv64-xs.bin",
349            "pmp/hugepage-pmp-atom/amtest-riscv64-xs.bin",
350            "asid/asid.bin",
351            "isa_misc/xret_clear_mprv.bin",
352            "isa_misc/satp_ppn.bin",
353            "cache-management/softprefetchtest-riscv64-xs.bin"
354        ]
355        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
356        return misc_tests
357
358    def __get_ci_mc(self, name=None):
359        base_dir = "/nfs/home/share/ci-workloads"
360        workloads = [
361            "dualcoretest/ldvio-riscv64-xs.bin"
362        ]
363        mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
364        return mc_tests
365
366    def __get_ci_nodiff(self, name=None):
367        base_dir = "/nfs/home/share/ci-workloads"
368        workloads = [
369            "cache-management/cacheoptest-riscv64-xs.bin"
370        ]
371        tests = map(lambda x: os.path.join(base_dir, x), workloads)
372        return tests
373
374    def __am_apps_path(self, bench):
375        filename = f"{bench}-riscv64-noop.bin"
376        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
377
378    def __get_ci_workloads(self, name):
379        workloads = {
380            "linux-hello": "bbl.bin",
381            "linux-hello-smp": "bbl.bin",
382            "linux-hello-opensbi": "fw_payload.bin",
383            "linux-hello-smp-opensbi": "fw_payload.bin",
384            "linux-hello-new": "bbl.bin",
385            "linux-hello-smp-new": "bbl.bin",
386            "povray": "_700480000000_.gz",
387            "mcf": "_17520000000_.gz",
388            "xalancbmk": "_266100000000_.gz",
389            "gcc": "_39720000000_.gz",
390            "namd": "_434640000000_.gz",
391            "milc": "_103620000000_.gz",
392            "lbm": "_140840000000_.gz",
393            "gromacs": "_275480000000_.gz",
394            "wrf": "_1916220000000_.gz",
395            "astar": "_122060000000_.gz"
396        }
397        if name in workloads:
398            return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
399        # select a random SPEC checkpoint
400        assert(name == "random")
401        all_cpt = [
402            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
403            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
404            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
405            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
406            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
407            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
408            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
409            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
410        ]
411        all_json = [
412            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
413            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
414            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
415            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
416            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
417            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
418            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
419            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
420        ]
421        assert(len(all_cpt) == len(all_json))
422        cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
423        all_gcpt = load_all_gcpt(cpt_path, json_path)
424        return [random.choice(all_gcpt)]
425
426    def run_ci(self, test):
427        all_tests = {
428            "cputest": self.__get_ci_cputest,
429            "riscv-tests": self.__get_ci_rvtest,
430            "misc-tests": self.__get_ci_misc,
431            "mc-tests": self.__get_ci_mc,
432            "nodiff-tests": self.__get_ci_nodiff,
433            "microbench": self.__am_apps_path,
434            "coremark": self.__am_apps_path
435        }
436        for target in all_tests.get(test, self.__get_ci_workloads)(test):
437            print(target)
438            ret = self.run_emu(target)
439            if ret:
440                if self.args.default_wave_home != self.args.wave_home:
441                    print("copy wave file to " + self.args.wave_home)
442                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
443                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
444                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
445                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
446                return ret
447        return 0
448
449    def run_ci_vcs(self, test):
450        all_tests = {
451            "cputest": self.__get_ci_cputest,
452            "riscv-tests": self.__get_ci_rvtest,
453            "misc-tests": self.__get_ci_misc,
454            "mc-tests": self.__get_ci_mc,
455            "nodiff-tests": self.__get_ci_nodiff,
456            "microbench": self.__am_apps_path,
457            "coremark": self.__am_apps_path
458        }
459        for target in all_tests.get(test, self.__get_ci_workloads)(test):
460            print(target)
461            ret = self.run_simv(target)
462            if ret:
463                if self.args.default_wave_home != self.args.wave_home:
464                    print("copy wave file to " + self.args.wave_home)
465                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME")
466                    self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME")
467                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
468                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
469                return ret
470        return 0
471
472def get_free_cores(n):
473    while True:
474        # To avoid potential conflicts, we allow CI to use SMT.
475        num_logical_core = psutil.cpu_count(logical=False)
476        core_usage = psutil.cpu_percent(interval=1, percpu=True)
477        num_window = num_logical_core // n
478        for i in range(num_window):
479            window_usage = core_usage[i * n : i * n + n]
480            if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage):
481                return (((i * n) % 128)// 64, i * n, i * n + n - 1)
482        print(f"No free {n} cores found. CPU usage: {core_usage}\n")
483
484if __name__ == "__main__":
485    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
486    parser.add_argument('workload', nargs='?', type=str, default="",
487                        help='input workload file in binary format')
488    # actions
489    parser.add_argument('--build', action='store_true', help='build XS emu')
490    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
491    parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs')
492    parser.add_argument('--vcs-build', action='store_true', help='build XS simv')
493    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
494    parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv')
495    parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
496    parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
497    # environment variables
498    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
499    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
500    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
501    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
502    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
503    # chisel arguments
504    parser.add_argument('--enable-log', action='store_true', help='enable log')
505    parser.add_argument('--num-cores', type=int, help='number of cores')
506    # makefile arguments
507    parser.add_argument('--release', action='store_true', help='enable release')
508    parser.add_argument('--spike', action='store_true', help='enable spike diff')
509    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
510    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
511    parser.add_argument('--trace', action='store_true', help='enable vcd waveform')
512    parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform')
513    parser.add_argument('--config', nargs='?', type=str, help='config')
514    parser.add_argument('--mfc', action='store_true', help='use mfc')
515    parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter')
516    parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs')
517    # emu arguments
518    parser.add_argument('--numa', action='store_true', help='use numactl')
519    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
520    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
521    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
522    parser.add_argument('--no-diff', action='store_true', help='disable difftest')
523    parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
524    # both makefile and emu arguments
525    parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump')
526    parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)')
527    parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo')
528    parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo')
529    parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC')
530
531    args = parser.parse_args()
532
533    xs = XiangShan(args)
534    ret = xs.run(args)
535
536    sys.exit(ret)
537