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