xref: /XiangShan/scripts/xiangshan.py (revision 1545277abc67bbe5123a324f0b61142535bfe61f)
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
2059bcbb59SYinan Xuimport os
2159bcbb59SYinan Xuimport random
22c11a4d2cSYinan Xuimport subprocess
2359bcbb59SYinan Xuimport sys
245ef7374fSLi Qianruoimport time
25c11a4d2cSYinan Xu
26c11a4d2cSYinan Xu
27c11a4d2cSYinan Xuclass XSArgs(object):
28c11a4d2cSYinan Xu    script_path = os.path.realpath(__file__)
29c11a4d2cSYinan Xu    # default path to the repositories
30c11a4d2cSYinan Xu    noop_home = os.path.join(os.path.dirname(script_path), "..")
31c11a4d2cSYinan Xu    nemu_home = os.path.join(noop_home, "../NEMU")
32c11a4d2cSYinan Xu    am_home = os.path.join(noop_home, "../nexus-am")
33c11a4d2cSYinan Xu    dramsim3_home = os.path.join(noop_home, "../DRAMsim3")
34c11a4d2cSYinan Xu    rvtest_home = os.path.join(noop_home, "../riscv-tests")
3524e2eab6SJinYue    default_wave_home = os.path.join(noop_home, "build")
3624e2eab6SJinYue    wave_home   = default_wave_home
37c11a4d2cSYinan Xu
38c11a4d2cSYinan Xu    def __init__(self, args):
39c11a4d2cSYinan Xu        # all path environment variables that should be set
40c11a4d2cSYinan Xu        all_path = [
41c11a4d2cSYinan Xu            # (python argument, environment variable, default, target function)
42c11a4d2cSYinan Xu            (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
43c11a4d2cSYinan Xu            (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
44c11a4d2cSYinan Xu            (args.am, "AM_HOME", self.am_home, self.set_am_home),
45c11a4d2cSYinan Xu            (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
46c11a4d2cSYinan Xu            (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
47c11a4d2cSYinan Xu        ]
48c11a4d2cSYinan Xu        for (arg_in, env, default, set_func) in all_path:
49c11a4d2cSYinan Xu            set_func(self.__extract_path(arg_in, env, default))
50c11a4d2cSYinan Xu        # Chisel arguments
51*1545277aSYinan Xu        self.enable_log = args.enable_log
525ef7374fSLi Qianruo        self.num_cores = args.num_cores
53c11a4d2cSYinan Xu        # Makefile arguments
54c11a4d2cSYinan Xu        self.threads = args.threads
55c11a4d2cSYinan Xu        self.with_dramsim3 = 1 if args.with_dramsim3 else None
56*1545277aSYinan Xu        self.is_release = 1 if args.release else None
57eb852d4cSJinYue        self.trace = 1 if args.trace or not args.disable_fork  else None
586c0058d3SYinan Xu        self.config = args.config
59c11a4d2cSYinan Xu        # emu arguments
60c11a4d2cSYinan Xu        self.max_instr = args.max_instr
6159bcbb59SYinan Xu        self.seed = random.randint(0, 9999)
62c11a4d2cSYinan Xu        self.numa = args.numa
63f9930da0SYinan Xu        self.diff = args.diff
64078fde2bSJinYue        self.fork = not args.disable_fork
6524e2eab6SJinYue        # wave dump path
6624e2eab6SJinYue        if args.wave_dump is not None:
6724e2eab6SJinYue            self.set_wave_home(args.wave_dump)
6824e2eab6SJinYue        else:
6924e2eab6SJinYue            self.set_wave_home(self.default_wave_home)
70c11a4d2cSYinan Xu
71c11a4d2cSYinan Xu    def get_env_variables(self):
72c11a4d2cSYinan Xu        all_env = {
73c11a4d2cSYinan Xu            "NOOP_HOME"    : self.noop_home,
74c11a4d2cSYinan Xu            "NEMU_HOME"    : self.nemu_home,
7524e2eab6SJinYue            "WAVE_HOME"    : self.wave_home,
76c11a4d2cSYinan Xu            "AM_HOME"      : self.am_home,
77c11a4d2cSYinan Xu            "DRAMSIM3_HOME": self.dramsim3_home
78c11a4d2cSYinan Xu        }
79c11a4d2cSYinan Xu        return all_env
80c11a4d2cSYinan Xu
81c11a4d2cSYinan Xu    def get_chisel_args(self, prefix=None):
82c11a4d2cSYinan Xu        chisel_args = [
83*1545277aSYinan Xu            (self.enable_log, "enable-log")
84c11a4d2cSYinan Xu        ]
85c11a4d2cSYinan Xu        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
86c11a4d2cSYinan Xu        if prefix is not None:
87c11a4d2cSYinan Xu            args = map(lambda x: prefix + x, args)
88c11a4d2cSYinan Xu        return args
89c11a4d2cSYinan Xu
90c11a4d2cSYinan Xu    def get_makefile_args(self):
91c11a4d2cSYinan Xu        makefile_args = [
92c11a4d2cSYinan Xu            (self.threads,       "EMU_THREADS"),
93c11a4d2cSYinan Xu            (self.with_dramsim3, "WITH_DRAMSIM3"),
94*1545277aSYinan Xu            (self.is_release,    "RELEASE"),
956c0058d3SYinan Xu            (self.trace,         "EMU_TRACE"),
965ef7374fSLi Qianruo            (self.config,        "CONFIG"),
975ef7374fSLi Qianruo            (self.num_cores,     "NUM_CORES")
98c11a4d2cSYinan Xu        ]
99c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, makefile_args)
100c11a4d2cSYinan Xu        return args
101c11a4d2cSYinan Xu
102c11a4d2cSYinan Xu    def get_emu_args(self):
103c11a4d2cSYinan Xu        emu_args = [
10459bcbb59SYinan Xu            (self.max_instr, "max-instr"),
105f9930da0SYinan Xu            (self.diff,      "diff"),
10659bcbb59SYinan Xu            (self.seed,      "seed")
107c11a4d2cSYinan Xu        ]
108c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, emu_args)
109c11a4d2cSYinan Xu        return args
110c11a4d2cSYinan Xu
111c11a4d2cSYinan Xu    def show(self):
112c11a4d2cSYinan Xu        print("Extra environment variables:")
113c11a4d2cSYinan Xu        env = self.get_env_variables()
114c11a4d2cSYinan Xu        for env_name in env:
115c11a4d2cSYinan Xu            print(f"{env_name}: {env[env_name]}")
116c11a4d2cSYinan Xu        print()
117c11a4d2cSYinan Xu        print("Chisel arguments:")
118c11a4d2cSYinan Xu        print(" ".join(self.get_chisel_args()))
119c11a4d2cSYinan Xu        print()
120c11a4d2cSYinan Xu        print("Makefile arguments:")
121c11a4d2cSYinan Xu        for val, name in self.get_makefile_args():
122c11a4d2cSYinan Xu            print(f"{name}={val}")
123c11a4d2cSYinan Xu        print()
124c11a4d2cSYinan Xu        print("emu arguments:")
125c11a4d2cSYinan Xu        for val, name in self.get_emu_args():
126c11a4d2cSYinan Xu            print(f"--{name} {val}")
127c11a4d2cSYinan Xu        print()
128c11a4d2cSYinan Xu
129c11a4d2cSYinan Xu    def __extract_path(self, path, env=None, default=None):
130c11a4d2cSYinan Xu        if path is None and env is not None:
131c11a4d2cSYinan Xu            path = os.getenv(env)
132c11a4d2cSYinan Xu        if path is None and default is not None:
133c11a4d2cSYinan Xu            path = default
134c11a4d2cSYinan Xu        path = os.path.realpath(path)
135c11a4d2cSYinan Xu        return path
136c11a4d2cSYinan Xu
137c11a4d2cSYinan Xu    def set_noop_home(self, path):
138c11a4d2cSYinan Xu        self.noop_home = path
139c11a4d2cSYinan Xu
140c11a4d2cSYinan Xu    def set_nemu_home(self, path):
141c11a4d2cSYinan Xu        self.nemu_home = path
142c11a4d2cSYinan Xu
143c11a4d2cSYinan Xu    def set_am_home(self, path):
144c11a4d2cSYinan Xu        self.am_home = path
145c11a4d2cSYinan Xu
146c11a4d2cSYinan Xu    def set_dramsim3_home(self, path):
147c11a4d2cSYinan Xu        self.dramsim3_home = path
148c11a4d2cSYinan Xu
149c11a4d2cSYinan Xu    def set_rvtest_home(self, path):
150c11a4d2cSYinan Xu        self.rvtest_home = path
151c11a4d2cSYinan Xu
15224e2eab6SJinYue    def set_wave_home(self, path):
15324e2eab6SJinYue        print(f"set wave home to {path}")
15424e2eab6SJinYue        self.wave_home = path
15524e2eab6SJinYue
156c11a4d2cSYinan Xu# XiangShan environment
157c11a4d2cSYinan Xuclass XiangShan(object):
158c11a4d2cSYinan Xu    def __init__(self, args):
159c11a4d2cSYinan Xu        self.args = XSArgs(args)
160c11a4d2cSYinan Xu
161c11a4d2cSYinan Xu    def show(self):
162c11a4d2cSYinan Xu        self.args.show()
163c11a4d2cSYinan Xu
164c11a4d2cSYinan Xu    def generate_verilog(self):
165c11a4d2cSYinan Xu        print("Generating XiangShan verilog with the following configurations:")
166c11a4d2cSYinan Xu        self.show()
167c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
168c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
169c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
170c11a4d2cSYinan Xu        return return_code
171c11a4d2cSYinan Xu
172c11a4d2cSYinan Xu    def build_emu(self):
173c11a4d2cSYinan Xu        print("Building XiangShan emu with the following configurations:")
174c11a4d2cSYinan Xu        self.show()
175c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
176c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
177a3e87608SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
178c11a4d2cSYinan Xu        return return_code
179c11a4d2cSYinan Xu
180c11a4d2cSYinan Xu    def run_emu(self, workload):
181c11a4d2cSYinan Xu        print("Running XiangShan emu with the following configurations:")
182c11a4d2cSYinan Xu        self.show()
183c11a4d2cSYinan Xu        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
184c11a4d2cSYinan Xu        print("workload:", workload)
185c11a4d2cSYinan Xu        numa_args = f"numactl -m 1 -C 64-{64+self.args.threads-1}" if self.args.numa else ""
186078fde2bSJinYue        fork_args = "--enable-fork" if self.args.fork else ""
187078fde2bSJinYue        return_code = self.__exec_cmd(f'{numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args} {fork_args}')
188c11a4d2cSYinan Xu        return return_code
189c11a4d2cSYinan Xu
190c11a4d2cSYinan Xu    def run(self, args):
191c11a4d2cSYinan Xu        if args.ci is not None:
192c11a4d2cSYinan Xu            return self.run_ci(args.ci)
193c11a4d2cSYinan Xu        actions = [
194c11a4d2cSYinan Xu            (args.generate, lambda _ : self.generate_verilog()),
195c11a4d2cSYinan Xu            (args.build, lambda _ : self.build_emu()),
196c11a4d2cSYinan Xu            (args.workload, lambda args: self.run_emu(args.workload))
197c11a4d2cSYinan Xu        ]
198c11a4d2cSYinan Xu        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
199c11a4d2cSYinan Xu        for i, action in enumerate(valid_actions):
200c11a4d2cSYinan Xu            print(f"Action {i}:")
201c11a4d2cSYinan Xu            ret = action(args)
202c11a4d2cSYinan Xu            if ret:
203c11a4d2cSYinan Xu                return ret
204c11a4d2cSYinan Xu        return 0
205c11a4d2cSYinan Xu
206c11a4d2cSYinan Xu    def __exec_cmd(self, cmd):
207c11a4d2cSYinan Xu        env = dict(os.environ)
208c11a4d2cSYinan Xu        env.update(self.args.get_env_variables())
209c11a4d2cSYinan Xu        print("subprocess call cmd:", cmd)
2105ef7374fSLi Qianruo        start = time.time()
211c11a4d2cSYinan Xu        return_code = subprocess.call(cmd, shell=True, env=env)
2125ef7374fSLi Qianruo        end = time.time()
2135ef7374fSLi Qianruo        print(f"Elapsed time: {end - start} seconds")
214c11a4d2cSYinan Xu        return return_code
215c11a4d2cSYinan Xu
216c11a4d2cSYinan Xu    def __get_ci_cputest(self, name=None):
217c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
218c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
219c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
220c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
221c11a4d2cSYinan Xu        return cputest
222c11a4d2cSYinan Xu
223c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
224c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
225c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
226c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
227c11a4d2cSYinan Xu        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
228c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
229c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
230c11a4d2cSYinan Xu        return riscv_tests
231c11a4d2cSYinan Xu
232675acc68SYinan Xu    def __get_ci_misc(self, name=None):
233675acc68SYinan Xu        base_dir = "/home/ci-runner/xsenv/workloads"
234675acc68SYinan Xu        workloads = [
235675acc68SYinan Xu            "bitmanip/bitMisc.bin",
2363feeca58Szfw            "crypto/crypto-riscv64-noop.bin",
237675acc68SYinan Xu            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
238675acc68SYinan Xu            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
23964a887e0SYinan Xu            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
24035620aa8Swakafa            "ext_intr/amtest-riscv64-xs.bin",
2413f4ec46fSCODE-JTZ            "cache-alias/aliastest-riscv64-xs.bin",
242af2f7849Shappy-lx            "Svinval/rv64mi-p-svinval.bin",
243b6982e83SLemover            "pmp/pmp.riscv.bin",
24445f497a4Shappy-lx            "asid/asid.bin",
2453f4ec46fSCODE-JTZ            "cache-management/softprefetch-riscv64-noop.bin"
246675acc68SYinan Xu        ]
247675acc68SYinan Xu        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
248675acc68SYinan Xu        return misc_tests
249675acc68SYinan Xu
250c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
251c11a4d2cSYinan Xu        filename = f"{bench}-riscv64-noop.bin"
252c11a4d2cSYinan Xu        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
253c11a4d2cSYinan Xu
254c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
255c11a4d2cSYinan Xu        workloads = {
256c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
2575092a298Szfw            "povray": "_700480000000_.gz",
2585092a298Szfw            "mcf": "_17520000000_.gz",
2595092a298Szfw            "xalancbmk": "_266100000000_.gz",
2605092a298Szfw            "gcc": "_39720000000_.gz",
2615092a298Szfw            "namd": "_434640000000_.gz",
2625092a298Szfw            "milc": "_103620000000_.gz",
2635092a298Szfw            "lbm": "_140840000000_.gz",
2647b441e5eSYinan Xu            "gromacs": "_275480000000_.gz",
2657b441e5eSYinan Xu            "wrf": "_1916220000000_.gz",
2667b441e5eSYinan Xu            "astar": "_122060000000_.gz"
267c11a4d2cSYinan Xu        }
268c11a4d2cSYinan Xu        return [os.path.join("/home/ci-runner/xsenv/workloads", name, workloads[name])]
269c11a4d2cSYinan Xu
270c11a4d2cSYinan Xu    def run_ci(self, test):
271c11a4d2cSYinan Xu        all_tests = {
272c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
273c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
274675acc68SYinan Xu            "misc-tests": self.__get_ci_misc,
275c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
276c11a4d2cSYinan Xu            "coremark": self.__am_apps_path
277c11a4d2cSYinan Xu        }
278c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
279c11a4d2cSYinan Xu            print(target)
280c11a4d2cSYinan Xu            ret = self.run_emu(target)
281c11a4d2cSYinan Xu            if ret:
28224e2eab6SJinYue                if self.args.default_wave_home != self.args.wave_home:
28324e2eab6SJinYue                    print("copy wave file to " + self.args.wave_home)
28424e2eab6SJinYue                    self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
285bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
286bc063562SLemover                    self.__exec_cmd(f"cp $NOOP_HOME/build/SimTop.v $WAVE_HOME")
287c11a4d2cSYinan Xu                return ret
288c11a4d2cSYinan Xu        return 0
289c11a4d2cSYinan Xu
290c11a4d2cSYinan Xuif __name__ == "__main__":
291c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
292c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
293c11a4d2cSYinan Xu                        help='input workload file in binary format')
294c11a4d2cSYinan Xu    # actions
295c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
296c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
297c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
298c11a4d2cSYinan Xu    # environment variables
299c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
300c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
301c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
302c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
30324e2eab6SJinYue    parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
304c11a4d2cSYinan Xu    # chisel arguments
305*1545277aSYinan Xu    parser.add_argument('--enable-log', action='store_true', help='enable log')
3065ef7374fSLi Qianruo    parser.add_argument('--num-cores', type=int, help='number of cores')
307c11a4d2cSYinan Xu    # makefile arguments
308*1545277aSYinan Xu    parser.add_argument('--release', action='store_true', help='enable release')
309c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
310c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
311c11a4d2cSYinan Xu    parser.add_argument('--trace', action='store_true', help='enable waveform')
3126c0058d3SYinan Xu    parser.add_argument('--config', nargs='?', type=str, help='config')
313c11a4d2cSYinan Xu    # emu arguments
314c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
315f9930da0SYinan Xu    parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
316c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
317078fde2bSJinYue    parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
31824e2eab6SJinYue    # ci action head sha
319c11a4d2cSYinan Xu
320c11a4d2cSYinan Xu    args = parser.parse_args()
321c11a4d2cSYinan Xu
322c11a4d2cSYinan Xu    xs = XiangShan(args)
323c11a4d2cSYinan Xu    ret = xs.run(args)
324c11a4d2cSYinan Xu
325c11a4d2cSYinan Xu    sys.exit(ret)
326