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