xref: /XiangShan/scripts/xiangshan.py (revision dd720cae57979dc858f1162f0044b13d1ceb5495)
1c6d43980SLemover#***************************************************************************************
254cc3a06STang Haojin# Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
354cc3a06STang Haojin# Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences
4f320e0f0SYinan Xu# Copyright (c) 2020-2021 Peng Cheng Laboratory
5c6d43980SLemover#
6c6d43980SLemover# XiangShan is licensed under Mulan PSL v2.
7c6d43980SLemover# You can use this software according to the terms and conditions of the Mulan PSL v2.
8c6d43980SLemover# You may obtain a copy of Mulan PSL v2 at:
9c6d43980SLemover#          http://license.coscl.org.cn/MulanPSL2
10c6d43980SLemover#
11c6d43980SLemover# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12c6d43980SLemover# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13c6d43980SLemover# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14c6d43980SLemover#
15c6d43980SLemover# See the Mulan PSL v2 for more details.
16c6d43980SLemover#***************************************************************************************
17c6d43980SLemover
18c11a4d2cSYinan Xu# Simple version of xiangshan python wrapper
19c11a4d2cSYinan Xu
20c11a4d2cSYinan Xuimport argparse
219f32a80dSYinan Xuimport json
2259bcbb59SYinan Xuimport os
2359bcbb59SYinan Xuimport random
249f32a80dSYinan Xuimport signal
25c11a4d2cSYinan Xuimport subprocess
2659bcbb59SYinan Xuimport sys
275ef7374fSLi Qianruoimport time
289810c04aSYangyu Chenimport shlex
2994e266cbSYinan Xuimport psutil
30a941bfc4STang Haojinimport re
31c11a4d2cSYinan Xu
329f32a80dSYinan Xudef load_all_gcpt(gcpt_path, json_path):
339f32a80dSYinan Xu    all_gcpt = []
349f32a80dSYinan Xu    with open(json_path) as f:
359f32a80dSYinan Xu        data = json.load(f)
369f32a80dSYinan Xu    for benchspec in data:
379f32a80dSYinan Xu        for point in data[benchspec]:
389f32a80dSYinan Xu            weight = data[benchspec][point]
399f32a80dSYinan Xu            gcpt = os.path.join(gcpt_path, "_".join([benchspec, point, weight]))
409f32a80dSYinan Xu            bin_dir = os.path.join(gcpt, "0")
419f32a80dSYinan Xu            bin_file = list(os.listdir(bin_dir))
429f32a80dSYinan Xu            assert(len(bin_file) == 1)
439f32a80dSYinan Xu            bin_path = os.path.join(bin_dir, bin_file[0])
449f32a80dSYinan Xu            assert(os.path.isfile(bin_path))
459f32a80dSYinan Xu            all_gcpt.append(bin_path)
469f32a80dSYinan Xu    return all_gcpt
479f32a80dSYinan Xu
48c11a4d2cSYinan Xuclass XSArgs(object):
49c11a4d2cSYinan Xu    script_path = os.path.realpath(__file__)
50c11a4d2cSYinan Xu    # default path to the repositories
51c11a4d2cSYinan Xu    noop_home = os.path.join(os.path.dirname(script_path), "..")
52c11a4d2cSYinan Xu    nemu_home = os.path.join(noop_home, "../NEMU")
53c11a4d2cSYinan Xu    am_home = os.path.join(noop_home, "../nexus-am")
54c11a4d2cSYinan Xu    dramsim3_home = os.path.join(noop_home, "../DRAMsim3")
55c11a4d2cSYinan Xu    rvtest_home = os.path.join(noop_home, "../riscv-tests")
5624e2eab6SJinYue    default_wave_home = os.path.join(noop_home, "build")
5724e2eab6SJinYue    wave_home   = default_wave_home
58c11a4d2cSYinan Xu
59c11a4d2cSYinan Xu    def __init__(self, args):
60c11a4d2cSYinan Xu        # all path environment variables that should be set
61c11a4d2cSYinan Xu        all_path = [
62c11a4d2cSYinan Xu            # (python argument, environment variable, default, target function)
63c11a4d2cSYinan Xu            (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
64c11a4d2cSYinan Xu            (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
65c11a4d2cSYinan Xu            (args.am, "AM_HOME", self.am_home, self.set_am_home),
66c11a4d2cSYinan Xu            (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
67c11a4d2cSYinan Xu            (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
68c11a4d2cSYinan Xu        ]
69c11a4d2cSYinan Xu        for (arg_in, env, default, set_func) in all_path:
70c11a4d2cSYinan Xu            set_func(self.__extract_path(arg_in, env, default))
71c11a4d2cSYinan Xu        # Chisel arguments
721545277aSYinan Xu        self.enable_log = args.enable_log
735ef7374fSLi Qianruo        self.num_cores = args.num_cores
74c11a4d2cSYinan Xu        # Makefile arguments
75c11a4d2cSYinan Xu        self.threads = args.threads
76c11a4d2cSYinan Xu        self.with_dramsim3 = 1 if args.with_dramsim3 else None
771545277aSYinan Xu        self.is_release = 1 if args.release else None
787d45a146SYinan Xu        self.is_spike = "Spike" if args.spike else None
7940f31726Sgood-circle        self.trace = 1 if args.trace or not args.disable_fork and not args.trace_fst else None
8040f31726Sgood-circle        self.trace_fst = "fst" if args.trace_fst else None
816c0058d3SYinan Xu        self.config = args.config
82453674e0STang Haojin        self.emu_optimize = args.emu_optimize
8354cc3a06STang Haojin        self.xprop = 1 if args.xprop else None
8454cc3a06STang Haojin        self.with_chiseldb = 0 if args.no_db else None
85c11a4d2cSYinan Xu        # emu arguments
86c11a4d2cSYinan Xu        self.max_instr = args.max_instr
8725ac26c6SWilliam Wang        self.ram_size = args.ram_size
8859bcbb59SYinan Xu        self.seed = random.randint(0, 9999)
89c11a4d2cSYinan Xu        self.numa = args.numa
90f9930da0SYinan Xu        self.diff = args.diff
9138e9143dSYinan Xu        if args.spike and "nemu" in args.diff:
9238e9143dSYinan Xu            self.diff = self.diff.replace("nemu-interpreter", "spike")
93078fde2bSJinYue        self.fork = not args.disable_fork
949c297294SWilliam Wang        self.disable_diff = args.no_diff
95e3cd2c1fSwakafa        self.disable_db = args.no_db
96bc247239SXuan Hu        self.gcpt_restore_bin = args.gcpt_restore_bin
979810c04aSYangyu Chen        self.pgo = args.pgo
989810c04aSYangyu Chen        self.pgo_max_cycle = args.pgo_max_cycle
999810c04aSYangyu Chen        self.pgo_emu_args = args.pgo_emu_args
1009810c04aSYangyu Chen        self.llvm_profdata = args.llvm_profdata
10124e2eab6SJinYue        # wave dump path
10224e2eab6SJinYue        if args.wave_dump is not None:
10324e2eab6SJinYue            self.set_wave_home(args.wave_dump)
10424e2eab6SJinYue        else:
10524e2eab6SJinYue            self.set_wave_home(self.default_wave_home)
106c11a4d2cSYinan Xu
107c11a4d2cSYinan Xu    def get_env_variables(self):
108c11a4d2cSYinan Xu        all_env = {
109c11a4d2cSYinan Xu            "NOOP_HOME"    : self.noop_home,
110c11a4d2cSYinan Xu            "NEMU_HOME"    : self.nemu_home,
11124e2eab6SJinYue            "WAVE_HOME"    : self.wave_home,
112c11a4d2cSYinan Xu            "AM_HOME"      : self.am_home,
113e5597226Swakafa            "DRAMSIM3_HOME": self.dramsim3_home,
114e5597226Swakafa            "MODULEPATH": "/usr/share/Modules/modulefiles:/etc/modulefiles"
115c11a4d2cSYinan Xu        }
116c11a4d2cSYinan Xu        return all_env
117c11a4d2cSYinan Xu
118c11a4d2cSYinan Xu    def get_chisel_args(self, prefix=None):
119d3126fd3STang Haojin        chisel_args = [
1201545277aSYinan Xu            (self.enable_log, "enable-log")
121c11a4d2cSYinan Xu        ]
122d3126fd3STang Haojin        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
123c11a4d2cSYinan Xu        if prefix is not None:
124c11a4d2cSYinan Xu            args = map(lambda x: prefix + x, args)
125c11a4d2cSYinan Xu        return args
126c11a4d2cSYinan Xu
127c11a4d2cSYinan Xu    def get_makefile_args(self):
128c11a4d2cSYinan Xu        makefile_args = [
129c11a4d2cSYinan Xu            (self.threads,       "EMU_THREADS"),
130c11a4d2cSYinan Xu            (self.with_dramsim3, "WITH_DRAMSIM3"),
1311545277aSYinan Xu            (self.is_release,    "RELEASE"),
13238e9143dSYinan Xu            (self.is_spike,      "REF"),
1336c0058d3SYinan Xu            (self.trace,         "EMU_TRACE"),
13440f31726Sgood-circle            (self.trace_fst,     "EMU_TRACE"),
1355ef7374fSLi Qianruo            (self.config,        "CONFIG"),
136084afb77STang Haojin            (self.num_cores,     "NUM_CORES"),
13754cc3a06STang Haojin            (self.emu_optimize,  "EMU_OPTIMIZE"),
13854cc3a06STang Haojin            (self.xprop,         "ENABLE_XPROP"),
1399810c04aSYangyu Chen            (self.with_chiseldb, "WITH_CHISELDB"),
1409810c04aSYangyu Chen            (self.pgo,           "PGO_WORKLOAD"),
1419810c04aSYangyu Chen            (self.pgo_max_cycle, "PGO_MAX_CYCLE"),
1429810c04aSYangyu Chen            (self.pgo_emu_args,  "PGO_EMU_ARGS"),
1439810c04aSYangyu Chen            (self.llvm_profdata, "LLVM_PROFDATA"),
144c11a4d2cSYinan Xu        ]
145c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, makefile_args)
1469810c04aSYangyu Chen        args = [(shlex.quote(str(arg[0])), arg[1]) for arg in args] # shell escape
147c11a4d2cSYinan Xu        return args
148c11a4d2cSYinan Xu
149c11a4d2cSYinan Xu    def get_emu_args(self):
150c11a4d2cSYinan Xu        emu_args = [
15159bcbb59SYinan Xu            (self.max_instr, "max-instr"),
152f9930da0SYinan Xu            (self.diff,      "diff"),
15325ac26c6SWilliam Wang            (self.seed,      "seed"),
15425ac26c6SWilliam Wang            (self.ram_size,  "ram-size"),
155c11a4d2cSYinan Xu        ]
156c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, emu_args)
157c11a4d2cSYinan Xu        return args
158c11a4d2cSYinan Xu
159c11a4d2cSYinan Xu    def show(self):
160c11a4d2cSYinan Xu        print("Extra environment variables:")
161c11a4d2cSYinan Xu        env = self.get_env_variables()
162c11a4d2cSYinan Xu        for env_name in env:
163c11a4d2cSYinan Xu            print(f"{env_name}: {env[env_name]}")
164c11a4d2cSYinan Xu        print()
165c11a4d2cSYinan Xu        print("Chisel arguments:")
166c11a4d2cSYinan Xu        print(" ".join(self.get_chisel_args()))
167c11a4d2cSYinan Xu        print()
168c11a4d2cSYinan Xu        print("Makefile arguments:")
169c11a4d2cSYinan Xu        for val, name in self.get_makefile_args():
170c11a4d2cSYinan Xu            print(f"{name}={val}")
171c11a4d2cSYinan Xu        print()
172c11a4d2cSYinan Xu        print("emu arguments:")
173c11a4d2cSYinan Xu        for val, name in self.get_emu_args():
174c11a4d2cSYinan Xu            print(f"--{name} {val}")
175c11a4d2cSYinan Xu        print()
176c11a4d2cSYinan Xu
177c11a4d2cSYinan Xu    def __extract_path(self, path, env=None, default=None):
178c11a4d2cSYinan Xu        if path is None and env is not None:
179c11a4d2cSYinan Xu            path = os.getenv(env)
180c11a4d2cSYinan Xu        if path is None and default is not None:
181c11a4d2cSYinan Xu            path = default
182c11a4d2cSYinan Xu        path = os.path.realpath(path)
183c11a4d2cSYinan Xu        return path
184c11a4d2cSYinan Xu
185c11a4d2cSYinan Xu    def set_noop_home(self, path):
186c11a4d2cSYinan Xu        self.noop_home = path
187c11a4d2cSYinan Xu
188c11a4d2cSYinan Xu    def set_nemu_home(self, path):
189c11a4d2cSYinan Xu        self.nemu_home = path
190c11a4d2cSYinan Xu
191c11a4d2cSYinan Xu    def set_am_home(self, path):
192c11a4d2cSYinan Xu        self.am_home = path
193c11a4d2cSYinan Xu
194c11a4d2cSYinan Xu    def set_dramsim3_home(self, path):
195c11a4d2cSYinan Xu        self.dramsim3_home = path
196c11a4d2cSYinan Xu
197c11a4d2cSYinan Xu    def set_rvtest_home(self, path):
198c11a4d2cSYinan Xu        self.rvtest_home = path
199c11a4d2cSYinan Xu
20024e2eab6SJinYue    def set_wave_home(self, path):
20124e2eab6SJinYue        print(f"set wave home to {path}")
20224e2eab6SJinYue        self.wave_home = path
20324e2eab6SJinYue
204c11a4d2cSYinan Xu# XiangShan environment
205c11a4d2cSYinan Xuclass XiangShan(object):
206c11a4d2cSYinan Xu    def __init__(self, args):
207c11a4d2cSYinan Xu        self.args = XSArgs(args)
2089f32a80dSYinan Xu        self.timeout = args.timeout
209c11a4d2cSYinan Xu
210c11a4d2cSYinan Xu    def show(self):
211c11a4d2cSYinan Xu        self.args.show()
212c11a4d2cSYinan Xu
213ef3b5b96SWilliam Wang    def make_clean(self):
214ef3b5b96SWilliam Wang        print("Clean up CI workspace")
215ef3b5b96SWilliam Wang        self.show()
216ef3b5b96SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean')
217ef3b5b96SWilliam Wang        return return_code
218ef3b5b96SWilliam Wang
219c11a4d2cSYinan Xu    def generate_verilog(self):
220c11a4d2cSYinan Xu        print("Generating XiangShan verilog with the following configurations:")
221c11a4d2cSYinan Xu        self.show()
222c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
223c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
224c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
225c11a4d2cSYinan Xu        return return_code
226c11a4d2cSYinan Xu
227e5597226Swakafa    def generate_sim_verilog(self):
228e5597226Swakafa        print("Generating XiangShan sim-verilog with the following configurations:")
229e5597226Swakafa        self.show()
230e5597226Swakafa        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
231e5597226Swakafa        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
232e5597226Swakafa        return_code = self.__exec_cmd(f'make -C $NOOP_HOME sim-verilog SIM_ARGS="{sim_args}" {make_args}')
233e5597226Swakafa        return return_code
234e5597226Swakafa
235c11a4d2cSYinan Xu    def build_emu(self):
236c11a4d2cSYinan Xu        print("Building XiangShan emu with the following configurations:")
237c11a4d2cSYinan Xu        self.show()
238c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
239c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
240a3e87608SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
241c11a4d2cSYinan Xu        return return_code
242c11a4d2cSYinan Xu
243e5597226Swakafa    def build_simv(self):
244e5597226Swakafa        print("Building XiangShan simv with the following configurations")
245e5597226Swakafa        self.show()
246e5597226Swakafa        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
247e5597226Swakafa        # TODO: make the following commands grouped as unseen scripts
248e5597226Swakafa        return_code = self.__exec_cmd(f'\
249e5597226Swakafa            eval `/usr/bin/modulecmd zsh load license`;\
250e5597226Swakafa            eval `/usr/bin/modulecmd zsh load synopsys/vcs/Q-2020.03-SP2`;\
251e5597226Swakafa            eval `/usr/bin/modulecmd zsh load synopsys/verdi/S-2021.09-SP1`;\
252e5597226Swakafa            VERDI_HOME=/nfs/tools/synopsys/verdi/S-2021.09-SP1 \
253e5597226Swakafa            make -C $NOOP_HOME simv {make_args} CONSIDER_FSDB=1')  # set CONSIDER_FSDB for compatibility
254e5597226Swakafa        return return_code
255e5597226Swakafa
256c11a4d2cSYinan Xu    def run_emu(self, workload):
257c11a4d2cSYinan Xu        print("Running XiangShan emu with the following configurations:")
258c11a4d2cSYinan Xu        self.show()
259c11a4d2cSYinan Xu        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
260c11a4d2cSYinan Xu        print("workload:", workload)
261c9d90c8dSYinan Xu        numa_args = ""
262c9d90c8dSYinan Xu        if self.args.numa:
26394e266cbSYinan Xu            numa_info = get_free_cores(self.args.threads)
264c9d90c8dSYinan Xu            numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}"
265078fde2bSJinYue        fork_args = "--enable-fork" if self.args.fork else ""
2669c297294SWilliam Wang        diff_args = "--no-diff" if self.args.disable_diff else ""
267e3cd2c1fSwakafa        chiseldb_args = "--dump-db" if not self.args.disable_db else ""
268bc247239SXuan Hu        gcpt_restore_args = f"-r {self.args.gcpt_restore_bin}" if len(self.args.gcpt_restore_bin) != 0 else ""
269bc247239SXuan Hu        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}')
270c11a4d2cSYinan Xu        return return_code
271c11a4d2cSYinan Xu
272e5597226Swakafa    def run_simv(self, workload):
273e5597226Swakafa        print("Running XiangShan simv with the following configurations:")
274e5597226Swakafa        self.show()
275e5597226Swakafa        diff_args = "$NOOP_HOME/"+ args.diff
27654cc3a06STang Haojin        assert_args = "-assert finish_maxfail=30 -assert global_finish_maxfail=10000"
277ae0295f4STang Haojin        return_code = self.__exec_cmd(f'cd $NOOP_HOME/build && ./simv +workload={workload} +diff={diff_args} +dump-wave=fsdb {assert_args} | tee simv.log')
278823787d8STang Haojin        with open(f"{self.args.noop_home}/build/simv.log") as f:
279ae0295f4STang Haojin            content = f.read()
280ae0295f4STang Haojin            if "Offending" in content or "HIT GOOD TRAP" not in content:
281823787d8STang Haojin                return 1
282ae0295f4STang Haojin        return return_code
283e5597226Swakafa
284c11a4d2cSYinan Xu    def run(self, args):
285c11a4d2cSYinan Xu        if args.ci is not None:
286c11a4d2cSYinan Xu            return self.run_ci(args.ci)
287e5597226Swakafa        if args.ci_vcs is not None:
288e5597226Swakafa            return self.run_ci_vcs(args.ci_vcs)
289c11a4d2cSYinan Xu        actions = [
290c11a4d2cSYinan Xu            (args.generate, lambda _ : self.generate_verilog()),
291e5597226Swakafa            (args.vcs_gen, lambda _ : self.generate_sim_verilog()),
292c11a4d2cSYinan Xu            (args.build, lambda _ : self.build_emu()),
293e5597226Swakafa            (args.vcs_build, lambda _ : self.build_simv()),
294ef3b5b96SWilliam Wang            (args.workload, lambda args: self.run_emu(args.workload)),
295ef3b5b96SWilliam Wang            (args.clean, lambda _ : self.make_clean())
296c11a4d2cSYinan Xu        ]
297c11a4d2cSYinan Xu        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
298c11a4d2cSYinan Xu        for i, action in enumerate(valid_actions):
299c11a4d2cSYinan Xu            print(f"Action {i}:")
300c11a4d2cSYinan Xu            ret = action(args)
301c11a4d2cSYinan Xu            if ret:
302c11a4d2cSYinan Xu                return ret
303c11a4d2cSYinan Xu        return 0
304c11a4d2cSYinan Xu
305c11a4d2cSYinan Xu    def __exec_cmd(self, cmd):
306c11a4d2cSYinan Xu        env = dict(os.environ)
307c11a4d2cSYinan Xu        env.update(self.args.get_env_variables())
308c11a4d2cSYinan Xu        print("subprocess call cmd:", cmd)
3095ef7374fSLi Qianruo        start = time.time()
3109f32a80dSYinan Xu        proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid)
3119f32a80dSYinan Xu        try:
3129f32a80dSYinan Xu            return_code = proc.wait(self.timeout)
3135ef7374fSLi Qianruo            end = time.time()
3145ef7374fSLi Qianruo            print(f"Elapsed time: {end - start} seconds")
315c11a4d2cSYinan Xu            return return_code
3169f32a80dSYinan Xu        except (KeyboardInterrupt, subprocess.TimeoutExpired):
3179f32a80dSYinan Xu            os.killpg(os.getpgid(proc.pid), signal.SIGINT)
3189f32a80dSYinan Xu            print(f"KeyboardInterrupt or TimeoutExpired.")
3199f32a80dSYinan Xu            return 0
320c11a4d2cSYinan Xu
321c11a4d2cSYinan Xu    def __get_ci_cputest(self, name=None):
322c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
323c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
324c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
325c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
326c11a4d2cSYinan Xu        return cputest
327c11a4d2cSYinan Xu
328c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
329c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
330c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
331c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
332c11a4d2cSYinan Xu        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
333c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
334c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
335c11a4d2cSYinan Xu        return riscv_tests
336c11a4d2cSYinan Xu
337675acc68SYinan Xu    def __get_ci_misc(self, name=None):
338da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
339675acc68SYinan Xu        workloads = [
340675acc68SYinan Xu            "bitmanip/bitMisc.bin",
3413feeca58Szfw            "crypto/crypto-riscv64-noop.bin",
342675acc68SYinan Xu            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
343675acc68SYinan Xu            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
34464a887e0SYinan Xu            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
34535620aa8Swakafa            "ext_intr/amtest-riscv64-xs.bin",
3463f4ec46fSCODE-JTZ            "cache-alias/aliastest-riscv64-xs.bin",
347af2f7849Shappy-lx            "Svinval/rv64mi-p-svinval.bin",
348b6982e83SLemover            "pmp/pmp.riscv.bin",
349b4edc553SWilliam Wang            "pmp/pmp-am/amtest-riscv64-xs.bin",
350b4edc553SWilliam Wang            "pmp/hugepage-pmp-atom/amtest-riscv64-xs.bin",
35145f497a4Shappy-lx            "asid/asid.bin",
3527d9edc86SLemover            "isa_misc/xret_clear_mprv.bin",
353705cbec3SLemover            "isa_misc/satp_ppn.bin",
3549c297294SWilliam Wang            "cache-management/softprefetchtest-riscv64-xs.bin"
355675acc68SYinan Xu        ]
356675acc68SYinan Xu        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
357675acc68SYinan Xu        return misc_tests
358675acc68SYinan Xu
3591f903014SXu, Zefan    def __get_ci_rvhtest(self, name=None):
3601f903014SXu, Zefan        base_dir = "/nfs/home/share/ci-workloads/H-extension-tests"
3611f903014SXu, Zefan        workloads = [
3621f903014SXu, Zefan            # "riscv-hyp-tests/rvh_test.bin",
3631f903014SXu, Zefan            "xvisor_wboxtest/checkpoint.gz",
3641f903014SXu, Zefan        ]
3651f903014SXu, Zefan        rvh_tests = map(lambda x: os.path.join(base_dir, x), workloads)
3661f903014SXu, Zefan        return rvh_tests
3671f903014SXu, Zefan
3686ebd27e9Slwd    def __get_ci_rvvbench(self, name=None):
3696ebd27e9Slwd        base_dir = "/nfs/home/share/ci-workloads"
3706ebd27e9Slwd        workloads = [
3716ebd27e9Slwd            "rvv-bench/poly1305.bin",
3726ebd27e9Slwd            "rvv-bench/mergelines.bin"
3736ebd27e9Slwd        ]
3746ebd27e9Slwd        rvvbench = map(lambda x: os.path.join(base_dir, x), workloads)
3756ebd27e9Slwd        return rvvbench
3766ebd27e9Slwd
3776ebd27e9Slwd    def __get_ci_rvvtest(self, name=None):
3786ebd27e9Slwd        base_dir = "/nfs/home/share/ci-workloads/V-extension-tests"
3796ebd27e9Slwd        workloads = [
3806ebd27e9Slwd            "rvv-test/vluxei32.v-0.bin",
3816ebd27e9Slwd            "rvv-test/vlsseg4e32.v-0.bin",
3826ebd27e9Slwd            "rvv-test/vlseg4e32.v-0.bin",
3836ebd27e9Slwd            "rvv-test/vsetvl-0.bin",
3846ebd27e9Slwd            "rvv-test/vsetivli-0.bin",
3856ebd27e9Slwd            "rvv-test/vsuxei32.v-0.bin",
3866ebd27e9Slwd            "rvv-test/vse16.v-0.bin",
3876ebd27e9Slwd            "rvv-test/vsse16.v-1.bin",
3886ebd27e9Slwd            "rvv-test/vlse32.v-0.bin",
3896ebd27e9Slwd            "rvv-test/vsetvli-0.bin",
3906ebd27e9Slwd            "rvv-test/vle16.v-0.bin",
3916ebd27e9Slwd            "rvv-test/vle32.v-0.bin",
3926ebd27e9Slwd            "rvv-test/vfsgnj.vv-0.bin",
3936ebd27e9Slwd            "rvv-test/vfadd.vf-0.bin",
3946ebd27e9Slwd            "rvv-test/vfsub.vf-0.bin",
3956ebd27e9Slwd            "rvv-test/vslide1down.vx-0.bin"
3966ebd27e9Slwd        ]
3976ebd27e9Slwd        rvv_test = map(lambda x: os.path.join(base_dir, x), workloads)
3986ebd27e9Slwd        return rvv_test
3996ebd27e9Slwd
400ef3b5b96SWilliam Wang    def __get_ci_mc(self, name=None):
401ef3b5b96SWilliam Wang        base_dir = "/nfs/home/share/ci-workloads"
402ef3b5b96SWilliam Wang        workloads = [
403ef3b5b96SWilliam Wang            "dualcoretest/ldvio-riscv64-xs.bin"
404ef3b5b96SWilliam Wang        ]
405ef3b5b96SWilliam Wang        mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
406ef3b5b96SWilliam Wang        return mc_tests
407ef3b5b96SWilliam Wang
4089c297294SWilliam Wang    def __get_ci_nodiff(self, name=None):
409da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
4109c297294SWilliam Wang        workloads = [
4119c297294SWilliam Wang            "cache-management/cacheoptest-riscv64-xs.bin"
4129c297294SWilliam Wang        ]
4139c297294SWilliam Wang        tests = map(lambda x: os.path.join(base_dir, x), workloads)
4149c297294SWilliam Wang        return tests
4159c297294SWilliam Wang
416c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
417c11a4d2cSYinan Xu        filename = f"{bench}-riscv64-noop.bin"
418c11a4d2cSYinan Xu        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
419c11a4d2cSYinan Xu
420c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
421c11a4d2cSYinan Xu        workloads = {
422c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
423fac0ab56Swakafa            "linux-hello-smp": "bbl.bin",
4244a8a734eSceba            "linux-hello-opensbi": "fw_payload.bin",
4254a8a734eSceba            "linux-hello-smp-opensbi": "fw_payload.bin",
426e975de62STang Haojin            "linux-hello-new": "bbl.bin",
427e975de62STang Haojin            "linux-hello-smp-new": "bbl.bin",
428609a6cf0Schengguanghui            "linux-hello-smp-newcsr": "bbl.bin",
4295092a298Szfw            "povray": "_700480000000_.gz",
4305092a298Szfw            "mcf": "_17520000000_.gz",
4315092a298Szfw            "xalancbmk": "_266100000000_.gz",
4325092a298Szfw            "gcc": "_39720000000_.gz",
4335092a298Szfw            "namd": "_434640000000_.gz",
4345092a298Szfw            "milc": "_103620000000_.gz",
4355092a298Szfw            "lbm": "_140840000000_.gz",
4367b441e5eSYinan Xu            "gromacs": "_275480000000_.gz",
4377b441e5eSYinan Xu            "wrf": "_1916220000000_.gz",
4387b441e5eSYinan Xu            "astar": "_122060000000_.gz"
439c11a4d2cSYinan Xu        }
4409f32a80dSYinan Xu        if name in workloads:
441da3b568bSYinan Xu            return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
4429f32a80dSYinan Xu        # select a random SPEC checkpoint
4439f32a80dSYinan Xu        assert(name == "random")
4449f32a80dSYinan Xu        all_cpt = [
4459f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
4469f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
4479f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
4489f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
4499f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
4509f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
4519f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
4529f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
4539f32a80dSYinan Xu        ]
4549f32a80dSYinan Xu        all_json = [
4559f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
4569f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
4579f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
4589f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
4599f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
4609f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
4619f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
4629f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
4639f32a80dSYinan Xu        ]
4649f32a80dSYinan Xu        assert(len(all_cpt) == len(all_json))
4659f32a80dSYinan Xu        cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
4669f32a80dSYinan Xu        all_gcpt = load_all_gcpt(cpt_path, json_path)
4679f32a80dSYinan Xu        return [random.choice(all_gcpt)]
468c11a4d2cSYinan Xu
469c11a4d2cSYinan Xu    def run_ci(self, test):
470c11a4d2cSYinan Xu        all_tests = {
471c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
472c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
473675acc68SYinan Xu            "misc-tests": self.__get_ci_misc,
474ef3b5b96SWilliam Wang            "mc-tests": self.__get_ci_mc,
4759c297294SWilliam Wang            "nodiff-tests": self.__get_ci_nodiff,
4761f903014SXu, Zefan            "rvh-tests": self.__get_ci_rvhtest,
477c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
4786ebd27e9Slwd            "coremark": self.__am_apps_path,
4796ebd27e9Slwd            "rvv-bench": self.__get_ci_rvvbench,
4806ebd27e9Slwd            "rvv-test": self.__get_ci_rvvtest
481c11a4d2cSYinan Xu        }
482c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
483c11a4d2cSYinan Xu            print(target)
484c11a4d2cSYinan Xu            ret = self.run_emu(target)
485c11a4d2cSYinan Xu            if ret:
48624e2eab6SJinYue                if self.args.default_wave_home != self.args.wave_home:
48724e2eab6SJinYue                    print("copy wave file to " + self.args.wave_home)
48824e2eab6SJinYue                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
489bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
49045f43e6eSTang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
491e3cd2c1fSwakafa                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
492c11a4d2cSYinan Xu                return ret
493c11a4d2cSYinan Xu        return 0
494c11a4d2cSYinan Xu
495e5597226Swakafa    def run_ci_vcs(self, test):
496e5597226Swakafa        all_tests = {
497e5597226Swakafa            "cputest": self.__get_ci_cputest,
498e5597226Swakafa            "riscv-tests": self.__get_ci_rvtest,
499e5597226Swakafa            "misc-tests": self.__get_ci_misc,
500e5597226Swakafa            "mc-tests": self.__get_ci_mc,
501e5597226Swakafa            "nodiff-tests": self.__get_ci_nodiff,
5021f903014SXu, Zefan            "rvh-tests": self.__get_ci_rvhtest,
503e5597226Swakafa            "microbench": self.__am_apps_path,
5046ebd27e9Slwd            "coremark": self.__am_apps_path,
5056ebd27e9Slwd            "rvv-bench": self.__get_ci_rvvbench,
5066ebd27e9Slwd            "rvv-test": self.__get_ci_rvvtest
507e5597226Swakafa        }
508e5597226Swakafa        for target in all_tests.get(test, self.__get_ci_workloads)(test):
509e5597226Swakafa            print(target)
510e5597226Swakafa            ret = self.run_simv(target)
511e5597226Swakafa            if ret:
512e5597226Swakafa                if self.args.default_wave_home != self.args.wave_home:
513e5597226Swakafa                    print("copy wave file to " + self.args.wave_home)
51454cc3a06STang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME")
51554cc3a06STang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME")
51645f43e6eSTang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
517e3cd2c1fSwakafa                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
518e5597226Swakafa                return ret
519e5597226Swakafa        return 0
520e5597226Swakafa
52194e266cbSYinan Xudef get_free_cores(n):
522a941bfc4STang Haojin    numa_re = re.compile(r'.*numactl +.*-C +([0-9]+)-([0-9]+).*')
52394e266cbSYinan Xu    while True:
524a941bfc4STang Haojin        disable_cores = []
525a941bfc4STang Haojin        for proc in psutil.process_iter():
526a941bfc4STang Haojin            try:
527a941bfc4STang Haojin                joint = ' '.join(proc.cmdline())
528a941bfc4STang Haojin                numa_match = numa_re.match(joint)
529*dd720caeSTang Haojin                if numa_match and 'ssh' not in proc.name():
530a941bfc4STang Haojin                    disable_cores.extend(range(int(numa_match.group(1)), int(numa_match.group(2)) + 1))
531a941bfc4STang Haojin            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
532a941bfc4STang Haojin                pass
533b86f926fSYinan Xu        num_logical_core = psutil.cpu_count(logical=False)
53494e266cbSYinan Xu        core_usage = psutil.cpu_percent(interval=1, percpu=True)
53594e266cbSYinan Xu        num_window = num_logical_core // n
53694e266cbSYinan Xu        for i in range(num_window):
537a941bfc4STang Haojin            if set(disable_cores) & set(range(i * n, i * n + n)):
538a941bfc4STang Haojin                continue
53994e266cbSYinan Xu            window_usage = core_usage[i * n : i * n + n]
54001a51437SYinan Xu            if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage):
54194e266cbSYinan Xu                return (((i * n) % 128)// 64, i * n, i * n + n - 1)
54294e266cbSYinan Xu        print(f"No free {n} cores found. CPU usage: {core_usage}\n")
54394e266cbSYinan Xu
544c11a4d2cSYinan Xuif __name__ == "__main__":
545c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
546c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
547c11a4d2cSYinan Xu                        help='input workload file in binary format')
548c11a4d2cSYinan Xu    # actions
549c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
550c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
551e5597226Swakafa    parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs')
552e5597226Swakafa    parser.add_argument('--vcs-build', action='store_true', help='build XS simv')
553c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
554e5597226Swakafa    parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv')
555ef3b5b96SWilliam Wang    parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
5569f32a80dSYinan Xu    parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
557c11a4d2cSYinan Xu    # environment variables
558c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
559c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
560c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
561c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
56224e2eab6SJinYue    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
563c11a4d2cSYinan Xu    # chisel arguments
5641545277aSYinan Xu    parser.add_argument('--enable-log', action='store_true', help='enable log')
5655ef7374fSLi Qianruo    parser.add_argument('--num-cores', type=int, help='number of cores')
566c11a4d2cSYinan Xu    # makefile arguments
5671545277aSYinan Xu    parser.add_argument('--release', action='store_true', help='enable release')
56838e9143dSYinan Xu    parser.add_argument('--spike', action='store_true', help='enable spike diff')
569c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
570c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
57140f31726Sgood-circle    parser.add_argument('--trace', action='store_true', help='enable vcd waveform')
57240f31726Sgood-circle    parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform')
5736c0058d3SYinan Xu    parser.add_argument('--config', nargs='?', type=str, help='config')
574453674e0STang Haojin    parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter')
57554cc3a06STang Haojin    parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs')
576c11a4d2cSYinan Xu    # emu arguments
577c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
578f9930da0SYinan Xu    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
579c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
580078fde2bSJinYue    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
5819c297294SWilliam Wang    parser.add_argument('--no-diff', action='store_true', help='disable difftest')
58225ac26c6SWilliam Wang    parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
583bc247239SXuan Hu    parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt")
58454cc3a06STang Haojin    # both makefile and emu arguments
585e3cd2c1fSwakafa    parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump')
5869810c04aSYangyu Chen    parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)')
5879810c04aSYangyu Chen    parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo')
5889810c04aSYangyu Chen    parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo')
5899810c04aSYangyu Chen    parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC')
590c11a4d2cSYinan Xu
591c11a4d2cSYinan Xu    args = parser.parse_args()
592c11a4d2cSYinan Xu
593c11a4d2cSYinan Xu    xs = XiangShan(args)
594c11a4d2cSYinan Xu    ret = xs.run(args)
595c11a4d2cSYinan Xu
596c11a4d2cSYinan Xu    sys.exit(ret)
597