xref: /XiangShan/scripts/xiangshan.py (revision 20156f774775cea2950c4a680f68fd44c1aaa0e3)
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
840b62a2fbSChen Xi        self.with_chiseldb = 0 if args.no_db else 1
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):
3220d7009bfSXu, Zefan        # base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
3230d7009bfSXu, Zefan        base_dir = "/nfs/home/share/ci-workloads/nexus-am-workloads/tests/cputest"
324c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
325c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
326c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
327c11a4d2cSYinan Xu        return cputest
328c11a4d2cSYinan Xu
329c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
330c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
331c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
332c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
33341d8d239Shappy-lx        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud", "rv64mi"]
334c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
335c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
336c11a4d2cSYinan Xu        return riscv_tests
337c11a4d2cSYinan Xu
338675acc68SYinan Xu    def __get_ci_misc(self, name=None):
339da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
340675acc68SYinan Xu        workloads = [
341675acc68SYinan Xu            "bitmanip/bitMisc.bin",
3423feeca58Szfw            "crypto/crypto-riscv64-noop.bin",
3430d7009bfSXu, Zefan            # "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
3440d7009bfSXu, Zefan            # "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
3450d7009bfSXu, Zefan            # "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
3460d7009bfSXu, Zefan            "nexus-am-workloads/amtest/external_intr-riscv64-xs.bin",
3470d7009bfSXu, Zefan            "nexus-am-workloads/tests/aliastest/aliastest-riscv64-xs.bin",
348af2f7849Shappy-lx            "Svinval/rv64mi-p-svinval.bin",
349b6982e83SLemover            "pmp/pmp.riscv.bin",
3500d7009bfSXu, Zefan            "nexus-am-workloads/amtest/pmp_test-riscv64-xs.bin",
3510d7009bfSXu, Zefan            "nexus-am-workloads/amtest/sv39_hp_atom_test-riscv64-xs.bin",
35245f497a4Shappy-lx            "asid/asid.bin",
3537d9edc86SLemover            "isa_misc/xret_clear_mprv.bin",
354705cbec3SLemover            "isa_misc/satp_ppn.bin",
3559c297294SWilliam Wang            "cache-management/softprefetchtest-riscv64-xs.bin"
356675acc68SYinan Xu        ]
357675acc68SYinan Xu        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
358675acc68SYinan Xu        return misc_tests
359675acc68SYinan Xu
3601f903014SXu, Zefan    def __get_ci_rvhtest(self, name=None):
3611f903014SXu, Zefan        base_dir = "/nfs/home/share/ci-workloads/H-extension-tests"
3621f903014SXu, Zefan        workloads = [
363afdeb382SXu, Zefan            "riscv-hyp-tests/rvh_test.bin",
3641f903014SXu, Zefan            "xvisor_wboxtest/checkpoint.gz",
3651f903014SXu, Zefan        ]
3661f903014SXu, Zefan        rvh_tests = map(lambda x: os.path.join(base_dir, x), workloads)
3671f903014SXu, Zefan        return rvh_tests
3681f903014SXu, Zefan
3696ebd27e9Slwd    def __get_ci_rvvbench(self, name=None):
3706ebd27e9Slwd        base_dir = "/nfs/home/share/ci-workloads"
3716ebd27e9Slwd        workloads = [
3726ebd27e9Slwd            "rvv-bench/poly1305.bin",
3736ebd27e9Slwd            "rvv-bench/mergelines.bin"
3746ebd27e9Slwd        ]
3756ebd27e9Slwd        rvvbench = map(lambda x: os.path.join(base_dir, x), workloads)
3766ebd27e9Slwd        return rvvbench
3776ebd27e9Slwd
3786ebd27e9Slwd    def __get_ci_rvvtest(self, name=None):
3796ebd27e9Slwd        base_dir = "/nfs/home/share/ci-workloads/V-extension-tests"
3806ebd27e9Slwd        workloads = [
3816ebd27e9Slwd            "rvv-test/vluxei32.v-0.bin",
3826ebd27e9Slwd            "rvv-test/vlsseg4e32.v-0.bin",
3836ebd27e9Slwd            "rvv-test/vlseg4e32.v-0.bin",
3846ebd27e9Slwd            "rvv-test/vsetvl-0.bin",
3856ebd27e9Slwd            "rvv-test/vsetivli-0.bin",
3866ebd27e9Slwd            "rvv-test/vsuxei32.v-0.bin",
3876ebd27e9Slwd            "rvv-test/vse16.v-0.bin",
3886ebd27e9Slwd            "rvv-test/vsse16.v-1.bin",
3896ebd27e9Slwd            "rvv-test/vlse32.v-0.bin",
3906ebd27e9Slwd            "rvv-test/vsetvli-0.bin",
3916ebd27e9Slwd            "rvv-test/vle16.v-0.bin",
3926ebd27e9Slwd            "rvv-test/vle32.v-0.bin",
3936ebd27e9Slwd            "rvv-test/vfsgnj.vv-0.bin",
3946ebd27e9Slwd            "rvv-test/vfadd.vf-0.bin",
3956ebd27e9Slwd            "rvv-test/vfsub.vf-0.bin",
3966ebd27e9Slwd            "rvv-test/vslide1down.vx-0.bin"
3976ebd27e9Slwd        ]
3986ebd27e9Slwd        rvv_test = map(lambda x: os.path.join(base_dir, x), workloads)
3996ebd27e9Slwd        return rvv_test
4006ebd27e9Slwd
401ef3b5b96SWilliam Wang    def __get_ci_mc(self, name=None):
402ef3b5b96SWilliam Wang        base_dir = "/nfs/home/share/ci-workloads"
403ef3b5b96SWilliam Wang        workloads = [
4040d7009bfSXu, Zefan            "nexus-am-workloads/tests/dualcoretest/ldvio-riscv64-xs.bin"
405ef3b5b96SWilliam Wang        ]
406ef3b5b96SWilliam Wang        mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
407ef3b5b96SWilliam Wang        return mc_tests
408ef3b5b96SWilliam Wang
4099c297294SWilliam Wang    def __get_ci_nodiff(self, name=None):
410da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
4119c297294SWilliam Wang        workloads = [
4129c297294SWilliam Wang            "cache-management/cacheoptest-riscv64-xs.bin"
4139c297294SWilliam Wang        ]
4149c297294SWilliam Wang        tests = map(lambda x: os.path.join(base_dir, x), workloads)
4159c297294SWilliam Wang        return tests
4169c297294SWilliam Wang
417c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
4180d7009bfSXu, Zefan        base_dir = '/nfs/home/share/ci-workloads/nexus-am-workloads/apps'
4190d7009bfSXu, Zefan        filename = f"{bench}-riscv64-xs.bin"
4200d7009bfSXu, Zefan        return [os.path.join(base_dir, bench, filename)]
421c11a4d2cSYinan Xu
422c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
423c11a4d2cSYinan Xu        workloads = {
424c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
425fac0ab56Swakafa            "linux-hello-smp": "bbl.bin",
4264a8a734eSceba            "linux-hello-opensbi": "fw_payload.bin",
4274a8a734eSceba            "linux-hello-smp-opensbi": "fw_payload.bin",
428e975de62STang Haojin            "linux-hello-new": "bbl.bin",
429e975de62STang Haojin            "linux-hello-smp-new": "bbl.bin",
430609a6cf0Schengguanghui            "linux-hello-smp-newcsr": "bbl.bin",
4315092a298Szfw            "povray": "_700480000000_.gz",
4325092a298Szfw            "mcf": "_17520000000_.gz",
4335092a298Szfw            "xalancbmk": "_266100000000_.gz",
4345092a298Szfw            "gcc": "_39720000000_.gz",
4355092a298Szfw            "namd": "_434640000000_.gz",
4365092a298Szfw            "milc": "_103620000000_.gz",
4375092a298Szfw            "lbm": "_140840000000_.gz",
4387b441e5eSYinan Xu            "gromacs": "_275480000000_.gz",
4397b441e5eSYinan Xu            "wrf": "_1916220000000_.gz",
4407b441e5eSYinan Xu            "astar": "_122060000000_.gz"
441c11a4d2cSYinan Xu        }
4429f32a80dSYinan Xu        if name in workloads:
443da3b568bSYinan Xu            return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
4449f32a80dSYinan Xu        # select a random SPEC checkpoint
4459f32a80dSYinan Xu        assert(name == "random")
4469f32a80dSYinan Xu        all_cpt = [
4479f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
4489f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
4499f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
4509f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
4519f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
4529f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
4539f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
4549f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
4559f32a80dSYinan Xu        ]
4569f32a80dSYinan Xu        all_json = [
4579f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
4589f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
4599f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
4609f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
4619f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
4629f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
4639f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
4649f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
4659f32a80dSYinan Xu        ]
4669f32a80dSYinan Xu        assert(len(all_cpt) == len(all_json))
4679f32a80dSYinan Xu        cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
4689f32a80dSYinan Xu        all_gcpt = load_all_gcpt(cpt_path, json_path)
4699f32a80dSYinan Xu        return [random.choice(all_gcpt)]
470c11a4d2cSYinan Xu
471c11a4d2cSYinan Xu    def run_ci(self, test):
472c11a4d2cSYinan Xu        all_tests = {
473c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
474c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
475675acc68SYinan Xu            "misc-tests": self.__get_ci_misc,
476ef3b5b96SWilliam Wang            "mc-tests": self.__get_ci_mc,
4779c297294SWilliam Wang            "nodiff-tests": self.__get_ci_nodiff,
4781f903014SXu, Zefan            "rvh-tests": self.__get_ci_rvhtest,
479c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
4806ebd27e9Slwd            "coremark": self.__am_apps_path,
481*20156f77STang Haojin            "coremark-1-iteration": self.__am_apps_path,
4826ebd27e9Slwd            "rvv-bench": self.__get_ci_rvvbench,
4836ebd27e9Slwd            "rvv-test": self.__get_ci_rvvtest
484c11a4d2cSYinan Xu        }
485c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
486c11a4d2cSYinan Xu            print(target)
487c11a4d2cSYinan Xu            ret = self.run_emu(target)
488c11a4d2cSYinan Xu            if ret:
48924e2eab6SJinYue                if self.args.default_wave_home != self.args.wave_home:
49024e2eab6SJinYue                    print("copy wave file to " + self.args.wave_home)
49124e2eab6SJinYue                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
492bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
49345f43e6eSTang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
494e3cd2c1fSwakafa                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
495c11a4d2cSYinan Xu                return ret
496c11a4d2cSYinan Xu        return 0
497c11a4d2cSYinan Xu
498e5597226Swakafa    def run_ci_vcs(self, test):
499e5597226Swakafa        all_tests = {
500e5597226Swakafa            "cputest": self.__get_ci_cputest,
501e5597226Swakafa            "riscv-tests": self.__get_ci_rvtest,
502e5597226Swakafa            "misc-tests": self.__get_ci_misc,
503e5597226Swakafa            "mc-tests": self.__get_ci_mc,
504e5597226Swakafa            "nodiff-tests": self.__get_ci_nodiff,
5051f903014SXu, Zefan            "rvh-tests": self.__get_ci_rvhtest,
506e5597226Swakafa            "microbench": self.__am_apps_path,
5076ebd27e9Slwd            "coremark": self.__am_apps_path,
508*20156f77STang Haojin            "coremark-1-iteration": self.__am_apps_path,
5096ebd27e9Slwd            "rvv-bench": self.__get_ci_rvvbench,
5106ebd27e9Slwd            "rvv-test": self.__get_ci_rvvtest
511e5597226Swakafa        }
512e5597226Swakafa        for target in all_tests.get(test, self.__get_ci_workloads)(test):
513e5597226Swakafa            print(target)
514e5597226Swakafa            ret = self.run_simv(target)
515e5597226Swakafa            if ret:
516e5597226Swakafa                if self.args.default_wave_home != self.args.wave_home:
517e5597226Swakafa                    print("copy wave file to " + self.args.wave_home)
51854cc3a06STang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME")
51954cc3a06STang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME")
52045f43e6eSTang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
521e3cd2c1fSwakafa                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
522e5597226Swakafa                return ret
523e5597226Swakafa        return 0
524e5597226Swakafa
52594e266cbSYinan Xudef get_free_cores(n):
526a941bfc4STang Haojin    numa_re = re.compile(r'.*numactl +.*-C +([0-9]+)-([0-9]+).*')
52794e266cbSYinan Xu    while True:
528a941bfc4STang Haojin        disable_cores = []
529a941bfc4STang Haojin        for proc in psutil.process_iter():
530a941bfc4STang Haojin            try:
531a941bfc4STang Haojin                joint = ' '.join(proc.cmdline())
532a941bfc4STang Haojin                numa_match = numa_re.match(joint)
533dd720caeSTang Haojin                if numa_match and 'ssh' not in proc.name():
534a941bfc4STang Haojin                    disable_cores.extend(range(int(numa_match.group(1)), int(numa_match.group(2)) + 1))
535a941bfc4STang Haojin            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
536a941bfc4STang Haojin                pass
537b86f926fSYinan Xu        num_logical_core = psutil.cpu_count(logical=False)
53894e266cbSYinan Xu        core_usage = psutil.cpu_percent(interval=1, percpu=True)
53994e266cbSYinan Xu        num_window = num_logical_core // n
54094e266cbSYinan Xu        for i in range(num_window):
541a941bfc4STang Haojin            if set(disable_cores) & set(range(i * n, i * n + n)):
542a941bfc4STang Haojin                continue
54394e266cbSYinan Xu            window_usage = core_usage[i * n : i * n + n]
54401a51437SYinan Xu            if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage):
54571f0f4ccSTang Haojin                return (((i * n) % num_logical_core) // (num_logical_core // 2), i * n, i * n + n - 1)
54694e266cbSYinan Xu        print(f"No free {n} cores found. CPU usage: {core_usage}\n")
54743f08742STang Haojin        time.sleep(random.uniform(1, 60))
54894e266cbSYinan Xu
549c11a4d2cSYinan Xuif __name__ == "__main__":
550c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
551c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
552c11a4d2cSYinan Xu                        help='input workload file in binary format')
553c11a4d2cSYinan Xu    # actions
554c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
555c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
556e5597226Swakafa    parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs')
557e5597226Swakafa    parser.add_argument('--vcs-build', action='store_true', help='build XS simv')
558c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
559e5597226Swakafa    parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv')
560ef3b5b96SWilliam Wang    parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
5619f32a80dSYinan Xu    parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
562c11a4d2cSYinan Xu    # environment variables
563c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
564c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
565c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
566c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
56724e2eab6SJinYue    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
568c11a4d2cSYinan Xu    # chisel arguments
5691545277aSYinan Xu    parser.add_argument('--enable-log', action='store_true', help='enable log')
5705ef7374fSLi Qianruo    parser.add_argument('--num-cores', type=int, help='number of cores')
571c11a4d2cSYinan Xu    # makefile arguments
5721545277aSYinan Xu    parser.add_argument('--release', action='store_true', help='enable release')
57338e9143dSYinan Xu    parser.add_argument('--spike', action='store_true', help='enable spike diff')
574c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
575c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
57640f31726Sgood-circle    parser.add_argument('--trace', action='store_true', help='enable vcd waveform')
57740f31726Sgood-circle    parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform')
5786c0058d3SYinan Xu    parser.add_argument('--config', nargs='?', type=str, help='config')
579453674e0STang Haojin    parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter')
58054cc3a06STang Haojin    parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs')
581c11a4d2cSYinan Xu    # emu arguments
582c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
583f9930da0SYinan Xu    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
584c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
585078fde2bSJinYue    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
5869c297294SWilliam Wang    parser.add_argument('--no-diff', action='store_true', help='disable difftest')
58725ac26c6SWilliam Wang    parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
588bc247239SXuan Hu    parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt")
58954cc3a06STang Haojin    # both makefile and emu arguments
590e3cd2c1fSwakafa    parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump')
5919810c04aSYangyu Chen    parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)')
5929810c04aSYangyu Chen    parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo')
5939810c04aSYangyu Chen    parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo')
5949810c04aSYangyu Chen    parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC')
595c11a4d2cSYinan Xu
596c11a4d2cSYinan Xu    args = parser.parse_args()
597c11a4d2cSYinan Xu
598c11a4d2cSYinan Xu    xs = XiangShan(args)
599c11a4d2cSYinan Xu    ret = xs.run(args)
600c11a4d2cSYinan Xu
601c11a4d2cSYinan Xu    sys.exit(ret)
602