xref: /XiangShan/scripts/xiangshan.py (revision 3f4ec46f46b3a5024eb568d3ccfcddaba3befd49)
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")
35c11a4d2cSYinan Xu
36c11a4d2cSYinan Xu    def __init__(self, args):
37c11a4d2cSYinan Xu        # all path environment variables that should be set
38c11a4d2cSYinan Xu        all_path = [
39c11a4d2cSYinan Xu            # (python argument, environment variable, default, target function)
40c11a4d2cSYinan Xu            (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
41c11a4d2cSYinan Xu            (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
42c11a4d2cSYinan Xu            (args.am, "AM_HOME", self.am_home, self.set_am_home),
43c11a4d2cSYinan Xu            (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
44c11a4d2cSYinan Xu            (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
45c11a4d2cSYinan Xu        ]
46c11a4d2cSYinan Xu        for (arg_in, env, default, set_func) in all_path:
47c11a4d2cSYinan Xu            set_func(self.__extract_path(arg_in, env, default))
48c11a4d2cSYinan Xu        # Chisel arguments
49c11a4d2cSYinan Xu        self.disable_log = args.disable_log
505ef7374fSLi Qianruo        self.num_cores = args.num_cores
51c11a4d2cSYinan Xu        # Makefile arguments
52c11a4d2cSYinan Xu        self.threads = args.threads
53c11a4d2cSYinan Xu        self.with_dramsim3 = 1 if args.with_dramsim3 else None
54c11a4d2cSYinan Xu        self.trace = 1 if args.trace else None
556c0058d3SYinan Xu        self.config = args.config
56c11a4d2cSYinan Xu        # emu arguments
57c11a4d2cSYinan Xu        self.max_instr = args.max_instr
5859bcbb59SYinan Xu        self.seed = random.randint(0, 9999)
59c11a4d2cSYinan Xu        self.numa = args.numa
60c11a4d2cSYinan Xu
61c11a4d2cSYinan Xu    def get_env_variables(self):
62c11a4d2cSYinan Xu        all_env = {
63c11a4d2cSYinan Xu            "NOOP_HOME"    : self.noop_home,
64c11a4d2cSYinan Xu            "NEMU_HOME"    : self.nemu_home,
65c11a4d2cSYinan Xu            "AM_HOME"      : self.am_home,
66c11a4d2cSYinan Xu            "DRAMSIM3_HOME": self.dramsim3_home
67c11a4d2cSYinan Xu        }
68c11a4d2cSYinan Xu        return all_env
69c11a4d2cSYinan Xu
70c11a4d2cSYinan Xu    def get_chisel_args(self, prefix=None):
71c11a4d2cSYinan Xu        chisel_args = [
725ef7374fSLi Qianruo            (self.disable_log, "disable-log")
73c11a4d2cSYinan Xu        ]
74c11a4d2cSYinan Xu        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
75c11a4d2cSYinan Xu        if prefix is not None:
76c11a4d2cSYinan Xu            args = map(lambda x: prefix + x, args)
77c11a4d2cSYinan Xu        return args
78c11a4d2cSYinan Xu
79c11a4d2cSYinan Xu    def get_makefile_args(self):
80c11a4d2cSYinan Xu        makefile_args = [
81c11a4d2cSYinan Xu            (self.threads,       "EMU_THREADS"),
82c11a4d2cSYinan Xu            (self.with_dramsim3, "WITH_DRAMSIM3"),
836c0058d3SYinan Xu            (self.trace,         "EMU_TRACE"),
845ef7374fSLi Qianruo            (self.config,        "CONFIG"),
855ef7374fSLi Qianruo            (self.num_cores,     "NUM_CORES")
86c11a4d2cSYinan Xu        ]
87c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, makefile_args)
88c11a4d2cSYinan Xu        return args
89c11a4d2cSYinan Xu
90c11a4d2cSYinan Xu    def get_emu_args(self):
91c11a4d2cSYinan Xu        emu_args = [
9259bcbb59SYinan Xu            (self.max_instr, "max-instr"),
9359bcbb59SYinan Xu            (self.seed,      "seed")
94c11a4d2cSYinan Xu        ]
95c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, emu_args)
96c11a4d2cSYinan Xu        return args
97c11a4d2cSYinan Xu
98c11a4d2cSYinan Xu    def show(self):
99c11a4d2cSYinan Xu        print("Extra environment variables:")
100c11a4d2cSYinan Xu        env = self.get_env_variables()
101c11a4d2cSYinan Xu        for env_name in env:
102c11a4d2cSYinan Xu            print(f"{env_name}: {env[env_name]}")
103c11a4d2cSYinan Xu        print()
104c11a4d2cSYinan Xu        print("Chisel arguments:")
105c11a4d2cSYinan Xu        print(" ".join(self.get_chisel_args()))
106c11a4d2cSYinan Xu        print()
107c11a4d2cSYinan Xu        print("Makefile arguments:")
108c11a4d2cSYinan Xu        for val, name in self.get_makefile_args():
109c11a4d2cSYinan Xu            print(f"{name}={val}")
110c11a4d2cSYinan Xu        print()
111c11a4d2cSYinan Xu        print("emu arguments:")
112c11a4d2cSYinan Xu        for val, name in self.get_emu_args():
113c11a4d2cSYinan Xu            print(f"--{name} {val}")
114c11a4d2cSYinan Xu        print()
115c11a4d2cSYinan Xu
116c11a4d2cSYinan Xu    def __extract_path(self, path, env=None, default=None):
117c11a4d2cSYinan Xu        if path is None and env is not None:
118c11a4d2cSYinan Xu            path = os.getenv(env)
119c11a4d2cSYinan Xu        if path is None and default is not None:
120c11a4d2cSYinan Xu            path = default
121c11a4d2cSYinan Xu        path = os.path.realpath(path)
122c11a4d2cSYinan Xu        return path
123c11a4d2cSYinan Xu
124c11a4d2cSYinan Xu    def set_noop_home(self, path):
125c11a4d2cSYinan Xu        self.noop_home = path
126c11a4d2cSYinan Xu
127c11a4d2cSYinan Xu    def set_nemu_home(self, path):
128c11a4d2cSYinan Xu        self.nemu_home = path
129c11a4d2cSYinan Xu
130c11a4d2cSYinan Xu    def set_am_home(self, path):
131c11a4d2cSYinan Xu        self.am_home = path
132c11a4d2cSYinan Xu
133c11a4d2cSYinan Xu    def set_dramsim3_home(self, path):
134c11a4d2cSYinan Xu        self.dramsim3_home = path
135c11a4d2cSYinan Xu
136c11a4d2cSYinan Xu    def set_rvtest_home(self, path):
137c11a4d2cSYinan Xu        self.rvtest_home = path
138c11a4d2cSYinan Xu
139c11a4d2cSYinan Xu# XiangShan environment
140c11a4d2cSYinan Xuclass XiangShan(object):
141c11a4d2cSYinan Xu    def __init__(self, args):
142c11a4d2cSYinan Xu        self.args = XSArgs(args)
143c11a4d2cSYinan Xu
144c11a4d2cSYinan Xu    def show(self):
145c11a4d2cSYinan Xu        self.args.show()
146c11a4d2cSYinan Xu
147c11a4d2cSYinan Xu    def generate_verilog(self):
148c11a4d2cSYinan Xu        print("Generating XiangShan verilog with the following configurations:")
149c11a4d2cSYinan Xu        self.show()
150c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
151c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
152c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
153c11a4d2cSYinan Xu        return return_code
154c11a4d2cSYinan Xu
155c11a4d2cSYinan Xu    def build_emu(self):
156c11a4d2cSYinan Xu        print("Building XiangShan emu with the following configurations:")
157c11a4d2cSYinan Xu        self.show()
158c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
159c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
160a3e87608SWilliam Wang        return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
161c11a4d2cSYinan Xu        return return_code
162c11a4d2cSYinan Xu
163c11a4d2cSYinan Xu    def run_emu(self, workload):
164c11a4d2cSYinan Xu        print("Running XiangShan emu with the following configurations:")
165c11a4d2cSYinan Xu        self.show()
166c11a4d2cSYinan Xu        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
167c11a4d2cSYinan Xu        print("workload:", workload)
168c11a4d2cSYinan Xu        numa_args = f"numactl -m 1 -C 64-{64+self.args.threads-1}" if self.args.numa else ""
169c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'{numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args}')
170c11a4d2cSYinan Xu        return return_code
171c11a4d2cSYinan Xu
172c11a4d2cSYinan Xu    def run(self, args):
173c11a4d2cSYinan Xu        if args.ci is not None:
174c11a4d2cSYinan Xu            return self.run_ci(args.ci)
175c11a4d2cSYinan Xu        actions = [
176c11a4d2cSYinan Xu            (args.generate, lambda _ : self.generate_verilog()),
177c11a4d2cSYinan Xu            (args.build, lambda _ : self.build_emu()),
178c11a4d2cSYinan Xu            (args.workload, lambda args: self.run_emu(args.workload))
179c11a4d2cSYinan Xu        ]
180c11a4d2cSYinan Xu        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
181c11a4d2cSYinan Xu        for i, action in enumerate(valid_actions):
182c11a4d2cSYinan Xu            print(f"Action {i}:")
183c11a4d2cSYinan Xu            ret = action(args)
184c11a4d2cSYinan Xu            if ret:
185c11a4d2cSYinan Xu                return ret
186c11a4d2cSYinan Xu        return 0
187c11a4d2cSYinan Xu
188c11a4d2cSYinan Xu    def __exec_cmd(self, cmd):
189c11a4d2cSYinan Xu        env = dict(os.environ)
190c11a4d2cSYinan Xu        env.update(self.args.get_env_variables())
191c11a4d2cSYinan Xu        print("subprocess call cmd:", cmd)
1925ef7374fSLi Qianruo        start = time.time()
193c11a4d2cSYinan Xu        return_code = subprocess.call(cmd, shell=True, env=env)
1945ef7374fSLi Qianruo        end = time.time()
1955ef7374fSLi Qianruo        print(f"Elapsed time: {end - start} seconds")
196c11a4d2cSYinan Xu        return return_code
197c11a4d2cSYinan Xu
198c11a4d2cSYinan Xu    def __get_ci_cputest(self, name=None):
199c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
200c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
201c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
202c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
203c11a4d2cSYinan Xu        return cputest
204c11a4d2cSYinan Xu
205c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
206c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
207c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
208c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
209c11a4d2cSYinan Xu        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
210c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
211c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
212c11a4d2cSYinan Xu        return riscv_tests
213c11a4d2cSYinan Xu
214675acc68SYinan Xu    def __get_ci_misc(self, name=None):
215675acc68SYinan Xu        base_dir = "/home/ci-runner/xsenv/workloads"
216675acc68SYinan Xu        workloads = [
217675acc68SYinan Xu            "bitmanip/bitMisc.bin",
2183feeca58Szfw            "crypto/crypto-riscv64-noop.bin",
219675acc68SYinan Xu            "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
220675acc68SYinan Xu            "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
22164a887e0SYinan Xu            "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
22235620aa8Swakafa            "ext_intr/amtest-riscv64-xs.bin",
223*3f4ec46fSCODE-JTZ            "cache-alias/aliastest-riscv64-xs.bin",
224*3f4ec46fSCODE-JTZ            "cache-management/softprefetch-riscv64-noop.bin"
225675acc68SYinan Xu        ]
226675acc68SYinan Xu        misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
227675acc68SYinan Xu        return misc_tests
228675acc68SYinan Xu
229c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
230c11a4d2cSYinan Xu        filename = f"{bench}-riscv64-noop.bin"
231c11a4d2cSYinan Xu        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
232c11a4d2cSYinan Xu
233c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
234c11a4d2cSYinan Xu        workloads = {
235c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
2365092a298Szfw            "povray": "_700480000000_.gz",
2375092a298Szfw            "mcf": "_17520000000_.gz",
2385092a298Szfw            "xalancbmk": "_266100000000_.gz",
2395092a298Szfw            "gcc": "_39720000000_.gz",
2405092a298Szfw            "namd": "_434640000000_.gz",
2415092a298Szfw            "milc": "_103620000000_.gz",
2425092a298Szfw            "lbm": "_140840000000_.gz",
2437b441e5eSYinan Xu            "gromacs": "_275480000000_.gz",
2447b441e5eSYinan Xu            "wrf": "_1916220000000_.gz",
2457b441e5eSYinan Xu            "astar": "_122060000000_.gz"
246c11a4d2cSYinan Xu        }
247c11a4d2cSYinan Xu        return [os.path.join("/home/ci-runner/xsenv/workloads", name, workloads[name])]
248c11a4d2cSYinan Xu
249c11a4d2cSYinan Xu    def run_ci(self, test):
250c11a4d2cSYinan Xu        all_tests = {
251c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
252c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
253675acc68SYinan Xu            "misc-tests": self.__get_ci_misc,
254c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
255c11a4d2cSYinan Xu            "coremark": self.__am_apps_path
256c11a4d2cSYinan Xu        }
257c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
258c11a4d2cSYinan Xu            print(target)
259c11a4d2cSYinan Xu            ret = self.run_emu(target)
260c11a4d2cSYinan Xu            if ret:
261c11a4d2cSYinan Xu                return ret
262c11a4d2cSYinan Xu        return 0
263c11a4d2cSYinan Xu
264c11a4d2cSYinan Xuif __name__ == "__main__":
265c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
266c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
267c11a4d2cSYinan Xu                        help='input workload file in binary format')
268c11a4d2cSYinan Xu    # actions
269c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
270c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
271c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
272c11a4d2cSYinan Xu    # environment variables
273c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
274c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
275c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
276c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
277c11a4d2cSYinan Xu    # chisel arguments
278c11a4d2cSYinan Xu    parser.add_argument('--disable-log', action='store_true', help='disable log')
2795ef7374fSLi Qianruo    parser.add_argument('--num-cores', type=int, help='number of cores')
280c11a4d2cSYinan Xu    # makefile arguments
281c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
282c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
283c11a4d2cSYinan Xu    parser.add_argument('--trace', action='store_true', help='enable waveform')
2846c0058d3SYinan Xu    parser.add_argument('--config', nargs='?', type=str, help='config')
285c11a4d2cSYinan Xu    # emu arguments
286c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
287c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
288c11a4d2cSYinan Xu
289c11a4d2cSYinan Xu    args = parser.parse_args()
290c11a4d2cSYinan Xu
291c11a4d2cSYinan Xu    xs = XiangShan(args)
292c11a4d2cSYinan Xu    ret = xs.run(args)
293c11a4d2cSYinan Xu
294c11a4d2cSYinan Xu    sys.exit(ret)
295