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 26*94e266cbSYinan Xuimport psutil 27*94e266cbSYinan Xu 28c11a4d2cSYinan Xu 29c11a4d2cSYinan Xuclass XSArgs(object): 30c11a4d2cSYinan Xu script_path = os.path.realpath(__file__) 31c11a4d2cSYinan Xu # default path to the repositories 32c11a4d2cSYinan Xu noop_home = os.path.join(os.path.dirname(script_path), "..") 33c11a4d2cSYinan Xu nemu_home = os.path.join(noop_home, "../NEMU") 34c11a4d2cSYinan Xu am_home = os.path.join(noop_home, "../nexus-am") 35c11a4d2cSYinan Xu dramsim3_home = os.path.join(noop_home, "../DRAMsim3") 36c11a4d2cSYinan Xu rvtest_home = os.path.join(noop_home, "../riscv-tests") 3724e2eab6SJinYue default_wave_home = os.path.join(noop_home, "build") 3824e2eab6SJinYue wave_home = default_wave_home 39c11a4d2cSYinan Xu 40c11a4d2cSYinan Xu def __init__(self, args): 41c11a4d2cSYinan Xu # all path environment variables that should be set 42c11a4d2cSYinan Xu all_path = [ 43c11a4d2cSYinan Xu # (python argument, environment variable, default, target function) 44c11a4d2cSYinan Xu (None, "NOOP_HOME", self.noop_home, self.set_noop_home), 45c11a4d2cSYinan Xu (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home), 46c11a4d2cSYinan Xu (args.am, "AM_HOME", self.am_home, self.set_am_home), 47c11a4d2cSYinan Xu (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home), 48c11a4d2cSYinan Xu (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home), 49c11a4d2cSYinan Xu ] 50c11a4d2cSYinan Xu for (arg_in, env, default, set_func) in all_path: 51c11a4d2cSYinan Xu set_func(self.__extract_path(arg_in, env, default)) 52c11a4d2cSYinan Xu # Chisel arguments 531545277aSYinan Xu self.enable_log = args.enable_log 545ef7374fSLi Qianruo self.num_cores = args.num_cores 55c11a4d2cSYinan Xu # Makefile arguments 56c11a4d2cSYinan Xu self.threads = args.threads 57c11a4d2cSYinan Xu self.with_dramsim3 = 1 if args.with_dramsim3 else None 581545277aSYinan Xu self.is_release = 1 if args.release else None 59eb852d4cSJinYue self.trace = 1 if args.trace or not args.disable_fork else None 606c0058d3SYinan Xu self.config = args.config 61c11a4d2cSYinan Xu # emu arguments 62c11a4d2cSYinan Xu self.max_instr = args.max_instr 6359bcbb59SYinan Xu self.seed = random.randint(0, 9999) 64c11a4d2cSYinan Xu self.numa = args.numa 65f9930da0SYinan Xu self.diff = args.diff 66078fde2bSJinYue self.fork = not args.disable_fork 6724e2eab6SJinYue # wave dump path 6824e2eab6SJinYue if args.wave_dump is not None: 6924e2eab6SJinYue self.set_wave_home(args.wave_dump) 7024e2eab6SJinYue else: 7124e2eab6SJinYue self.set_wave_home(self.default_wave_home) 72c11a4d2cSYinan Xu 73c11a4d2cSYinan Xu def get_env_variables(self): 74c11a4d2cSYinan Xu all_env = { 75c11a4d2cSYinan Xu "NOOP_HOME" : self.noop_home, 76c11a4d2cSYinan Xu "NEMU_HOME" : self.nemu_home, 7724e2eab6SJinYue "WAVE_HOME" : self.wave_home, 78c11a4d2cSYinan Xu "AM_HOME" : self.am_home, 79c11a4d2cSYinan Xu "DRAMSIM3_HOME": self.dramsim3_home 80c11a4d2cSYinan Xu } 81c11a4d2cSYinan Xu return all_env 82c11a4d2cSYinan Xu 83c11a4d2cSYinan Xu def get_chisel_args(self, prefix=None): 84c11a4d2cSYinan Xu chisel_args = [ 851545277aSYinan Xu (self.enable_log, "enable-log") 86c11a4d2cSYinan Xu ] 87c11a4d2cSYinan Xu args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args)) 88c11a4d2cSYinan Xu if prefix is not None: 89c11a4d2cSYinan Xu args = map(lambda x: prefix + x, args) 90c11a4d2cSYinan Xu return args 91c11a4d2cSYinan Xu 92c11a4d2cSYinan Xu def get_makefile_args(self): 93c11a4d2cSYinan Xu makefile_args = [ 94c11a4d2cSYinan Xu (self.threads, "EMU_THREADS"), 95c11a4d2cSYinan Xu (self.with_dramsim3, "WITH_DRAMSIM3"), 961545277aSYinan Xu (self.is_release, "RELEASE"), 976c0058d3SYinan Xu (self.trace, "EMU_TRACE"), 985ef7374fSLi Qianruo (self.config, "CONFIG"), 995ef7374fSLi Qianruo (self.num_cores, "NUM_CORES") 100c11a4d2cSYinan Xu ] 101c11a4d2cSYinan Xu args = filter(lambda arg: arg[0] is not None, makefile_args) 102c11a4d2cSYinan Xu return args 103c11a4d2cSYinan Xu 104c11a4d2cSYinan Xu def get_emu_args(self): 105c11a4d2cSYinan Xu emu_args = [ 10659bcbb59SYinan Xu (self.max_instr, "max-instr"), 107f9930da0SYinan Xu (self.diff, "diff"), 10859bcbb59SYinan Xu (self.seed, "seed") 109c11a4d2cSYinan Xu ] 110c11a4d2cSYinan Xu args = filter(lambda arg: arg[0] is not None, emu_args) 111c11a4d2cSYinan Xu return args 112c11a4d2cSYinan Xu 113c11a4d2cSYinan Xu def show(self): 114c11a4d2cSYinan Xu print("Extra environment variables:") 115c11a4d2cSYinan Xu env = self.get_env_variables() 116c11a4d2cSYinan Xu for env_name in env: 117c11a4d2cSYinan Xu print(f"{env_name}: {env[env_name]}") 118c11a4d2cSYinan Xu print() 119c11a4d2cSYinan Xu print("Chisel arguments:") 120c11a4d2cSYinan Xu print(" ".join(self.get_chisel_args())) 121c11a4d2cSYinan Xu print() 122c11a4d2cSYinan Xu print("Makefile arguments:") 123c11a4d2cSYinan Xu for val, name in self.get_makefile_args(): 124c11a4d2cSYinan Xu print(f"{name}={val}") 125c11a4d2cSYinan Xu print() 126c11a4d2cSYinan Xu print("emu arguments:") 127c11a4d2cSYinan Xu for val, name in self.get_emu_args(): 128c11a4d2cSYinan Xu print(f"--{name} {val}") 129c11a4d2cSYinan Xu print() 130c11a4d2cSYinan Xu 131c11a4d2cSYinan Xu def __extract_path(self, path, env=None, default=None): 132c11a4d2cSYinan Xu if path is None and env is not None: 133c11a4d2cSYinan Xu path = os.getenv(env) 134c11a4d2cSYinan Xu if path is None and default is not None: 135c11a4d2cSYinan Xu path = default 136c11a4d2cSYinan Xu path = os.path.realpath(path) 137c11a4d2cSYinan Xu return path 138c11a4d2cSYinan Xu 139c11a4d2cSYinan Xu def set_noop_home(self, path): 140c11a4d2cSYinan Xu self.noop_home = path 141c11a4d2cSYinan Xu 142c11a4d2cSYinan Xu def set_nemu_home(self, path): 143c11a4d2cSYinan Xu self.nemu_home = path 144c11a4d2cSYinan Xu 145c11a4d2cSYinan Xu def set_am_home(self, path): 146c11a4d2cSYinan Xu self.am_home = path 147c11a4d2cSYinan Xu 148c11a4d2cSYinan Xu def set_dramsim3_home(self, path): 149c11a4d2cSYinan Xu self.dramsim3_home = path 150c11a4d2cSYinan Xu 151c11a4d2cSYinan Xu def set_rvtest_home(self, path): 152c11a4d2cSYinan Xu self.rvtest_home = path 153c11a4d2cSYinan Xu 15424e2eab6SJinYue def set_wave_home(self, path): 15524e2eab6SJinYue print(f"set wave home to {path}") 15624e2eab6SJinYue self.wave_home = path 15724e2eab6SJinYue 158c11a4d2cSYinan Xu# XiangShan environment 159c11a4d2cSYinan Xuclass XiangShan(object): 160c11a4d2cSYinan Xu def __init__(self, args): 161c11a4d2cSYinan Xu self.args = XSArgs(args) 162c11a4d2cSYinan Xu 163c11a4d2cSYinan Xu def show(self): 164c11a4d2cSYinan Xu self.args.show() 165c11a4d2cSYinan Xu 166c11a4d2cSYinan Xu def generate_verilog(self): 167c11a4d2cSYinan Xu print("Generating XiangShan verilog with the following configurations:") 168c11a4d2cSYinan Xu self.show() 169c11a4d2cSYinan Xu sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 170c11a4d2cSYinan Xu make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 171c11a4d2cSYinan Xu return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}') 172c11a4d2cSYinan Xu return return_code 173c11a4d2cSYinan Xu 174c11a4d2cSYinan Xu def build_emu(self): 175c11a4d2cSYinan Xu print("Building XiangShan emu with the following configurations:") 176c11a4d2cSYinan Xu self.show() 177c11a4d2cSYinan Xu sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 178c11a4d2cSYinan Xu make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 179a3e87608SWilliam Wang return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}') 180c11a4d2cSYinan Xu return return_code 181c11a4d2cSYinan Xu 182c11a4d2cSYinan Xu def run_emu(self, workload): 183c11a4d2cSYinan Xu print("Running XiangShan emu with the following configurations:") 184c11a4d2cSYinan Xu self.show() 185c11a4d2cSYinan Xu emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args())) 186c11a4d2cSYinan Xu print("workload:", workload) 187*94e266cbSYinan Xu numa_info = get_free_cores(self.args.threads) 188*94e266cbSYinan Xu numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}" if self.args.numa else "" 189078fde2bSJinYue fork_args = "--enable-fork" if self.args.fork else "" 190078fde2bSJinYue return_code = self.__exec_cmd(f'{numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args} {fork_args}') 191c11a4d2cSYinan Xu return return_code 192c11a4d2cSYinan Xu 193c11a4d2cSYinan Xu def run(self, args): 194c11a4d2cSYinan Xu if args.ci is not None: 195c11a4d2cSYinan Xu return self.run_ci(args.ci) 196c11a4d2cSYinan Xu actions = [ 197c11a4d2cSYinan Xu (args.generate, lambda _ : self.generate_verilog()), 198c11a4d2cSYinan Xu (args.build, lambda _ : self.build_emu()), 199c11a4d2cSYinan Xu (args.workload, lambda args: self.run_emu(args.workload)) 200c11a4d2cSYinan Xu ] 201c11a4d2cSYinan Xu valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions)) 202c11a4d2cSYinan Xu for i, action in enumerate(valid_actions): 203c11a4d2cSYinan Xu print(f"Action {i}:") 204c11a4d2cSYinan Xu ret = action(args) 205c11a4d2cSYinan Xu if ret: 206c11a4d2cSYinan Xu return ret 207c11a4d2cSYinan Xu return 0 208c11a4d2cSYinan Xu 209c11a4d2cSYinan Xu def __exec_cmd(self, cmd): 210c11a4d2cSYinan Xu env = dict(os.environ) 211c11a4d2cSYinan Xu env.update(self.args.get_env_variables()) 212c11a4d2cSYinan Xu print("subprocess call cmd:", cmd) 2135ef7374fSLi Qianruo start = time.time() 214c11a4d2cSYinan Xu return_code = subprocess.call(cmd, shell=True, env=env) 2155ef7374fSLi Qianruo end = time.time() 2165ef7374fSLi Qianruo print(f"Elapsed time: {end - start} seconds") 217c11a4d2cSYinan Xu return return_code 218c11a4d2cSYinan Xu 219c11a4d2cSYinan Xu def __get_ci_cputest(self, name=None): 220c11a4d2cSYinan Xu base_dir = os.path.join(self.args.am_home, "tests/cputest/build") 221c11a4d2cSYinan Xu cputest = os.listdir(base_dir) 222c11a4d2cSYinan Xu cputest = filter(lambda x: x.endswith(".bin"), cputest) 223c11a4d2cSYinan Xu cputest = map(lambda x: os.path.join(base_dir, x), cputest) 224c11a4d2cSYinan Xu return cputest 225c11a4d2cSYinan Xu 226c11a4d2cSYinan Xu def __get_ci_rvtest(self, name=None): 227c11a4d2cSYinan Xu base_dir = os.path.join(self.args.rvtest_home, "isa/build") 228c11a4d2cSYinan Xu riscv_tests = os.listdir(base_dir) 229c11a4d2cSYinan Xu riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests) 230c11a4d2cSYinan Xu all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"] 231c11a4d2cSYinan Xu riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests) 232c11a4d2cSYinan Xu riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests) 233c11a4d2cSYinan Xu return riscv_tests 234c11a4d2cSYinan Xu 235675acc68SYinan Xu def __get_ci_misc(self, name=None): 236675acc68SYinan Xu base_dir = "/home/ci-runner/xsenv/workloads" 237675acc68SYinan Xu workloads = [ 238675acc68SYinan Xu "bitmanip/bitMisc.bin", 2393feeca58Szfw "crypto/crypto-riscv64-noop.bin", 240675acc68SYinan Xu "coremark_rv64gc_o2/coremark-riscv64-xs.bin", 241675acc68SYinan Xu "coremark_rv64gc_o3/coremark-riscv64-xs.bin", 24264a887e0SYinan Xu "coremark_rv64gcb_o3/coremark-riscv64-xs.bin", 24335620aa8Swakafa "ext_intr/amtest-riscv64-xs.bin", 2443f4ec46fSCODE-JTZ "cache-alias/aliastest-riscv64-xs.bin", 245af2f7849Shappy-lx "Svinval/rv64mi-p-svinval.bin", 246b6982e83SLemover "pmp/pmp.riscv.bin", 24745f497a4Shappy-lx "asid/asid.bin", 2483f4ec46fSCODE-JTZ "cache-management/softprefetch-riscv64-noop.bin" 249675acc68SYinan Xu ] 250675acc68SYinan Xu misc_tests = map(lambda x: os.path.join(base_dir, x), workloads) 251675acc68SYinan Xu return misc_tests 252675acc68SYinan Xu 253c11a4d2cSYinan Xu def __am_apps_path(self, bench): 254c11a4d2cSYinan Xu filename = f"{bench}-riscv64-noop.bin" 255c11a4d2cSYinan Xu return [os.path.join(self.args.am_home, "apps", bench, "build", filename)] 256c11a4d2cSYinan Xu 257c11a4d2cSYinan Xu def __get_ci_workloads(self, name): 258c11a4d2cSYinan Xu workloads = { 259c11a4d2cSYinan Xu "linux-hello": "bbl.bin", 2605092a298Szfw "povray": "_700480000000_.gz", 2615092a298Szfw "mcf": "_17520000000_.gz", 2625092a298Szfw "xalancbmk": "_266100000000_.gz", 2635092a298Szfw "gcc": "_39720000000_.gz", 2645092a298Szfw "namd": "_434640000000_.gz", 2655092a298Szfw "milc": "_103620000000_.gz", 2665092a298Szfw "lbm": "_140840000000_.gz", 2677b441e5eSYinan Xu "gromacs": "_275480000000_.gz", 2687b441e5eSYinan Xu "wrf": "_1916220000000_.gz", 2697b441e5eSYinan Xu "astar": "_122060000000_.gz" 270c11a4d2cSYinan Xu } 271c11a4d2cSYinan Xu return [os.path.join("/home/ci-runner/xsenv/workloads", name, workloads[name])] 272c11a4d2cSYinan Xu 273c11a4d2cSYinan Xu def run_ci(self, test): 274c11a4d2cSYinan Xu all_tests = { 275c11a4d2cSYinan Xu "cputest": self.__get_ci_cputest, 276c11a4d2cSYinan Xu "riscv-tests": self.__get_ci_rvtest, 277675acc68SYinan Xu "misc-tests": self.__get_ci_misc, 278c11a4d2cSYinan Xu "microbench": self.__am_apps_path, 279c11a4d2cSYinan Xu "coremark": self.__am_apps_path 280c11a4d2cSYinan Xu } 281c11a4d2cSYinan Xu for target in all_tests.get(test, self.__get_ci_workloads)(test): 282c11a4d2cSYinan Xu print(target) 283c11a4d2cSYinan Xu ret = self.run_emu(target) 284c11a4d2cSYinan Xu if ret: 28524e2eab6SJinYue if self.args.default_wave_home != self.args.wave_home: 28624e2eab6SJinYue print("copy wave file to " + self.args.wave_home) 28724e2eab6SJinYue self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME") 288bc063562SLemover self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME") 289bc063562SLemover self.__exec_cmd(f"cp $NOOP_HOME/build/SimTop.v $WAVE_HOME") 290c11a4d2cSYinan Xu return ret 291c11a4d2cSYinan Xu return 0 292c11a4d2cSYinan Xu 293*94e266cbSYinan Xudef get_free_cores(n): 294*94e266cbSYinan Xu while True: 295*94e266cbSYinan Xu # To avoid potential conflicts, we allow CI to use SMT. 296*94e266cbSYinan Xu num_logical_core = psutil.cpu_count(logical=True) 297*94e266cbSYinan Xu core_usage = psutil.cpu_percent(interval=1, percpu=True) 298*94e266cbSYinan Xu num_window = num_logical_core // n 299*94e266cbSYinan Xu for i in range(num_window): 300*94e266cbSYinan Xu window_usage = core_usage[i * n : i * n + n] 301*94e266cbSYinan Xu if sum(window_usage) < 0.3 * n and True not in map(lambda x: x > 0.5, window_usage): 302*94e266cbSYinan Xu return (((i * n) % 128)// 64, i * n, i * n + n - 1) 303*94e266cbSYinan Xu print(f"No free {n} cores found. CPU usage: {core_usage}\n") 304*94e266cbSYinan Xu 305c11a4d2cSYinan Xuif __name__ == "__main__": 306c11a4d2cSYinan Xu parser = argparse.ArgumentParser(description='Python wrapper for XiangShan') 307c11a4d2cSYinan Xu parser.add_argument('workload', nargs='?', type=str, default="", 308c11a4d2cSYinan Xu help='input workload file in binary format') 309c11a4d2cSYinan Xu # actions 310c11a4d2cSYinan Xu parser.add_argument('--build', action='store_true', help='build XS emu') 311c11a4d2cSYinan Xu parser.add_argument('--generate', action='store_true', help='generate XS verilog') 312c11a4d2cSYinan Xu parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests') 313c11a4d2cSYinan Xu # environment variables 314c11a4d2cSYinan Xu parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu') 315c11a4d2cSYinan Xu parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am') 316c11a4d2cSYinan Xu parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3') 317c11a4d2cSYinan Xu parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests') 31824e2eab6SJinYue parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave') 319c11a4d2cSYinan Xu # chisel arguments 3201545277aSYinan Xu parser.add_argument('--enable-log', action='store_true', help='enable log') 3215ef7374fSLi Qianruo parser.add_argument('--num-cores', type=int, help='number of cores') 322c11a4d2cSYinan Xu # makefile arguments 3231545277aSYinan Xu parser.add_argument('--release', action='store_true', help='enable release') 324c11a4d2cSYinan Xu parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3') 325c11a4d2cSYinan Xu parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads') 326c11a4d2cSYinan Xu parser.add_argument('--trace', action='store_true', help='enable waveform') 3276c0058d3SYinan Xu parser.add_argument('--config', nargs='?', type=str, help='config') 328c11a4d2cSYinan Xu # emu arguments 329c11a4d2cSYinan Xu parser.add_argument('--numa', action='store_true', help='use numactl') 330f9930da0SYinan Xu parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so') 331c11a4d2cSYinan Xu parser.add_argument('--max-instr', nargs='?', type=int, help='max instr') 332078fde2bSJinYue parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS') 33324e2eab6SJinYue # ci action head sha 334c11a4d2cSYinan Xu 335c11a4d2cSYinan Xu args = parser.parse_args() 336c11a4d2cSYinan Xu 337c11a4d2cSYinan Xu xs = XiangShan(args) 338c11a4d2cSYinan Xu ret = xs.run(args) 339c11a4d2cSYinan Xu 340c11a4d2cSYinan Xu sys.exit(ret) 341