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") 3524e2eab6SJinYue default_wave_home = os.path.join(noop_home, "build") 3624e2eab6SJinYue wave_home = default_wave_home 37c11a4d2cSYinan Xu 38c11a4d2cSYinan Xu def __init__(self, args): 39c11a4d2cSYinan Xu # all path environment variables that should be set 40c11a4d2cSYinan Xu all_path = [ 41c11a4d2cSYinan Xu # (python argument, environment variable, default, target function) 42c11a4d2cSYinan Xu (None, "NOOP_HOME", self.noop_home, self.set_noop_home), 43c11a4d2cSYinan Xu (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home), 44c11a4d2cSYinan Xu (args.am, "AM_HOME", self.am_home, self.set_am_home), 45c11a4d2cSYinan Xu (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home), 46c11a4d2cSYinan Xu (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home), 47c11a4d2cSYinan Xu ] 48c11a4d2cSYinan Xu for (arg_in, env, default, set_func) in all_path: 49c11a4d2cSYinan Xu set_func(self.__extract_path(arg_in, env, default)) 50c11a4d2cSYinan Xu # Chisel arguments 51*1545277aSYinan Xu self.enable_log = args.enable_log 525ef7374fSLi Qianruo self.num_cores = args.num_cores 53c11a4d2cSYinan Xu # Makefile arguments 54c11a4d2cSYinan Xu self.threads = args.threads 55c11a4d2cSYinan Xu self.with_dramsim3 = 1 if args.with_dramsim3 else None 56*1545277aSYinan Xu self.is_release = 1 if args.release else None 57eb852d4cSJinYue self.trace = 1 if args.trace or not args.disable_fork else None 586c0058d3SYinan Xu self.config = args.config 59c11a4d2cSYinan Xu # emu arguments 60c11a4d2cSYinan Xu self.max_instr = args.max_instr 6159bcbb59SYinan Xu self.seed = random.randint(0, 9999) 62c11a4d2cSYinan Xu self.numa = args.numa 63f9930da0SYinan Xu self.diff = args.diff 64078fde2bSJinYue self.fork = not args.disable_fork 6524e2eab6SJinYue # wave dump path 6624e2eab6SJinYue if args.wave_dump is not None: 6724e2eab6SJinYue self.set_wave_home(args.wave_dump) 6824e2eab6SJinYue else: 6924e2eab6SJinYue self.set_wave_home(self.default_wave_home) 70c11a4d2cSYinan Xu 71c11a4d2cSYinan Xu def get_env_variables(self): 72c11a4d2cSYinan Xu all_env = { 73c11a4d2cSYinan Xu "NOOP_HOME" : self.noop_home, 74c11a4d2cSYinan Xu "NEMU_HOME" : self.nemu_home, 7524e2eab6SJinYue "WAVE_HOME" : self.wave_home, 76c11a4d2cSYinan Xu "AM_HOME" : self.am_home, 77c11a4d2cSYinan Xu "DRAMSIM3_HOME": self.dramsim3_home 78c11a4d2cSYinan Xu } 79c11a4d2cSYinan Xu return all_env 80c11a4d2cSYinan Xu 81c11a4d2cSYinan Xu def get_chisel_args(self, prefix=None): 82c11a4d2cSYinan Xu chisel_args = [ 83*1545277aSYinan Xu (self.enable_log, "enable-log") 84c11a4d2cSYinan Xu ] 85c11a4d2cSYinan Xu args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args)) 86c11a4d2cSYinan Xu if prefix is not None: 87c11a4d2cSYinan Xu args = map(lambda x: prefix + x, args) 88c11a4d2cSYinan Xu return args 89c11a4d2cSYinan Xu 90c11a4d2cSYinan Xu def get_makefile_args(self): 91c11a4d2cSYinan Xu makefile_args = [ 92c11a4d2cSYinan Xu (self.threads, "EMU_THREADS"), 93c11a4d2cSYinan Xu (self.with_dramsim3, "WITH_DRAMSIM3"), 94*1545277aSYinan Xu (self.is_release, "RELEASE"), 956c0058d3SYinan Xu (self.trace, "EMU_TRACE"), 965ef7374fSLi Qianruo (self.config, "CONFIG"), 975ef7374fSLi Qianruo (self.num_cores, "NUM_CORES") 98c11a4d2cSYinan Xu ] 99c11a4d2cSYinan Xu args = filter(lambda arg: arg[0] is not None, makefile_args) 100c11a4d2cSYinan Xu return args 101c11a4d2cSYinan Xu 102c11a4d2cSYinan Xu def get_emu_args(self): 103c11a4d2cSYinan Xu emu_args = [ 10459bcbb59SYinan Xu (self.max_instr, "max-instr"), 105f9930da0SYinan Xu (self.diff, "diff"), 10659bcbb59SYinan Xu (self.seed, "seed") 107c11a4d2cSYinan Xu ] 108c11a4d2cSYinan Xu args = filter(lambda arg: arg[0] is not None, emu_args) 109c11a4d2cSYinan Xu return args 110c11a4d2cSYinan Xu 111c11a4d2cSYinan Xu def show(self): 112c11a4d2cSYinan Xu print("Extra environment variables:") 113c11a4d2cSYinan Xu env = self.get_env_variables() 114c11a4d2cSYinan Xu for env_name in env: 115c11a4d2cSYinan Xu print(f"{env_name}: {env[env_name]}") 116c11a4d2cSYinan Xu print() 117c11a4d2cSYinan Xu print("Chisel arguments:") 118c11a4d2cSYinan Xu print(" ".join(self.get_chisel_args())) 119c11a4d2cSYinan Xu print() 120c11a4d2cSYinan Xu print("Makefile arguments:") 121c11a4d2cSYinan Xu for val, name in self.get_makefile_args(): 122c11a4d2cSYinan Xu print(f"{name}={val}") 123c11a4d2cSYinan Xu print() 124c11a4d2cSYinan Xu print("emu arguments:") 125c11a4d2cSYinan Xu for val, name in self.get_emu_args(): 126c11a4d2cSYinan Xu print(f"--{name} {val}") 127c11a4d2cSYinan Xu print() 128c11a4d2cSYinan Xu 129c11a4d2cSYinan Xu def __extract_path(self, path, env=None, default=None): 130c11a4d2cSYinan Xu if path is None and env is not None: 131c11a4d2cSYinan Xu path = os.getenv(env) 132c11a4d2cSYinan Xu if path is None and default is not None: 133c11a4d2cSYinan Xu path = default 134c11a4d2cSYinan Xu path = os.path.realpath(path) 135c11a4d2cSYinan Xu return path 136c11a4d2cSYinan Xu 137c11a4d2cSYinan Xu def set_noop_home(self, path): 138c11a4d2cSYinan Xu self.noop_home = path 139c11a4d2cSYinan Xu 140c11a4d2cSYinan Xu def set_nemu_home(self, path): 141c11a4d2cSYinan Xu self.nemu_home = path 142c11a4d2cSYinan Xu 143c11a4d2cSYinan Xu def set_am_home(self, path): 144c11a4d2cSYinan Xu self.am_home = path 145c11a4d2cSYinan Xu 146c11a4d2cSYinan Xu def set_dramsim3_home(self, path): 147c11a4d2cSYinan Xu self.dramsim3_home = path 148c11a4d2cSYinan Xu 149c11a4d2cSYinan Xu def set_rvtest_home(self, path): 150c11a4d2cSYinan Xu self.rvtest_home = path 151c11a4d2cSYinan Xu 15224e2eab6SJinYue def set_wave_home(self, path): 15324e2eab6SJinYue print(f"set wave home to {path}") 15424e2eab6SJinYue self.wave_home = path 15524e2eab6SJinYue 156c11a4d2cSYinan Xu# XiangShan environment 157c11a4d2cSYinan Xuclass XiangShan(object): 158c11a4d2cSYinan Xu def __init__(self, args): 159c11a4d2cSYinan Xu self.args = XSArgs(args) 160c11a4d2cSYinan Xu 161c11a4d2cSYinan Xu def show(self): 162c11a4d2cSYinan Xu self.args.show() 163c11a4d2cSYinan Xu 164c11a4d2cSYinan Xu def generate_verilog(self): 165c11a4d2cSYinan Xu print("Generating XiangShan verilog with the following configurations:") 166c11a4d2cSYinan Xu self.show() 167c11a4d2cSYinan Xu sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 168c11a4d2cSYinan Xu make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 169c11a4d2cSYinan Xu return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}') 170c11a4d2cSYinan Xu return return_code 171c11a4d2cSYinan Xu 172c11a4d2cSYinan Xu def build_emu(self): 173c11a4d2cSYinan Xu print("Building XiangShan emu with the following configurations:") 174c11a4d2cSYinan Xu self.show() 175c11a4d2cSYinan Xu sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 176c11a4d2cSYinan Xu make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 177a3e87608SWilliam Wang return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}') 178c11a4d2cSYinan Xu return return_code 179c11a4d2cSYinan Xu 180c11a4d2cSYinan Xu def run_emu(self, workload): 181c11a4d2cSYinan Xu print("Running XiangShan emu with the following configurations:") 182c11a4d2cSYinan Xu self.show() 183c11a4d2cSYinan Xu emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args())) 184c11a4d2cSYinan Xu print("workload:", workload) 185c11a4d2cSYinan Xu numa_args = f"numactl -m 1 -C 64-{64+self.args.threads-1}" if self.args.numa else "" 186078fde2bSJinYue fork_args = "--enable-fork" if self.args.fork else "" 187078fde2bSJinYue return_code = self.__exec_cmd(f'{numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args} {fork_args}') 188c11a4d2cSYinan Xu return return_code 189c11a4d2cSYinan Xu 190c11a4d2cSYinan Xu def run(self, args): 191c11a4d2cSYinan Xu if args.ci is not None: 192c11a4d2cSYinan Xu return self.run_ci(args.ci) 193c11a4d2cSYinan Xu actions = [ 194c11a4d2cSYinan Xu (args.generate, lambda _ : self.generate_verilog()), 195c11a4d2cSYinan Xu (args.build, lambda _ : self.build_emu()), 196c11a4d2cSYinan Xu (args.workload, lambda args: self.run_emu(args.workload)) 197c11a4d2cSYinan Xu ] 198c11a4d2cSYinan Xu valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions)) 199c11a4d2cSYinan Xu for i, action in enumerate(valid_actions): 200c11a4d2cSYinan Xu print(f"Action {i}:") 201c11a4d2cSYinan Xu ret = action(args) 202c11a4d2cSYinan Xu if ret: 203c11a4d2cSYinan Xu return ret 204c11a4d2cSYinan Xu return 0 205c11a4d2cSYinan Xu 206c11a4d2cSYinan Xu def __exec_cmd(self, cmd): 207c11a4d2cSYinan Xu env = dict(os.environ) 208c11a4d2cSYinan Xu env.update(self.args.get_env_variables()) 209c11a4d2cSYinan Xu print("subprocess call cmd:", cmd) 2105ef7374fSLi Qianruo start = time.time() 211c11a4d2cSYinan Xu return_code = subprocess.call(cmd, shell=True, env=env) 2125ef7374fSLi Qianruo end = time.time() 2135ef7374fSLi Qianruo print(f"Elapsed time: {end - start} seconds") 214c11a4d2cSYinan Xu return return_code 215c11a4d2cSYinan Xu 216c11a4d2cSYinan Xu def __get_ci_cputest(self, name=None): 217c11a4d2cSYinan Xu base_dir = os.path.join(self.args.am_home, "tests/cputest/build") 218c11a4d2cSYinan Xu cputest = os.listdir(base_dir) 219c11a4d2cSYinan Xu cputest = filter(lambda x: x.endswith(".bin"), cputest) 220c11a4d2cSYinan Xu cputest = map(lambda x: os.path.join(base_dir, x), cputest) 221c11a4d2cSYinan Xu return cputest 222c11a4d2cSYinan Xu 223c11a4d2cSYinan Xu def __get_ci_rvtest(self, name=None): 224c11a4d2cSYinan Xu base_dir = os.path.join(self.args.rvtest_home, "isa/build") 225c11a4d2cSYinan Xu riscv_tests = os.listdir(base_dir) 226c11a4d2cSYinan Xu riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests) 227c11a4d2cSYinan Xu all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud"] 228c11a4d2cSYinan Xu riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests) 229c11a4d2cSYinan Xu riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests) 230c11a4d2cSYinan Xu return riscv_tests 231c11a4d2cSYinan Xu 232675acc68SYinan Xu def __get_ci_misc(self, name=None): 233675acc68SYinan Xu base_dir = "/home/ci-runner/xsenv/workloads" 234675acc68SYinan Xu workloads = [ 235675acc68SYinan Xu "bitmanip/bitMisc.bin", 2363feeca58Szfw "crypto/crypto-riscv64-noop.bin", 237675acc68SYinan Xu "coremark_rv64gc_o2/coremark-riscv64-xs.bin", 238675acc68SYinan Xu "coremark_rv64gc_o3/coremark-riscv64-xs.bin", 23964a887e0SYinan Xu "coremark_rv64gcb_o3/coremark-riscv64-xs.bin", 24035620aa8Swakafa "ext_intr/amtest-riscv64-xs.bin", 2413f4ec46fSCODE-JTZ "cache-alias/aliastest-riscv64-xs.bin", 242af2f7849Shappy-lx "Svinval/rv64mi-p-svinval.bin", 243b6982e83SLemover "pmp/pmp.riscv.bin", 24445f497a4Shappy-lx "asid/asid.bin", 2453f4ec46fSCODE-JTZ "cache-management/softprefetch-riscv64-noop.bin" 246675acc68SYinan Xu ] 247675acc68SYinan Xu misc_tests = map(lambda x: os.path.join(base_dir, x), workloads) 248675acc68SYinan Xu return misc_tests 249675acc68SYinan Xu 250c11a4d2cSYinan Xu def __am_apps_path(self, bench): 251c11a4d2cSYinan Xu filename = f"{bench}-riscv64-noop.bin" 252c11a4d2cSYinan Xu return [os.path.join(self.args.am_home, "apps", bench, "build", filename)] 253c11a4d2cSYinan Xu 254c11a4d2cSYinan Xu def __get_ci_workloads(self, name): 255c11a4d2cSYinan Xu workloads = { 256c11a4d2cSYinan Xu "linux-hello": "bbl.bin", 2575092a298Szfw "povray": "_700480000000_.gz", 2585092a298Szfw "mcf": "_17520000000_.gz", 2595092a298Szfw "xalancbmk": "_266100000000_.gz", 2605092a298Szfw "gcc": "_39720000000_.gz", 2615092a298Szfw "namd": "_434640000000_.gz", 2625092a298Szfw "milc": "_103620000000_.gz", 2635092a298Szfw "lbm": "_140840000000_.gz", 2647b441e5eSYinan Xu "gromacs": "_275480000000_.gz", 2657b441e5eSYinan Xu "wrf": "_1916220000000_.gz", 2667b441e5eSYinan Xu "astar": "_122060000000_.gz" 267c11a4d2cSYinan Xu } 268c11a4d2cSYinan Xu return [os.path.join("/home/ci-runner/xsenv/workloads", name, workloads[name])] 269c11a4d2cSYinan Xu 270c11a4d2cSYinan Xu def run_ci(self, test): 271c11a4d2cSYinan Xu all_tests = { 272c11a4d2cSYinan Xu "cputest": self.__get_ci_cputest, 273c11a4d2cSYinan Xu "riscv-tests": self.__get_ci_rvtest, 274675acc68SYinan Xu "misc-tests": self.__get_ci_misc, 275c11a4d2cSYinan Xu "microbench": self.__am_apps_path, 276c11a4d2cSYinan Xu "coremark": self.__am_apps_path 277c11a4d2cSYinan Xu } 278c11a4d2cSYinan Xu for target in all_tests.get(test, self.__get_ci_workloads)(test): 279c11a4d2cSYinan Xu print(target) 280c11a4d2cSYinan Xu ret = self.run_emu(target) 281c11a4d2cSYinan Xu if ret: 28224e2eab6SJinYue if self.args.default_wave_home != self.args.wave_home: 28324e2eab6SJinYue print("copy wave file to " + self.args.wave_home) 28424e2eab6SJinYue self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME") 285bc063562SLemover self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME") 286bc063562SLemover self.__exec_cmd(f"cp $NOOP_HOME/build/SimTop.v $WAVE_HOME") 287c11a4d2cSYinan Xu return ret 288c11a4d2cSYinan Xu return 0 289c11a4d2cSYinan Xu 290c11a4d2cSYinan Xuif __name__ == "__main__": 291c11a4d2cSYinan Xu parser = argparse.ArgumentParser(description='Python wrapper for XiangShan') 292c11a4d2cSYinan Xu parser.add_argument('workload', nargs='?', type=str, default="", 293c11a4d2cSYinan Xu help='input workload file in binary format') 294c11a4d2cSYinan Xu # actions 295c11a4d2cSYinan Xu parser.add_argument('--build', action='store_true', help='build XS emu') 296c11a4d2cSYinan Xu parser.add_argument('--generate', action='store_true', help='generate XS verilog') 297c11a4d2cSYinan Xu parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests') 298c11a4d2cSYinan Xu # environment variables 299c11a4d2cSYinan Xu parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu') 300c11a4d2cSYinan Xu parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am') 301c11a4d2cSYinan Xu parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3') 302c11a4d2cSYinan Xu parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests') 30324e2eab6SJinYue parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave') 304c11a4d2cSYinan Xu # chisel arguments 305*1545277aSYinan Xu parser.add_argument('--enable-log', action='store_true', help='enable log') 3065ef7374fSLi Qianruo parser.add_argument('--num-cores', type=int, help='number of cores') 307c11a4d2cSYinan Xu # makefile arguments 308*1545277aSYinan Xu parser.add_argument('--release', action='store_true', help='enable release') 309c11a4d2cSYinan Xu parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3') 310c11a4d2cSYinan Xu parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads') 311c11a4d2cSYinan Xu parser.add_argument('--trace', action='store_true', help='enable waveform') 3126c0058d3SYinan Xu parser.add_argument('--config', nargs='?', type=str, help='config') 313c11a4d2cSYinan Xu # emu arguments 314c11a4d2cSYinan Xu parser.add_argument('--numa', action='store_true', help='use numactl') 315f9930da0SYinan Xu parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so') 316c11a4d2cSYinan Xu parser.add_argument('--max-instr', nargs='?', type=int, help='max instr') 317078fde2bSJinYue parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS') 31824e2eab6SJinYue # ci action head sha 319c11a4d2cSYinan Xu 320c11a4d2cSYinan Xu args = parser.parse_args() 321c11a4d2cSYinan Xu 322c11a4d2cSYinan Xu xs = XiangShan(args) 323c11a4d2cSYinan Xu ret = xs.run(args) 324c11a4d2cSYinan Xu 325c11a4d2cSYinan Xu sys.exit(ret) 326