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