xref: /XiangShan/scripts/xiangshan.py (revision bc247239b2996f86593edb87bd8e8db9c293c6ea)
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
3094e266cbSYinan Xu
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
82084afb77STang Haojin        self.is_mfc = 1 if args.mfc else None
83453674e0STang Haojin        self.emu_optimize = args.emu_optimize
8454cc3a06STang Haojin        self.xprop = 1 if args.xprop else None
8554cc3a06STang Haojin        self.with_chiseldb = 0 if args.no_db else None
86c11a4d2cSYinan Xu        # emu arguments
87c11a4d2cSYinan Xu        self.max_instr = args.max_instr
8825ac26c6SWilliam Wang        self.ram_size = args.ram_size
8959bcbb59SYinan Xu        self.seed = random.randint(0, 9999)
90c11a4d2cSYinan Xu        self.numa = args.numa
91f9930da0SYinan Xu        self.diff = args.diff
9238e9143dSYinan Xu        if args.spike and "nemu" in args.diff:
9338e9143dSYinan Xu            self.diff = self.diff.replace("nemu-interpreter", "spike")
94078fde2bSJinYue        self.fork = not args.disable_fork
959c297294SWilliam Wang        self.disable_diff = args.no_diff
96e3cd2c1fSwakafa        self.disable_db = args.no_db
97*bc247239SXuan Hu        self.gcpt_restore_bin = args.gcpt_restore_bin
989810c04aSYangyu Chen        self.pgo = args.pgo
999810c04aSYangyu Chen        self.pgo_max_cycle = args.pgo_max_cycle
1009810c04aSYangyu Chen        self.pgo_emu_args = args.pgo_emu_args
1019810c04aSYangyu Chen        self.llvm_profdata = args.llvm_profdata
10224e2eab6SJinYue        # wave dump path
10324e2eab6SJinYue        if args.wave_dump is not None:
10424e2eab6SJinYue            self.set_wave_home(args.wave_dump)
10524e2eab6SJinYue        else:
10624e2eab6SJinYue            self.set_wave_home(self.default_wave_home)
107c11a4d2cSYinan Xu
108c11a4d2cSYinan Xu    def get_env_variables(self):
109c11a4d2cSYinan Xu        all_env = {
110c11a4d2cSYinan Xu            "NOOP_HOME"    : self.noop_home,
111c11a4d2cSYinan Xu            "NEMU_HOME"    : self.nemu_home,
11224e2eab6SJinYue            "WAVE_HOME"    : self.wave_home,
113c11a4d2cSYinan Xu            "AM_HOME"      : self.am_home,
114e5597226Swakafa            "DRAMSIM3_HOME": self.dramsim3_home,
115e5597226Swakafa            "MODULEPATH": "/usr/share/Modules/modulefiles:/etc/modulefiles"
116c11a4d2cSYinan Xu        }
117c11a4d2cSYinan Xu        return all_env
118c11a4d2cSYinan Xu
119c11a4d2cSYinan Xu    def get_chisel_args(self, prefix=None):
120d3126fd3STang Haojin        chisel_args = [
1211545277aSYinan Xu            (self.enable_log, "enable-log")
122c11a4d2cSYinan Xu        ]
123d3126fd3STang Haojin        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
124c11a4d2cSYinan Xu        if prefix is not None:
125c11a4d2cSYinan Xu            args = map(lambda x: prefix + x, args)
126c11a4d2cSYinan Xu        return args
127c11a4d2cSYinan Xu
128c11a4d2cSYinan Xu    def get_makefile_args(self):
129c11a4d2cSYinan Xu        makefile_args = [
130c11a4d2cSYinan Xu            (self.threads,       "EMU_THREADS"),
131c11a4d2cSYinan Xu            (self.with_dramsim3, "WITH_DRAMSIM3"),
1321545277aSYinan Xu            (self.is_release,    "RELEASE"),
13338e9143dSYinan Xu            (self.is_spike,      "REF"),
1346c0058d3SYinan Xu            (self.trace,         "EMU_TRACE"),
13540f31726Sgood-circle            (self.trace_fst,     "EMU_TRACE"),
1365ef7374fSLi Qianruo            (self.config,        "CONFIG"),
137084afb77STang Haojin            (self.num_cores,     "NUM_CORES"),
138453674e0STang Haojin            (self.is_mfc,        "MFC"),
13954cc3a06STang Haojin            (self.emu_optimize,  "EMU_OPTIMIZE"),
14054cc3a06STang Haojin            (self.xprop,         "ENABLE_XPROP"),
1419810c04aSYangyu Chen            (self.with_chiseldb, "WITH_CHISELDB"),
1429810c04aSYangyu Chen            (self.pgo,           "PGO_WORKLOAD"),
1439810c04aSYangyu Chen            (self.pgo_max_cycle, "PGO_MAX_CYCLE"),
1449810c04aSYangyu Chen            (self.pgo_emu_args,  "PGO_EMU_ARGS"),
1459810c04aSYangyu Chen            (self.llvm_profdata, "LLVM_PROFDATA"),
146c11a4d2cSYinan Xu        ]
147c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, makefile_args)
1489810c04aSYangyu Chen        args = [(shlex.quote(str(arg[0])), arg[1]) for arg in args] # shell escape
149c11a4d2cSYinan Xu        return args
150c11a4d2cSYinan Xu
151c11a4d2cSYinan Xu    def get_emu_args(self):
152c11a4d2cSYinan Xu        emu_args = [
15359bcbb59SYinan Xu            (self.max_instr, "max-instr"),
154f9930da0SYinan Xu            (self.diff,      "diff"),
15525ac26c6SWilliam Wang            (self.seed,      "seed"),
15625ac26c6SWilliam Wang            (self.ram_size,  "ram-size"),
157c11a4d2cSYinan Xu        ]
158c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, emu_args)
159c11a4d2cSYinan Xu        return args
160c11a4d2cSYinan Xu
161c11a4d2cSYinan Xu    def show(self):
162c11a4d2cSYinan Xu        print("Extra environment variables:")
163c11a4d2cSYinan Xu        env = self.get_env_variables()
164c11a4d2cSYinan Xu        for env_name in env:
165c11a4d2cSYinan Xu            print(f"{env_name}: {env[env_name]}")
166c11a4d2cSYinan Xu        print()
167c11a4d2cSYinan Xu        print("Chisel arguments:")
168c11a4d2cSYinan Xu        print(" ".join(self.get_chisel_args()))
169c11a4d2cSYinan Xu        print()
170c11a4d2cSYinan Xu        print("Makefile arguments:")
171c11a4d2cSYinan Xu        for val, name in self.get_makefile_args():
172c11a4d2cSYinan Xu            print(f"{name}={val}")
173c11a4d2cSYinan Xu        print()
174c11a4d2cSYinan Xu        print("emu arguments:")
175c11a4d2cSYinan Xu        for val, name in self.get_emu_args():
176c11a4d2cSYinan Xu            print(f"--{name} {val}")
177c11a4d2cSYinan Xu        print()
178c11a4d2cSYinan Xu
179c11a4d2cSYinan Xu    def __extract_path(self, path, env=None, default=None):
180c11a4d2cSYinan Xu        if path is None and env is not None:
181c11a4d2cSYinan Xu            path = os.getenv(env)
182c11a4d2cSYinan Xu        if path is None and default is not None:
183c11a4d2cSYinan Xu            path = default
184c11a4d2cSYinan Xu        path = os.path.realpath(path)
185c11a4d2cSYinan Xu        return path
186c11a4d2cSYinan Xu
187c11a4d2cSYinan Xu    def set_noop_home(self, path):
188c11a4d2cSYinan Xu        self.noop_home = path
189c11a4d2cSYinan Xu
190c11a4d2cSYinan Xu    def set_nemu_home(self, path):
191c11a4d2cSYinan Xu        self.nemu_home = path
192c11a4d2cSYinan Xu
193c11a4d2cSYinan Xu    def set_am_home(self, path):
194c11a4d2cSYinan Xu        self.am_home = path
195c11a4d2cSYinan Xu
196c11a4d2cSYinan Xu    def set_dramsim3_home(self, path):
197c11a4d2cSYinan Xu        self.dramsim3_home = path
198c11a4d2cSYinan Xu
199c11a4d2cSYinan Xu    def set_rvtest_home(self, path):
200c11a4d2cSYinan Xu        self.rvtest_home = path
201c11a4d2cSYinan Xu
20224e2eab6SJinYue    def set_wave_home(self, path):
20324e2eab6SJinYue        print(f"set wave home to {path}")
20424e2eab6SJinYue        self.wave_home = path
20524e2eab6SJinYue
206c11a4d2cSYinan Xu# XiangShan environment
207c11a4d2cSYinan Xuclass XiangShan(object):
208c11a4d2cSYinan Xu    def __init__(self, args):
209c11a4d2cSYinan Xu        self.args = XSArgs(args)
2109f32a80dSYinan Xu        self.timeout = args.timeout
211c11a4d2cSYinan Xu
212c11a4d2cSYinan Xu    def show(self):
213c11a4d2cSYinan Xu        self.args.show()
214c11a4d2cSYinan Xu
215ef3b5b96SWilliam Wang    def make_clean(self):
216ef3b5b96SWilliam Wang        print("Clean up CI workspace")
217ef3b5b96SWilliam Wang        self.show()
218ef3b5b96SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean')
219ef3b5b96SWilliam Wang        return return_code
220ef3b5b96SWilliam Wang
221c11a4d2cSYinan Xu    def generate_verilog(self):
222c11a4d2cSYinan Xu        print("Generating XiangShan verilog with the following configurations:")
223c11a4d2cSYinan Xu        self.show()
224c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
225c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
226c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
227c11a4d2cSYinan Xu        return return_code
228c11a4d2cSYinan Xu
229e5597226Swakafa    def generate_sim_verilog(self):
230e5597226Swakafa        print("Generating XiangShan sim-verilog with the following configurations:")
231e5597226Swakafa        self.show()
232e5597226Swakafa        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
233e5597226Swakafa        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
234e5597226Swakafa        return_code = self.__exec_cmd(f'make -C $NOOP_HOME sim-verilog SIM_ARGS="{sim_args}" {make_args}')
235e5597226Swakafa        return return_code
236e5597226Swakafa
237c11a4d2cSYinan Xu    def build_emu(self):
238c11a4d2cSYinan Xu        print("Building XiangShan emu with the following configurations:")
239c11a4d2cSYinan Xu        self.show()
240c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
241c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
242a3e87608SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
243c11a4d2cSYinan Xu        return return_code
244c11a4d2cSYinan Xu
245e5597226Swakafa    def build_simv(self):
246e5597226Swakafa        print("Building XiangShan simv with the following configurations")
247e5597226Swakafa        self.show()
248e5597226Swakafa        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
249e5597226Swakafa        # TODO: make the following commands grouped as unseen scripts
250e5597226Swakafa        return_code = self.__exec_cmd(f'\
251e5597226Swakafa            eval `/usr/bin/modulecmd zsh load license`;\
252e5597226Swakafa            eval `/usr/bin/modulecmd zsh load synopsys/vcs/Q-2020.03-SP2`;\
253e5597226Swakafa            eval `/usr/bin/modulecmd zsh load synopsys/verdi/S-2021.09-SP1`;\
254e5597226Swakafa            VERDI_HOME=/nfs/tools/synopsys/verdi/S-2021.09-SP1 \
255e5597226Swakafa            make -C $NOOP_HOME simv {make_args} CONSIDER_FSDB=1')  # set CONSIDER_FSDB for compatibility
256e5597226Swakafa        return return_code
257e5597226Swakafa
258c11a4d2cSYinan Xu    def run_emu(self, workload):
259c11a4d2cSYinan Xu        print("Running XiangShan emu with the following configurations:")
260c11a4d2cSYinan Xu        self.show()
261c11a4d2cSYinan Xu        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
262c11a4d2cSYinan Xu        print("workload:", workload)
263c9d90c8dSYinan Xu        numa_args = ""
264c9d90c8dSYinan Xu        if self.args.numa:
26594e266cbSYinan Xu            numa_info = get_free_cores(self.args.threads)
266c9d90c8dSYinan Xu            numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}"
267078fde2bSJinYue        fork_args = "--enable-fork" if self.args.fork else ""
2689c297294SWilliam Wang        diff_args = "--no-diff" if self.args.disable_diff else ""
269e3cd2c1fSwakafa        chiseldb_args = "--dump-db" if not self.args.disable_db else ""
270*bc247239SXuan Hu        gcpt_restore_args = f"-r {self.args.gcpt_restore_bin}" if len(self.args.gcpt_restore_bin) != 0 else ""
271*bc247239SXuan 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}')
272c11a4d2cSYinan Xu        return return_code
273c11a4d2cSYinan Xu
274e5597226Swakafa    def run_simv(self, workload):
275e5597226Swakafa        print("Running XiangShan simv with the following configurations:")
276e5597226Swakafa        self.show()
277e5597226Swakafa        diff_args = "$NOOP_HOME/"+ args.diff
27854cc3a06STang Haojin        assert_args = "-assert finish_maxfail=30 -assert global_finish_maxfail=10000"
279ae0295f4STang 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')
280823787d8STang Haojin        with open(f"{self.args.noop_home}/build/simv.log") as f:
281ae0295f4STang Haojin            content = f.read()
282ae0295f4STang Haojin            if "Offending" in content or "HIT GOOD TRAP" not in content:
283823787d8STang Haojin                return 1
284ae0295f4STang Haojin        return return_code
285e5597226Swakafa
286c11a4d2cSYinan Xu    def run(self, args):
287c11a4d2cSYinan Xu        if args.ci is not None:
288c11a4d2cSYinan Xu            return self.run_ci(args.ci)
289e5597226Swakafa        if args.ci_vcs is not None:
290e5597226Swakafa            return self.run_ci_vcs(args.ci_vcs)
291c11a4d2cSYinan Xu        actions = [
292c11a4d2cSYinan Xu            (args.generate, lambda _ : self.generate_verilog()),
293e5597226Swakafa            (args.vcs_gen, lambda _ : self.generate_sim_verilog()),
294c11a4d2cSYinan Xu            (args.build, lambda _ : self.build_emu()),
295e5597226Swakafa            (args.vcs_build, lambda _ : self.build_simv()),
296ef3b5b96SWilliam Wang            (args.workload, lambda args: self.run_emu(args.workload)),
297ef3b5b96SWilliam Wang            (args.clean, lambda _ : self.make_clean())
298c11a4d2cSYinan Xu        ]
299c11a4d2cSYinan Xu        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
300c11a4d2cSYinan Xu        for i, action in enumerate(valid_actions):
301c11a4d2cSYinan Xu            print(f"Action {i}:")
302c11a4d2cSYinan Xu            ret = action(args)
303c11a4d2cSYinan Xu            if ret:
304c11a4d2cSYinan Xu                return ret
305c11a4d2cSYinan Xu        return 0
306c11a4d2cSYinan Xu
307c11a4d2cSYinan Xu    def __exec_cmd(self, cmd):
308c11a4d2cSYinan Xu        env = dict(os.environ)
309c11a4d2cSYinan Xu        env.update(self.args.get_env_variables())
310c11a4d2cSYinan Xu        print("subprocess call cmd:", cmd)
3115ef7374fSLi Qianruo        start = time.time()
3129f32a80dSYinan Xu        proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid)
3139f32a80dSYinan Xu        try:
3149f32a80dSYinan Xu            return_code = proc.wait(self.timeout)
3155ef7374fSLi Qianruo            end = time.time()
3165ef7374fSLi Qianruo            print(f"Elapsed time: {end - start} seconds")
317c11a4d2cSYinan Xu            return return_code
3189f32a80dSYinan Xu        except (KeyboardInterrupt, subprocess.TimeoutExpired):
3199f32a80dSYinan Xu            os.killpg(os.getpgid(proc.pid), signal.SIGINT)
3209f32a80dSYinan Xu            print(f"KeyboardInterrupt or TimeoutExpired.")
3219f32a80dSYinan Xu            return 0
322c11a4d2cSYinan Xu
323c11a4d2cSYinan Xu    def __get_ci_cputest(self, name=None):
324c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
325c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
326c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
327c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
328c11a4d2cSYinan Xu        return cputest
329c11a4d2cSYinan Xu
330c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
331c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
332c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
333c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
334c11a4d2cSYinan Xu        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
335c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
336c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
337c11a4d2cSYinan Xu        return riscv_tests
338c11a4d2cSYinan Xu
339675acc68SYinan Xu    def __get_ci_misc(self, name=None):
340da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
341675acc68SYinan Xu        workloads = [
342675acc68SYinan Xu            "bitmanip/bitMisc.bin",
3433feeca58Szfw            "crypto/crypto-riscv64-noop.bin",
344675acc68SYinan Xu            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
345675acc68SYinan Xu            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
34664a887e0SYinan Xu            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
34735620aa8Swakafa            "ext_intr/amtest-riscv64-xs.bin",
3483f4ec46fSCODE-JTZ            "cache-alias/aliastest-riscv64-xs.bin",
349af2f7849Shappy-lx            "Svinval/rv64mi-p-svinval.bin",
350b6982e83SLemover            "pmp/pmp.riscv.bin",
351b4edc553SWilliam Wang            "pmp/pmp-am/amtest-riscv64-xs.bin",
352b4edc553SWilliam Wang            "pmp/hugepage-pmp-atom/amtest-riscv64-xs.bin",
35345f497a4Shappy-lx            "asid/asid.bin",
3547d9edc86SLemover            "isa_misc/xret_clear_mprv.bin",
355705cbec3SLemover            "isa_misc/satp_ppn.bin",
3569c297294SWilliam Wang            "cache-management/softprefetchtest-riscv64-xs.bin"
357675acc68SYinan Xu        ]
358675acc68SYinan Xu        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
359675acc68SYinan Xu        return misc_tests
360675acc68SYinan Xu
361ef3b5b96SWilliam Wang    def __get_ci_mc(self, name=None):
362ef3b5b96SWilliam Wang        base_dir = "/nfs/home/share/ci-workloads"
363ef3b5b96SWilliam Wang        workloads = [
364ef3b5b96SWilliam Wang            "dualcoretest/ldvio-riscv64-xs.bin"
365ef3b5b96SWilliam Wang        ]
366ef3b5b96SWilliam Wang        mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
367ef3b5b96SWilliam Wang        return mc_tests
368ef3b5b96SWilliam Wang
3699c297294SWilliam Wang    def __get_ci_nodiff(self, name=None):
370da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
3719c297294SWilliam Wang        workloads = [
3729c297294SWilliam Wang            "cache-management/cacheoptest-riscv64-xs.bin"
3739c297294SWilliam Wang        ]
3749c297294SWilliam Wang        tests = map(lambda x: os.path.join(base_dir, x), workloads)
3759c297294SWilliam Wang        return tests
3769c297294SWilliam Wang
377c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
378c11a4d2cSYinan Xu        filename = f"{bench}-riscv64-noop.bin"
379c11a4d2cSYinan Xu        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
380c11a4d2cSYinan Xu
381c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
382c11a4d2cSYinan Xu        workloads = {
383c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
384fac0ab56Swakafa            "linux-hello-smp": "bbl.bin",
3854a8a734eSceba            "linux-hello-opensbi": "fw_payload.bin",
3864a8a734eSceba            "linux-hello-smp-opensbi": "fw_payload.bin",
387e975de62STang Haojin            "linux-hello-new": "bbl.bin",
388e975de62STang Haojin            "linux-hello-smp-new": "bbl.bin",
389609a6cf0Schengguanghui            "linux-hello-smp-newcsr": "bbl.bin",
3905092a298Szfw            "povray": "_700480000000_.gz",
3915092a298Szfw            "mcf": "_17520000000_.gz",
3925092a298Szfw            "xalancbmk": "_266100000000_.gz",
3935092a298Szfw            "gcc": "_39720000000_.gz",
3945092a298Szfw            "namd": "_434640000000_.gz",
3955092a298Szfw            "milc": "_103620000000_.gz",
3965092a298Szfw            "lbm": "_140840000000_.gz",
3977b441e5eSYinan Xu            "gromacs": "_275480000000_.gz",
3987b441e5eSYinan Xu            "wrf": "_1916220000000_.gz",
3997b441e5eSYinan Xu            "astar": "_122060000000_.gz"
400c11a4d2cSYinan Xu        }
4019f32a80dSYinan Xu        if name in workloads:
402da3b568bSYinan Xu            return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
4039f32a80dSYinan Xu        # select a random SPEC checkpoint
4049f32a80dSYinan Xu        assert(name == "random")
4059f32a80dSYinan Xu        all_cpt = [
4069f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
4079f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
4089f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
4099f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
4109f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
4119f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
4129f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
4139f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
4149f32a80dSYinan Xu        ]
4159f32a80dSYinan Xu        all_json = [
4169f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
4179f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
4189f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
4199f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
4209f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
4219f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
4229f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
4239f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
4249f32a80dSYinan Xu        ]
4259f32a80dSYinan Xu        assert(len(all_cpt) == len(all_json))
4269f32a80dSYinan Xu        cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
4279f32a80dSYinan Xu        all_gcpt = load_all_gcpt(cpt_path, json_path)
4289f32a80dSYinan Xu        return [random.choice(all_gcpt)]
429c11a4d2cSYinan Xu
430c11a4d2cSYinan Xu    def run_ci(self, test):
431c11a4d2cSYinan Xu        all_tests = {
432c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
433c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
434675acc68SYinan Xu            "misc-tests": self.__get_ci_misc,
435ef3b5b96SWilliam Wang            "mc-tests": self.__get_ci_mc,
4369c297294SWilliam Wang            "nodiff-tests": self.__get_ci_nodiff,
437c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
438c11a4d2cSYinan Xu            "coremark": self.__am_apps_path
439c11a4d2cSYinan Xu        }
440c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
441c11a4d2cSYinan Xu            print(target)
442c11a4d2cSYinan Xu            ret = self.run_emu(target)
443c11a4d2cSYinan Xu            if ret:
44424e2eab6SJinYue                if self.args.default_wave_home != self.args.wave_home:
44524e2eab6SJinYue                    print("copy wave file to " + self.args.wave_home)
44624e2eab6SJinYue                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
447bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
44845f43e6eSTang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
449e3cd2c1fSwakafa                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
450c11a4d2cSYinan Xu                return ret
451c11a4d2cSYinan Xu        return 0
452c11a4d2cSYinan Xu
453e5597226Swakafa    def run_ci_vcs(self, test):
454e5597226Swakafa        all_tests = {
455e5597226Swakafa            "cputest": self.__get_ci_cputest,
456e5597226Swakafa            "riscv-tests": self.__get_ci_rvtest,
457e5597226Swakafa            "misc-tests": self.__get_ci_misc,
458e5597226Swakafa            "mc-tests": self.__get_ci_mc,
459e5597226Swakafa            "nodiff-tests": self.__get_ci_nodiff,
460e5597226Swakafa            "microbench": self.__am_apps_path,
461e5597226Swakafa            "coremark": self.__am_apps_path
462e5597226Swakafa        }
463e5597226Swakafa        for target in all_tests.get(test, self.__get_ci_workloads)(test):
464e5597226Swakafa            print(target)
465e5597226Swakafa            ret = self.run_simv(target)
466e5597226Swakafa            if ret:
467e5597226Swakafa                if self.args.default_wave_home != self.args.wave_home:
468e5597226Swakafa                    print("copy wave file to " + self.args.wave_home)
46954cc3a06STang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME")
47054cc3a06STang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME")
47145f43e6eSTang Haojin                    self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
472e3cd2c1fSwakafa                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
473e5597226Swakafa                return ret
474e5597226Swakafa        return 0
475e5597226Swakafa
47694e266cbSYinan Xudef get_free_cores(n):
47794e266cbSYinan Xu    while True:
47894e266cbSYinan Xu        # To avoid potential conflicts, we allow CI to use SMT.
479b86f926fSYinan Xu        num_logical_core = psutil.cpu_count(logical=False)
48094e266cbSYinan Xu        core_usage = psutil.cpu_percent(interval=1, percpu=True)
48194e266cbSYinan Xu        num_window = num_logical_core // n
48294e266cbSYinan Xu        for i in range(num_window):
48394e266cbSYinan Xu            window_usage = core_usage[i * n : i * n + n]
48401a51437SYinan Xu            if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage):
48594e266cbSYinan Xu                return (((i * n) % 128)// 64, i * n, i * n + n - 1)
48694e266cbSYinan Xu        print(f"No free {n} cores found. CPU usage: {core_usage}\n")
48794e266cbSYinan Xu
488c11a4d2cSYinan Xuif __name__ == "__main__":
489c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
490c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
491c11a4d2cSYinan Xu                        help='input workload file in binary format')
492c11a4d2cSYinan Xu    # actions
493c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
494c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
495e5597226Swakafa    parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs')
496e5597226Swakafa    parser.add_argument('--vcs-build', action='store_true', help='build XS simv')
497c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
498e5597226Swakafa    parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv')
499ef3b5b96SWilliam Wang    parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
5009f32a80dSYinan Xu    parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
501c11a4d2cSYinan Xu    # environment variables
502c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
503c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
504c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
505c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
50624e2eab6SJinYue    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
507c11a4d2cSYinan Xu    # chisel arguments
5081545277aSYinan Xu    parser.add_argument('--enable-log', action='store_true', help='enable log')
5095ef7374fSLi Qianruo    parser.add_argument('--num-cores', type=int, help='number of cores')
510c11a4d2cSYinan Xu    # makefile arguments
5111545277aSYinan Xu    parser.add_argument('--release', action='store_true', help='enable release')
51238e9143dSYinan Xu    parser.add_argument('--spike', action='store_true', help='enable spike diff')
513c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
514c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
51540f31726Sgood-circle    parser.add_argument('--trace', action='store_true', help='enable vcd waveform')
51640f31726Sgood-circle    parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform')
5176c0058d3SYinan Xu    parser.add_argument('--config', nargs='?', type=str, help='config')
518084afb77STang Haojin    parser.add_argument('--mfc', action='store_true', help='use mfc')
519453674e0STang Haojin    parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter')
52054cc3a06STang Haojin    parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs')
521c11a4d2cSYinan Xu    # emu arguments
522c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
523f9930da0SYinan Xu    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
524c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
525078fde2bSJinYue    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
5269c297294SWilliam Wang    parser.add_argument('--no-diff', action='store_true', help='disable difftest')
52725ac26c6SWilliam Wang    parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
528*bc247239SXuan Hu    parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt")
52954cc3a06STang Haojin    # both makefile and emu arguments
530e3cd2c1fSwakafa    parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump')
5319810c04aSYangyu Chen    parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)')
5329810c04aSYangyu Chen    parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo')
5339810c04aSYangyu Chen    parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo')
5349810c04aSYangyu Chen    parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC')
535c11a4d2cSYinan Xu
536c11a4d2cSYinan Xu    args = parser.parse_args()
537c11a4d2cSYinan Xu
538c11a4d2cSYinan Xu    xs = XiangShan(args)
539c11a4d2cSYinan Xu    ret = xs.run(args)
540c11a4d2cSYinan Xu
541c11a4d2cSYinan Xu    sys.exit(ret)
542