xref: /XiangShan/scripts/xiangshan.py (revision c6d439803a044ea209139672b25e35fe8d7f4aa0)
1*c6d43980SLemover#***************************************************************************************
2*c6d43980SLemover# Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3*c6d43980SLemover#
4*c6d43980SLemover# XiangShan is licensed under Mulan PSL v2.
5*c6d43980SLemover# You can use this software according to the terms and conditions of the Mulan PSL v2.
6*c6d43980SLemover# You may obtain a copy of Mulan PSL v2 at:
7*c6d43980SLemover#          http://license.coscl.org.cn/MulanPSL2
8*c6d43980SLemover#
9*c6d43980SLemover# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
10*c6d43980SLemover# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
11*c6d43980SLemover# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
12*c6d43980SLemover#
13*c6d43980SLemover# See the Mulan PSL v2 for more details.
14*c6d43980SLemover#***************************************************************************************
15*c6d43980SLemover
16c11a4d2cSYinan Xu# Simple version of xiangshan python wrapper
17c11a4d2cSYinan Xu
18c11a4d2cSYinan Xuimport os
19c11a4d2cSYinan Xuimport argparse
20c11a4d2cSYinan Xuimport sys
21c11a4d2cSYinan Xuimport subprocess
22c11a4d2cSYinan Xu
23c11a4d2cSYinan Xu
24c11a4d2cSYinan Xuclass XSArgs(object):
25c11a4d2cSYinan Xu    script_path = os.path.realpath(__file__)
26c11a4d2cSYinan Xu    # default path to the repositories
27c11a4d2cSYinan Xu    noop_home = os.path.join(os.path.dirname(script_path), "..")
28c11a4d2cSYinan Xu    nemu_home = os.path.join(noop_home, "../NEMU")
29c11a4d2cSYinan Xu    am_home = os.path.join(noop_home, "../nexus-am")
30c11a4d2cSYinan Xu    dramsim3_home = os.path.join(noop_home, "../DRAMsim3")
31c11a4d2cSYinan Xu    rvtest_home = os.path.join(noop_home, "../riscv-tests")
32c11a4d2cSYinan Xu
33c11a4d2cSYinan Xu    def __init__(self, args):
34c11a4d2cSYinan Xu        # all path environment variables that should be set
35c11a4d2cSYinan Xu        all_path = [
36c11a4d2cSYinan Xu            # (python argument, environment variable, default, target function)
37c11a4d2cSYinan Xu            (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
38c11a4d2cSYinan Xu            (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
39c11a4d2cSYinan Xu            (args.am, "AM_HOME", self.am_home, self.set_am_home),
40c11a4d2cSYinan Xu            (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
41c11a4d2cSYinan Xu            (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
42c11a4d2cSYinan Xu        ]
43c11a4d2cSYinan Xu        for (arg_in, env, default, set_func) in all_path:
44c11a4d2cSYinan Xu            set_func(self.__extract_path(arg_in, env, default))
45c11a4d2cSYinan Xu        # Chisel arguments
46c11a4d2cSYinan Xu        self.disable_log = args.disable_log
47c11a4d2cSYinan Xu        self.dual_core = args.dual_core
48c11a4d2cSYinan Xu        # Makefile arguments
49c11a4d2cSYinan Xu        self.threads = args.threads
50c11a4d2cSYinan Xu        self.with_dramsim3 = 1 if args.with_dramsim3 else None
51c11a4d2cSYinan Xu        self.trace = 1 if args.trace else None
52c11a4d2cSYinan Xu        # emu arguments
53c11a4d2cSYinan Xu        self.max_instr = args.max_instr
54c11a4d2cSYinan Xu        self.numa = args.numa
55c11a4d2cSYinan Xu
56c11a4d2cSYinan Xu    def get_env_variables(self):
57c11a4d2cSYinan Xu        all_env = {
58c11a4d2cSYinan Xu            "NOOP_HOME"    : self.noop_home,
59c11a4d2cSYinan Xu            "NEMU_HOME"    : self.nemu_home,
60c11a4d2cSYinan Xu            "AM_HOME"      : self.am_home,
61c11a4d2cSYinan Xu            "DRAMSIM3_HOME": self.dramsim3_home
62c11a4d2cSYinan Xu        }
63c11a4d2cSYinan Xu        return all_env
64c11a4d2cSYinan Xu
65c11a4d2cSYinan Xu    def get_chisel_args(self, prefix=None):
66c11a4d2cSYinan Xu        chisel_args = [
67c11a4d2cSYinan Xu            (self.disable_log, "disable-log"),
68c11a4d2cSYinan Xu            (self.dual_core,   "dual-core")
69c11a4d2cSYinan Xu        ]
70c11a4d2cSYinan Xu        args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
71c11a4d2cSYinan Xu        if prefix is not None:
72c11a4d2cSYinan Xu            args = map(lambda x: prefix + x, args)
73c11a4d2cSYinan Xu        return args
74c11a4d2cSYinan Xu
75c11a4d2cSYinan Xu    def get_makefile_args(self):
76c11a4d2cSYinan Xu        makefile_args = [
77c11a4d2cSYinan Xu            (self.threads, "EMU_THREADS"),
78c11a4d2cSYinan Xu            (self.with_dramsim3, "WITH_DRAMSIM3"),
79c11a4d2cSYinan Xu            (self.trace, "EMU_TRACE")
80c11a4d2cSYinan Xu        ]
81c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, makefile_args)
82c11a4d2cSYinan Xu        return args
83c11a4d2cSYinan Xu
84c11a4d2cSYinan Xu    def get_emu_args(self):
85c11a4d2cSYinan Xu        emu_args = [
86c11a4d2cSYinan Xu            (self.max_instr, "max-instr")
87c11a4d2cSYinan Xu        ]
88c11a4d2cSYinan Xu        args = filter(lambda arg: arg[0] is not None, emu_args)
89c11a4d2cSYinan Xu        return args
90c11a4d2cSYinan Xu
91c11a4d2cSYinan Xu    def show(self):
92c11a4d2cSYinan Xu        print("Extra environment variables:")
93c11a4d2cSYinan Xu        env = self.get_env_variables()
94c11a4d2cSYinan Xu        for env_name in env:
95c11a4d2cSYinan Xu            print(f"{env_name}: {env[env_name]}")
96c11a4d2cSYinan Xu        print()
97c11a4d2cSYinan Xu        print("Chisel arguments:")
98c11a4d2cSYinan Xu        print(" ".join(self.get_chisel_args()))
99c11a4d2cSYinan Xu        print()
100c11a4d2cSYinan Xu        print("Makefile arguments:")
101c11a4d2cSYinan Xu        for val, name in self.get_makefile_args():
102c11a4d2cSYinan Xu            print(f"{name}={val}")
103c11a4d2cSYinan Xu        print()
104c11a4d2cSYinan Xu        print("emu arguments:")
105c11a4d2cSYinan Xu        for val, name in self.get_emu_args():
106c11a4d2cSYinan Xu            print(f"--{name} {val}")
107c11a4d2cSYinan Xu        print()
108c11a4d2cSYinan Xu
109c11a4d2cSYinan Xu    def __extract_path(self, path, env=None, default=None):
110c11a4d2cSYinan Xu        if path is None and env is not None:
111c11a4d2cSYinan Xu            path = os.getenv(env)
112c11a4d2cSYinan Xu        if path is None and default is not None:
113c11a4d2cSYinan Xu            path = default
114c11a4d2cSYinan Xu        path = os.path.realpath(path)
115c11a4d2cSYinan Xu        return path
116c11a4d2cSYinan Xu
117c11a4d2cSYinan Xu    def set_noop_home(self, path):
118c11a4d2cSYinan Xu        self.noop_home = path
119c11a4d2cSYinan Xu
120c11a4d2cSYinan Xu    def set_nemu_home(self, path):
121c11a4d2cSYinan Xu        self.nemu_home = path
122c11a4d2cSYinan Xu
123c11a4d2cSYinan Xu    def set_am_home(self, path):
124c11a4d2cSYinan Xu        self.am_home = path
125c11a4d2cSYinan Xu
126c11a4d2cSYinan Xu    def set_dramsim3_home(self, path):
127c11a4d2cSYinan Xu        self.dramsim3_home = path
128c11a4d2cSYinan Xu
129c11a4d2cSYinan Xu    def set_rvtest_home(self, path):
130c11a4d2cSYinan Xu        self.rvtest_home = path
131c11a4d2cSYinan Xu
132c11a4d2cSYinan Xu# XiangShan environment
133c11a4d2cSYinan Xuclass XiangShan(object):
134c11a4d2cSYinan Xu    def __init__(self, args):
135c11a4d2cSYinan Xu        self.args = XSArgs(args)
136c11a4d2cSYinan Xu
137c11a4d2cSYinan Xu    def show(self):
138c11a4d2cSYinan Xu        self.args.show()
139c11a4d2cSYinan Xu
140c11a4d2cSYinan Xu    def generate_verilog(self):
141c11a4d2cSYinan Xu        print("Generating XiangShan verilog with the following configurations:")
142c11a4d2cSYinan Xu        self.show()
143c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
144c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
145c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
146c11a4d2cSYinan Xu        return return_code
147c11a4d2cSYinan Xu
148c11a4d2cSYinan Xu    def build_emu(self):
149c11a4d2cSYinan Xu        print("Building XiangShan emu with the following configurations:")
150c11a4d2cSYinan Xu        self.show()
151c11a4d2cSYinan Xu        sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
152c11a4d2cSYinan Xu        make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
153c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'make -C $NOOP_HOME ./build/emu -j200 SIM_ARGS="{sim_args}" {make_args}')
154c11a4d2cSYinan Xu        return return_code
155c11a4d2cSYinan Xu
156c11a4d2cSYinan Xu    def run_emu(self, workload):
157c11a4d2cSYinan Xu        print("Running XiangShan emu with the following configurations:")
158c11a4d2cSYinan Xu        self.show()
159c11a4d2cSYinan Xu        emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
160c11a4d2cSYinan Xu        print("workload:", workload)
161c11a4d2cSYinan Xu        numa_args = f"numactl -m 1 -C 64-{64+self.args.threads-1}" if self.args.numa else ""
162c11a4d2cSYinan Xu        return_code = self.__exec_cmd(f'{numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args}')
163c11a4d2cSYinan Xu        return return_code
164c11a4d2cSYinan Xu
165c11a4d2cSYinan Xu    def run(self, args):
166c11a4d2cSYinan Xu        if args.ci is not None:
167c11a4d2cSYinan Xu            return self.run_ci(args.ci)
168c11a4d2cSYinan Xu        actions = [
169c11a4d2cSYinan Xu            (args.generate, lambda _ : self.generate_verilog()),
170c11a4d2cSYinan Xu            (args.build, lambda _ : self.build_emu()),
171c11a4d2cSYinan Xu            (args.workload, lambda args: self.run_emu(args.workload))
172c11a4d2cSYinan Xu        ]
173c11a4d2cSYinan Xu        valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
174c11a4d2cSYinan Xu        for i, action in enumerate(valid_actions):
175c11a4d2cSYinan Xu            print(f"Action {i}:")
176c11a4d2cSYinan Xu            ret = action(args)
177c11a4d2cSYinan Xu            if ret:
178c11a4d2cSYinan Xu                return ret
179c11a4d2cSYinan Xu        return 0
180c11a4d2cSYinan Xu
181c11a4d2cSYinan Xu    def __exec_cmd(self, cmd):
182c11a4d2cSYinan Xu        env = dict(os.environ)
183c11a4d2cSYinan Xu        env.update(self.args.get_env_variables())
184c11a4d2cSYinan Xu        print("subprocess call cmd:", cmd)
185c11a4d2cSYinan Xu        return_code = subprocess.call(cmd, shell=True, env=env)
186c11a4d2cSYinan Xu        return return_code
187c11a4d2cSYinan Xu
188c11a4d2cSYinan Xu    def __get_ci_cputest(self, name=None):
189c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
190c11a4d2cSYinan Xu        cputest = os.listdir(base_dir)
191c11a4d2cSYinan Xu        cputest = filter(lambda x: x.endswith(".bin"), cputest)
192c11a4d2cSYinan Xu        cputest = map(lambda x: os.path.join(base_dir, x), cputest)
193c11a4d2cSYinan Xu        return cputest
194c11a4d2cSYinan Xu
195c11a4d2cSYinan Xu    def __get_ci_rvtest(self, name=None):
196c11a4d2cSYinan Xu        base_dir = os.path.join(self.args.rvtest_home, "isa/build")
197c11a4d2cSYinan Xu        riscv_tests = os.listdir(base_dir)
198c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
199c11a4d2cSYinan Xu        all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"]
200c11a4d2cSYinan Xu        riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
201c11a4d2cSYinan Xu        riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
202c11a4d2cSYinan Xu        return riscv_tests
203c11a4d2cSYinan Xu
204c11a4d2cSYinan Xu    def __am_apps_path(self, bench):
205c11a4d2cSYinan Xu        filename = f"{bench}-riscv64-noop.bin"
206c11a4d2cSYinan Xu        return [os.path.join(self.args.am_home, "apps", bench, "build", filename)]
207c11a4d2cSYinan Xu
208c11a4d2cSYinan Xu    def __get_ci_workloads(self, name):
209c11a4d2cSYinan Xu        workloads = {
210c11a4d2cSYinan Xu            "linux-hello": "bbl.bin",
211c11a4d2cSYinan Xu            "povray": "_3400001000_.gz",
212c11a4d2cSYinan Xu            "mcf": "_2550001000_.gz",
213c11a4d2cSYinan Xu            "xalancbmk": "_6600001000_.gz",
214c11a4d2cSYinan Xu            "gcc": "_1250001000_.gz",
215c11a4d2cSYinan Xu            "namd": "_4850001000_.gz",
216c11a4d2cSYinan Xu            "milc": "_4150001000_.gz",
217c11a4d2cSYinan Xu            "lbm": "_7550001000_.gz",
218c11a4d2cSYinan Xu            "gromacs": "_3150001000_.gz"
219c11a4d2cSYinan Xu        }
220c11a4d2cSYinan Xu        return [os.path.join("/home/ci-runner/xsenv/workloads", name, workloads[name])]
221c11a4d2cSYinan Xu
222c11a4d2cSYinan Xu    def run_ci(self, test):
223c11a4d2cSYinan Xu        all_tests = {
224c11a4d2cSYinan Xu            "cputest": self.__get_ci_cputest,
225c11a4d2cSYinan Xu            "riscv-tests": self.__get_ci_rvtest,
226c11a4d2cSYinan Xu            "microbench": self.__am_apps_path,
227c11a4d2cSYinan Xu            "coremark": self.__am_apps_path
228c11a4d2cSYinan Xu        }
229c11a4d2cSYinan Xu        for target in all_tests.get(test, self.__get_ci_workloads)(test):
230c11a4d2cSYinan Xu            print(target)
231c11a4d2cSYinan Xu            ret = self.run_emu(target)
232c11a4d2cSYinan Xu            if ret:
233c11a4d2cSYinan Xu                return ret
234c11a4d2cSYinan Xu        return 0
235c11a4d2cSYinan Xu
236c11a4d2cSYinan Xuif __name__ == "__main__":
237c11a4d2cSYinan Xu    parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
238c11a4d2cSYinan Xu    parser.add_argument('workload', nargs='?', type=str, default="",
239c11a4d2cSYinan Xu                        help='input workload file in binary format')
240c11a4d2cSYinan Xu    # actions
241c11a4d2cSYinan Xu    parser.add_argument('--build', action='store_true', help='build XS emu')
242c11a4d2cSYinan Xu    parser.add_argument('--generate', action='store_true', help='generate XS verilog')
243c11a4d2cSYinan Xu    parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
244c11a4d2cSYinan Xu    # environment variables
245c11a4d2cSYinan Xu    parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
246c11a4d2cSYinan Xu    parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
247c11a4d2cSYinan Xu    parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
248c11a4d2cSYinan Xu    parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
249c11a4d2cSYinan Xu    # chisel arguments
250c11a4d2cSYinan Xu    parser.add_argument('--disable-log', action='store_true', help='disable log')
251c11a4d2cSYinan Xu    parser.add_argument('--dual-core', action='store_true', help='dual core')
252c11a4d2cSYinan Xu    # makefile arguments
253c11a4d2cSYinan Xu    parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
254c11a4d2cSYinan Xu    parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
255c11a4d2cSYinan Xu    parser.add_argument('--trace', action='store_true', help='enable waveform')
256c11a4d2cSYinan Xu    # emu arguments
257c11a4d2cSYinan Xu    parser.add_argument('--numa', action='store_true', help='use numactl')
258c11a4d2cSYinan Xu    parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
259c11a4d2cSYinan Xu
260c11a4d2cSYinan Xu    args = parser.parse_args()
261c11a4d2cSYinan Xu
262c11a4d2cSYinan Xu    xs = XiangShan(args)
263c11a4d2cSYinan Xu    ret = xs.run(args)
264c11a4d2cSYinan Xu
265c11a4d2cSYinan Xu    sys.exit(ret)
266