xref: /XiangShan/scripts/xiangshan.py (revision 25ac26c6cff6435848a68c5adab06175b234dff0)
1c6d43980SLemover#***************************************************************************************
2c6d43980SLemover# Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3f320e0f0SYinan Xu# Copyright (c) 2020-2021 Peng Cheng Laboratory
4c6d43980SLemover#
5c6d43980SLemover# XiangShan is licensed under Mulan PSL v2.
6c6d43980SLemover# You can use this software according to the terms and conditions of the Mulan PSL v2.
7c6d43980SLemover# You may obtain a copy of Mulan PSL v2 at:
8c6d43980SLemover#          http://license.coscl.org.cn/MulanPSL2
9c6d43980SLemover#
10c6d43980SLemover# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11c6d43980SLemover# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12c6d43980SLemover# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13c6d43980SLemover#
14c6d43980SLemover# See the Mulan PSL v2 for more details.
15c6d43980SLemover#***************************************************************************************
16c6d43980SLemover
17c11a4d2cSYinan Xu# Simple version of xiangshan python wrapper
18c11a4d2cSYinan Xu
19c11a4d2cSYinan Xuimport argparse
209f32a80dSYinan Xuimport json
2159bcbb59SYinan Xuimport os
2259bcbb59SYinan Xuimport random
239f32a80dSYinan Xuimport signal
24c11a4d2cSYinan Xuimport subprocess
2559bcbb59SYinan Xuimport sys
265ef7374fSLi Qianruoimport time
27c11a4d2cSYinan Xu
2894e266cbSYinan Xuimport psutil
2994e266cbSYinan Xu
30c11a4d2cSYinan Xu
319f32a80dSYinan Xudef load_all_gcpt(gcpt_path, json_path):
329f32a80dSYinan Xu    all_gcpt = []
339f32a80dSYinan Xu    with open(json_path) as f:
349f32a80dSYinan Xu        data = json.load(f)
359f32a80dSYinan Xu    for benchspec in data:
369f32a80dSYinan Xu        for point in data[benchspec]:
379f32a80dSYinan Xu            weight = data[benchspec][point]
389f32a80dSYinan Xu            gcpt = os.path.join(gcpt_path, "_".join([benchspec, point, weight]))
399f32a80dSYinan Xu            bin_dir = os.path.join(gcpt, "0")
409f32a80dSYinan Xu            bin_file = list(os.listdir(bin_dir))
419f32a80dSYinan Xu            assert(len(bin_file) == 1)
429f32a80dSYinan Xu            bin_path = os.path.join(bin_dir, bin_file[0])
439f32a80dSYinan Xu            assert(os.path.isfile(bin_path))
449f32a80dSYinan Xu            all_gcpt.append(bin_path)
459f32a80dSYinan Xu    return all_gcpt
469f32a80dSYinan Xu
47c11a4d2cSYinan Xuclass XSArgs(object):
48c11a4d2cSYinan Xu    script_path = os.path.realpath(__file__)
49c11a4d2cSYinan Xu    # default path to the repositories
50c11a4d2cSYinan Xu    noop_home = os.path.join(os.path.dirname(script_path), "..")
51c11a4d2cSYinan Xu    nemu_home = os.path.join(noop_home, "../NEMU")
52c11a4d2cSYinan Xu    am_home = os.path.join(noop_home, "../nexus-am")
53c11a4d2cSYinan Xu    dramsim3_home = os.path.join(noop_home, "../DRAMsim3")
54c11a4d2cSYinan Xu    rvtest_home = os.path.join(noop_home, "../riscv-tests")
5524e2eab6SJinYue    default_wave_home = os.path.join(noop_home, "build")
5624e2eab6SJinYue    wave_home   = default_wave_home
57c11a4d2cSYinan Xu
58c11a4d2cSYinan Xu    def __init__(self, args):
59c11a4d2cSYinan Xu        # all path environment variables that should be set
60c11a4d2cSYinan Xu        all_path = [
61c11a4d2cSYinan Xu            # (python argument, environment variable, default, target function)
62c11a4d2cSYinan Xu            (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
63c11a4d2cSYinan Xu            (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
64c11a4d2cSYinan Xu            (args.am, "AM_HOME", self.am_home, self.set_am_home),
65c11a4d2cSYinan Xu            (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
66c11a4d2cSYinan Xu            (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
67c11a4d2cSYinan Xu        ]
68c11a4d2cSYinan Xu        for (arg_in, env, default, set_func) in all_path:
69c11a4d2cSYinan Xu            set_func(self.__extract_path(arg_in, env, default))
70c11a4d2cSYinan Xu        # Chisel arguments
711545277aSYinan Xu        self.enable_log = args.enable_log
725ef7374fSLi Qianruo        self.num_cores = args.num_cores
73c11a4d2cSYinan Xu        # Makefile arguments
74c11a4d2cSYinan Xu        self.threads = args.threads
75c11a4d2cSYinan Xu        self.with_dramsim3 = 1 if args.with_dramsim3 else None
761545277aSYinan Xu        self.is_release = 1 if args.release else None
7738e9143dSYinan Xu        self.is_spike = "spike" if args.spike else None
78eb852d4cSJinYue        self.trace = 1 if args.trace or not args.disable_fork  else None
796c0058d3SYinan Xu        self.config = args.config
80c11a4d2cSYinan Xu        # emu arguments
81c11a4d2cSYinan Xu        self.max_instr = args.max_instr
82*25ac26c6SWilliam Wang        self.ram_size = args.ram_size
8359bcbb59SYinan Xu        self.seed = random.randint(0, 9999)
84c11a4d2cSYinan Xu        self.numa = args.numa
85f9930da0SYinan Xu        self.diff = args.diff
8638e9143dSYinan Xu        if args.spike and "nemu" in args.diff:
8738e9143dSYinan Xu            self.diff = self.diff.replace("nemu-interpreter", "spike")
88078fde2bSJinYue        self.fork = not args.disable_fork
899c297294SWilliam Wang        self.disable_diff = args.no_diff
9024e2eab6SJinYue        # wave dump path
9124e2eab6SJinYue        if args.wave_dump is not None:
9224e2eab6SJinYue            self.set_wave_home(args.wave_dump)
9324e2eab6SJinYue        else:
9424e2eab6SJinYue            self.set_wave_home(self.default_wave_home)
95c11a4d2cSYinan Xu
96c11a4d2cSYinan Xu    def get_env_variables(self):
97c11a4d2cSYinan Xu        all_env = {
98c11a4d2cSYinan Xu            "NOOP_HOME"    : self.noop_home,
99c11a4d2cSYinan Xu            "NEMU_HOME"    : self.nemu_home,
10024e2eab6SJinYue            "WAVE_HOME"    : self.wave_home,
101c11a4d2cSYinan Xu            "AM_HOME"      : self.am_home,
102c11a4d2cSYinan Xu            "DRAMSIM3_HOME": self.dramsim3_home
103c11a4d2cSYinan Xu        }
104c11a4d2cSYinan Xu        return all_env
105c11a4d2cSYinan Xu
106c11a4d2cSYinan Xu    def get_chisel_args(self, prefix=None):
107c11a4d2cSYinan Xu        chisel_args = [
1081545277aSYinan Xu            (self.enable_log, "enable-log")
109c11a4d2cSYinan Xu        ]
110c11a4d2cSYinan Xu        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
111c11a4d2cSYinan Xu        if prefix is not None:
112c11a4d2cSYinan Xu            args = map(lambda x: prefix + x, args)
113c11a4d2cSYinan Xu        return args
114c11a4d2cSYinan Xu
115c11a4d2cSYinan Xu    def get_makefile_args(self):
116c11a4d2cSYinan Xu        makefile_args = [
117c11a4d2cSYinan Xu            (self.threads,       "EMU_THREADS"),
118c11a4d2cSYinan Xu            (self.with_dramsim3, "WITH_DRAMSIM3"),
1191545277aSYinan Xu            (self.is_release,    "RELEASE"),
12038e9143dSYinan Xu            (self.is_spike,      "REF"),
1216c0058d3SYinan Xu            (self.trace,         "EMU_TRACE"),
1225ef7374fSLi Qianruo            (self.config,        "CONFIG"),
1235ef7374fSLi Qianruo            (self.num_cores,     "NUM_CORES")
124c11a4d2cSYinan Xu        ]
125c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, makefile_args)
126c11a4d2cSYinan Xu        return args
127c11a4d2cSYinan Xu
128c11a4d2cSYinan Xu    def get_emu_args(self):
129c11a4d2cSYinan Xu        emu_args = [
13059bcbb59SYinan Xu            (self.max_instr, "max-instr"),
131f9930da0SYinan Xu            (self.diff,      "diff"),
132*25ac26c6SWilliam Wang            (self.seed,      "seed"),
133*25ac26c6SWilliam Wang            (self.ram_size,  "ram-size"),
134c11a4d2cSYinan Xu        ]
135c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, emu_args)
136c11a4d2cSYinan Xu        return args
137c11a4d2cSYinan Xu
138c11a4d2cSYinan Xu    def show(self):
139c11a4d2cSYinan Xu        print("Extra environment variables:")
140c11a4d2cSYinan Xu        env = self.get_env_variables()
141c11a4d2cSYinan Xu        for env_name in env:
142c11a4d2cSYinan Xu            print(f"{env_name}: {env[env_name]}")
143c11a4d2cSYinan Xu        print()
144c11a4d2cSYinan Xu        print("Chisel arguments:")
145c11a4d2cSYinan Xu        print(" ".join(self.get_chisel_args()))
146c11a4d2cSYinan Xu        print()
147c11a4d2cSYinan Xu        print("Makefile arguments:")
148c11a4d2cSYinan Xu        for val, name in self.get_makefile_args():
149c11a4d2cSYinan Xu            print(f"{name}={val}")
150c11a4d2cSYinan Xu        print()
151c11a4d2cSYinan Xu        print("emu arguments:")
152c11a4d2cSYinan Xu        for val, name in self.get_emu_args():
153c11a4d2cSYinan Xu            print(f"--{name} {val}")
154c11a4d2cSYinan Xu        print()
155c11a4d2cSYinan Xu
156c11a4d2cSYinan Xu    def __extract_path(self, path, env=None, default=None):
157c11a4d2cSYinan Xu        if path is None and env is not None:
158c11a4d2cSYinan Xu            path = os.getenv(env)
159c11a4d2cSYinan Xu        if path is None and default is not None:
160c11a4d2cSYinan Xu            path = default
161c11a4d2cSYinan Xu        path = os.path.realpath(path)
162c11a4d2cSYinan Xu        return path
163c11a4d2cSYinan Xu
164c11a4d2cSYinan Xu    def set_noop_home(self, path):
165c11a4d2cSYinan Xu        self.noop_home = path
166c11a4d2cSYinan Xu
167c11a4d2cSYinan Xu    def set_nemu_home(self, path):
168c11a4d2cSYinan Xu        self.nemu_home = path
169c11a4d2cSYinan Xu
170c11a4d2cSYinan Xu    def set_am_home(self, path):
171c11a4d2cSYinan Xu        self.am_home = path
172c11a4d2cSYinan Xu
173c11a4d2cSYinan Xu    def set_dramsim3_home(self, path):
174c11a4d2cSYinan Xu        self.dramsim3_home = path
175c11a4d2cSYinan Xu
176c11a4d2cSYinan Xu    def set_rvtest_home(self, path):
177c11a4d2cSYinan Xu        self.rvtest_home = path
178c11a4d2cSYinan Xu
17924e2eab6SJinYue    def set_wave_home(self, path):
18024e2eab6SJinYue        print(f"set wave home to {path}")
18124e2eab6SJinYue        self.wave_home = path
18224e2eab6SJinYue
183c11a4d2cSYinan Xu# XiangShan environment
184c11a4d2cSYinan Xuclass XiangShan(object):
185c11a4d2cSYinan Xu    def __init__(self, args):
186c11a4d2cSYinan Xu        self.args = XSArgs(args)
1879f32a80dSYinan Xu        self.timeout = args.timeout
188c11a4d2cSYinan Xu
189c11a4d2cSYinan Xu    def show(self):
190c11a4d2cSYinan Xu        self.args.show()
191c11a4d2cSYinan Xu
192ef3b5b96SWilliam Wang    def make_clean(self):
193ef3b5b96SWilliam Wang        print("Clean up CI workspace")
194ef3b5b96SWilliam Wang        self.show()
195ef3b5b96SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean')
196ef3b5b96SWilliam Wang        return return_code
197ef3b5b96SWilliam Wang
198c11a4d2cSYinan Xu    def generate_verilog(self):
199c11a4d2cSYinan Xu        print("Generating XiangShan verilog with the following configurations:")
200c11a4d2cSYinan Xu        self.show()
201c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
202c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
203c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
204c11a4d2cSYinan Xu        return return_code
205c11a4d2cSYinan Xu
206c11a4d2cSYinan Xu    def build_emu(self):
207c11a4d2cSYinan Xu        print("Building XiangShan emu with the following configurations:")
208c11a4d2cSYinan Xu        self.show()
209c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
210c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
211a3e87608SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
212c11a4d2cSYinan Xu        return return_code
213c11a4d2cSYinan Xu
214c11a4d2cSYinan Xu    def run_emu(self, workload):
215c11a4d2cSYinan Xu        print("Running XiangShan emu with the following configurations:")
216c11a4d2cSYinan Xu        self.show()
217c11a4d2cSYinan Xu        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
218c11a4d2cSYinan Xu        print("workload:", workload)
219c9d90c8dSYinan Xu        numa_args = ""
220c9d90c8dSYinan Xu        if self.args.numa:
22194e266cbSYinan Xu            numa_info = get_free_cores(self.args.threads)
222c9d90c8dSYinan Xu            numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}"
223078fde2bSJinYue        fork_args = "--enable-fork" if self.args.fork else ""
2249c297294SWilliam Wang        diff_args = "--no-diff" if self.args.disable_diff else ""
2259c297294SWilliam Wang        return_code = self.__exec_cmd(f'{numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args} {fork_args} {diff_args}')
226c11a4d2cSYinan Xu        return return_code
227c11a4d2cSYinan Xu
228c11a4d2cSYinan Xu    def run(self, args):
229c11a4d2cSYinan Xu        if args.ci is not None:
230c11a4d2cSYinan Xu            return self.run_ci(args.ci)
231c11a4d2cSYinan Xu        actions = [
232c11a4d2cSYinan Xu            (args.generate, lambda _ : self.generate_verilog()),
233c11a4d2cSYinan Xu            (args.build, lambda _ : self.build_emu()),
234ef3b5b96SWilliam Wang            (args.workload, lambda args: self.run_emu(args.workload)),
235ef3b5b96SWilliam Wang            (args.clean, lambda _ : self.make_clean())
236c11a4d2cSYinan Xu        ]
237c11a4d2cSYinan Xu        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
238c11a4d2cSYinan Xu        for i, action in enumerate(valid_actions):
239c11a4d2cSYinan Xu            print(f"Action {i}:")
240c11a4d2cSYinan Xu            ret = action(args)
241c11a4d2cSYinan Xu            if ret:
242c11a4d2cSYinan Xu                return ret
243c11a4d2cSYinan Xu        return 0
244c11a4d2cSYinan Xu
245c11a4d2cSYinan Xu    def __exec_cmd(self, cmd):
246c11a4d2cSYinan Xu        env = dict(os.environ)
247c11a4d2cSYinan Xu        env.update(self.args.get_env_variables())
248c11a4d2cSYinan Xu        print("subprocess call cmd:", cmd)
2495ef7374fSLi Qianruo        start = time.time()
2509f32a80dSYinan Xu        proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid)
2519f32a80dSYinan Xu        try:
2529f32a80dSYinan Xu            return_code = proc.wait(self.timeout)
2535ef7374fSLi Qianruo            end = time.time()
2545ef7374fSLi Qianruo            print(f"Elapsed time: {end - start} seconds")
255c11a4d2cSYinan Xu            return return_code
2569f32a80dSYinan Xu        except (KeyboardInterrupt, subprocess.TimeoutExpired):
2579f32a80dSYinan Xu            os.killpg(os.getpgid(proc.pid), signal.SIGINT)
2589f32a80dSYinan Xu            print(f"KeyboardInterrupt or TimeoutExpired.")
2599f32a80dSYinan Xu            return 0
260c11a4d2cSYinan Xu
261c11a4d2cSYinan Xu    def __get_ci_cputest(self, name=None):
262c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
263c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
264c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
265c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
266c11a4d2cSYinan Xu        return cputest
267c11a4d2cSYinan Xu
268c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
269c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
270c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
271c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
272c11a4d2cSYinan Xu        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
273c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
274c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
275c11a4d2cSYinan Xu        return riscv_tests
276c11a4d2cSYinan Xu
277675acc68SYinan Xu    def __get_ci_misc(self, name=None):
278da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
279675acc68SYinan Xu        workloads = [
280675acc68SYinan Xu            "bitmanip/bitMisc.bin",
2813feeca58Szfw            "crypto/crypto-riscv64-noop.bin",
282675acc68SYinan Xu            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
283675acc68SYinan Xu            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
28464a887e0SYinan Xu            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
28535620aa8Swakafa            "ext_intr/amtest-riscv64-xs.bin",
2863f4ec46fSCODE-JTZ            "cache-alias/aliastest-riscv64-xs.bin",
287af2f7849Shappy-lx            "Svinval/rv64mi-p-svinval.bin",
288b6982e83SLemover            "pmp/pmp.riscv.bin",
28945f497a4Shappy-lx            "asid/asid.bin",
2907d9edc86SLemover            "isa_misc/xret_clear_mprv.bin",
291705cbec3SLemover            "isa_misc/satp_ppn.bin",
2929c297294SWilliam Wang            "cache-management/softprefetchtest-riscv64-xs.bin"
293675acc68SYinan Xu        ]
294675acc68SYinan Xu        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
295675acc68SYinan Xu        return misc_tests
296675acc68SYinan Xu
297ef3b5b96SWilliam Wang    def __get_ci_mc(self, name=None):
298ef3b5b96SWilliam Wang        base_dir = "/nfs/home/share/ci-workloads"
299ef3b5b96SWilliam Wang        workloads = [
300ef3b5b96SWilliam Wang            "dualcoretest/ldvio-riscv64-xs.bin"
301ef3b5b96SWilliam Wang        ]
302ef3b5b96SWilliam Wang        mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
303ef3b5b96SWilliam Wang        return mc_tests
304ef3b5b96SWilliam Wang
3059c297294SWilliam Wang    def __get_ci_nodiff(self, name=None):
306da3b568bSYinan Xu        base_dir = "/nfs/home/share/ci-workloads"
3079c297294SWilliam Wang        workloads = [
3089c297294SWilliam Wang            "cache-management/cacheoptest-riscv64-xs.bin"
3099c297294SWilliam Wang        ]
3109c297294SWilliam Wang        tests = map(lambda x: os.path.join(base_dir, x), workloads)
3119c297294SWilliam Wang        return tests
3129c297294SWilliam Wang
313c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
314c11a4d2cSYinan Xu        filename = f"{bench}-riscv64-noop.bin"
315c11a4d2cSYinan Xu        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
316c11a4d2cSYinan Xu
317c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
318c11a4d2cSYinan Xu        workloads = {
319c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
320fac0ab56Swakafa            "linux-hello-smp": "bbl.bin",
3215092a298Szfw            "povray": "_700480000000_.gz",
3225092a298Szfw            "mcf": "_17520000000_.gz",
3235092a298Szfw            "xalancbmk": "_266100000000_.gz",
3245092a298Szfw            "gcc": "_39720000000_.gz",
3255092a298Szfw            "namd": "_434640000000_.gz",
3265092a298Szfw            "milc": "_103620000000_.gz",
3275092a298Szfw            "lbm": "_140840000000_.gz",
3287b441e5eSYinan Xu            "gromacs": "_275480000000_.gz",
3297b441e5eSYinan Xu            "wrf": "_1916220000000_.gz",
3307b441e5eSYinan Xu            "astar": "_122060000000_.gz"
331c11a4d2cSYinan Xu        }
3329f32a80dSYinan Xu        if name in workloads:
333da3b568bSYinan Xu            return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
3349f32a80dSYinan Xu        # select a random SPEC checkpoint
3359f32a80dSYinan Xu        assert(name == "random")
3369f32a80dSYinan Xu        all_cpt = [
3379f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
3389f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
3399f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
3409f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
3419f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
3429f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
3439f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
3449f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
3459f32a80dSYinan Xu        ]
3469f32a80dSYinan Xu        all_json = [
3479f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
3489f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
3499f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
3509f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
3519f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
3529f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
3539f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
3549f32a80dSYinan Xu            "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
3559f32a80dSYinan Xu        ]
3569f32a80dSYinan Xu        assert(len(all_cpt) == len(all_json))
3579f32a80dSYinan Xu        cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
3589f32a80dSYinan Xu        all_gcpt = load_all_gcpt(cpt_path, json_path)
3599f32a80dSYinan Xu        return [random.choice(all_gcpt)]
360c11a4d2cSYinan Xu
361c11a4d2cSYinan Xu    def run_ci(self, test):
362c11a4d2cSYinan Xu        all_tests = {
363c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
364c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
365675acc68SYinan Xu            "misc-tests": self.__get_ci_misc,
366ef3b5b96SWilliam Wang            "mc-tests": self.__get_ci_mc,
3679c297294SWilliam Wang            "nodiff-tests": self.__get_ci_nodiff,
368c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
369c11a4d2cSYinan Xu            "coremark": self.__am_apps_path
370c11a4d2cSYinan Xu        }
371c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
372c11a4d2cSYinan Xu            print(target)
373c11a4d2cSYinan Xu            ret = self.run_emu(target)
374c11a4d2cSYinan Xu            if ret:
37524e2eab6SJinYue                if self.args.default_wave_home != self.args.wave_home:
37624e2eab6SJinYue                    print("copy wave file to " + self.args.wave_home)
37724e2eab6SJinYue                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
378bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
379bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/SimTop.v $WAVE_HOME")
380c11a4d2cSYinan Xu                return ret
381c11a4d2cSYinan Xu        return 0
382c11a4d2cSYinan Xu
38394e266cbSYinan Xudef get_free_cores(n):
38494e266cbSYinan Xu    while True:
38594e266cbSYinan Xu        # To avoid potential conflicts, we allow CI to use SMT.
386b86f926fSYinan Xu        num_logical_core = psutil.cpu_count(logical=False)
38794e266cbSYinan Xu        core_usage = psutil.cpu_percent(interval=1, percpu=True)
38894e266cbSYinan Xu        num_window = num_logical_core // n
38994e266cbSYinan Xu        for i in range(num_window):
39094e266cbSYinan Xu            window_usage = core_usage[i * n : i * n + n]
39194e266cbSYinan Xu            if sum(window_usage) < 0.3 * n and True not in map(lambda x: x > 0.5, window_usage):
39294e266cbSYinan Xu                return (((i * n) % 128)// 64, i * n, i * n + n - 1)
39394e266cbSYinan Xu        print(f"No free {n} cores found. CPU usage: {core_usage}\n")
39494e266cbSYinan Xu
395c11a4d2cSYinan Xuif __name__ == "__main__":
396c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
397c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
398c11a4d2cSYinan Xu                        help='input workload file in binary format')
399c11a4d2cSYinan Xu    # actions
400c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
401c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
402c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
403ef3b5b96SWilliam Wang    parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
4049f32a80dSYinan Xu    parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
405c11a4d2cSYinan Xu    # environment variables
406c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
407c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
408c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
409c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
41024e2eab6SJinYue    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
411c11a4d2cSYinan Xu    # chisel arguments
4121545277aSYinan Xu    parser.add_argument('--enable-log', action='store_true', help='enable log')
4135ef7374fSLi Qianruo    parser.add_argument('--num-cores', type=int, help='number of cores')
414c11a4d2cSYinan Xu    # makefile arguments
4151545277aSYinan Xu    parser.add_argument('--release', action='store_true', help='enable release')
41638e9143dSYinan Xu    parser.add_argument('--spike', action='store_true', help='enable spike diff')
417c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
418c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
419c11a4d2cSYinan Xu    parser.add_argument('--trace', action='store_true', help='enable waveform')
4206c0058d3SYinan Xu    parser.add_argument('--config', nargs='?', type=str, help='config')
421c11a4d2cSYinan Xu    # emu arguments
422c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
423f9930da0SYinan Xu    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
424c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
425078fde2bSJinYue    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
4269c297294SWilliam Wang    parser.add_argument('--no-diff', action='store_true', help='disable difftest')
427*25ac26c6SWilliam Wang    parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
428c11a4d2cSYinan Xu
429c11a4d2cSYinan Xu    args = parser.parse_args()
430c11a4d2cSYinan Xu
431c11a4d2cSYinan Xu    xs = XiangShan(args)
432c11a4d2cSYinan Xu    ret = xs.run(args)
433c11a4d2cSYinan Xu
434c11a4d2cSYinan Xu    sys.exit(ret)
435