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