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