1#*************************************************************************************** 2# Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC) 3# Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences 4# Copyright (c) 2020-2021 Peng Cheng Laboratory 5# 6# XiangShan is licensed under Mulan PSL v2. 7# You can use this software according to the terms and conditions of the Mulan PSL v2. 8# You may obtain a copy of Mulan PSL v2 at: 9# http://license.coscl.org.cn/MulanPSL2 10# 11# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 12# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 13# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 14# 15# See the Mulan PSL v2 for more details. 16#*************************************************************************************** 17 18# Simple version of xiangshan python wrapper 19 20import argparse 21import json 22import os 23import random 24import signal 25import subprocess 26import sys 27import time 28import shlex 29import psutil 30import re 31 32def find_files_with_suffix(root_dir, suffixes): 33 matching_files = [] 34 for dirpath, _, filenames in os.walk(root_dir): 35 for filename in filenames: 36 if any(filename.endswith(suffix) for suffix in suffixes): 37 absolute_path = os.path.join(dirpath, filename) 38 matching_files.append(absolute_path) 39 return matching_files 40 41def load_all_gcpt(gcpt_paths): 42 all_gcpt = [] 43 for gcpt_path in gcpt_paths: 44 all_gcpt.extend(find_files_with_suffix(gcpt_path, ['.zstd', '.gz'])) 45 return all_gcpt 46 47class XSArgs(object): 48 script_path = os.path.realpath(__file__) 49 # default path to the repositories 50 noop_home = os.path.join(os.path.dirname(script_path), "..") 51 nemu_home = os.path.join(noop_home, "../NEMU") 52 am_home = os.path.join(noop_home, "../nexus-am") 53 dramsim3_home = os.path.join(noop_home, "../DRAMsim3") 54 rvtest_home = os.path.join(noop_home, "../riscv-tests") 55 default_wave_home = os.path.join(noop_home, "build") 56 wave_home = default_wave_home 57 58 def __init__(self, args): 59 # all path environment variables that should be set 60 all_path = [ 61 # (python argument, environment variable, default, target function) 62 (None, "NOOP_HOME", self.noop_home, self.set_noop_home), 63 (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home), 64 (args.am, "AM_HOME", self.am_home, self.set_am_home), 65 (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home), 66 (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home), 67 ] 68 for (arg_in, env, default, set_func) in all_path: 69 set_func(self.__extract_path(arg_in, env, default)) 70 # Chisel arguments 71 self.enable_log = args.enable_log 72 self.num_cores = args.num_cores 73 # Makefile arguments 74 self.threads = args.threads 75 self.with_dramsim3 = 1 if args.with_dramsim3 else None 76 self.is_release = 1 if args.release else None 77 self.is_spike = "Spike" if args.spike else None 78 self.trace = 1 if args.trace or not args.disable_fork and not args.trace_fst else None 79 self.trace_fst = "fst" if args.trace_fst else None 80 self.config = args.config 81 self.emu_optimize = args.emu_optimize 82 self.xprop = 1 if args.xprop else None 83 self.with_chiseldb = 0 if args.no_db else 1 84 # emu arguments 85 self.max_instr = args.max_instr 86 self.ram_size = args.ram_size 87 self.seed = random.randint(0, 9999) 88 self.numa = args.numa 89 self.diff = args.diff 90 if args.spike and "nemu" in args.diff: 91 self.diff = self.diff.replace("nemu-interpreter", "spike") 92 self.fork = not args.disable_fork 93 self.disable_diff = args.no_diff 94 self.disable_db = args.no_db 95 self.gcpt_restore_bin = args.gcpt_restore_bin 96 self.pgo = args.pgo 97 self.pgo_max_cycle = args.pgo_max_cycle 98 self.pgo_emu_args = args.pgo_emu_args 99 self.llvm_profdata = args.llvm_profdata 100 # wave dump path 101 if args.wave_dump is not None: 102 self.set_wave_home(args.wave_dump) 103 else: 104 self.set_wave_home(self.default_wave_home) 105 106 def get_env_variables(self): 107 all_env = { 108 "NOOP_HOME" : self.noop_home, 109 "NEMU_HOME" : self.nemu_home, 110 "WAVE_HOME" : self.wave_home, 111 "AM_HOME" : self.am_home, 112 "DRAMSIM3_HOME": self.dramsim3_home, 113 "MODULEPATH": "/usr/share/Modules/modulefiles:/etc/modulefiles" 114 } 115 return all_env 116 117 def get_chisel_args(self, prefix=None): 118 chisel_args = [ 119 (self.enable_log, "enable-log") 120 ] 121 args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args)) 122 if prefix is not None: 123 args = map(lambda x: prefix + x, args) 124 return args 125 126 def get_makefile_args(self): 127 makefile_args = [ 128 (self.threads, "EMU_THREADS"), 129 (self.with_dramsim3, "WITH_DRAMSIM3"), 130 (self.is_release, "RELEASE"), 131 (self.is_spike, "REF"), 132 (self.trace, "EMU_TRACE"), 133 (self.trace_fst, "EMU_TRACE"), 134 (self.config, "CONFIG"), 135 (self.num_cores, "NUM_CORES"), 136 (self.emu_optimize, "EMU_OPTIMIZE"), 137 (self.xprop, "ENABLE_XPROP"), 138 (self.with_chiseldb, "WITH_CHISELDB"), 139 (self.pgo, "PGO_WORKLOAD"), 140 (self.pgo_max_cycle, "PGO_MAX_CYCLE"), 141 (self.pgo_emu_args, "PGO_EMU_ARGS"), 142 (self.llvm_profdata, "LLVM_PROFDATA"), 143 ] 144 args = filter(lambda arg: arg[0] is not None, makefile_args) 145 args = [(shlex.quote(str(arg[0])), arg[1]) for arg in args] # shell escape 146 return args 147 148 def get_emu_args(self): 149 emu_args = [ 150 (self.max_instr, "max-instr"), 151 (self.diff, "diff"), 152 (self.seed, "seed"), 153 (self.ram_size, "ram-size"), 154 ] 155 args = filter(lambda arg: arg[0] is not None, emu_args) 156 return args 157 158 def show(self): 159 print("Extra environment variables:") 160 env = self.get_env_variables() 161 for env_name in env: 162 print(f"{env_name}: {env[env_name]}") 163 print() 164 print("Chisel arguments:") 165 print(" ".join(self.get_chisel_args())) 166 print() 167 print("Makefile arguments:") 168 for val, name in self.get_makefile_args(): 169 print(f"{name}={val}") 170 print() 171 print("emu arguments:") 172 for val, name in self.get_emu_args(): 173 print(f"--{name} {val}") 174 print() 175 176 def __extract_path(self, path, env=None, default=None): 177 if path is None and env is not None: 178 path = os.getenv(env) 179 if path is None and default is not None: 180 path = default 181 path = os.path.realpath(path) 182 return path 183 184 def set_noop_home(self, path): 185 self.noop_home = path 186 187 def set_nemu_home(self, path): 188 self.nemu_home = path 189 190 def set_am_home(self, path): 191 self.am_home = path 192 193 def set_dramsim3_home(self, path): 194 self.dramsim3_home = path 195 196 def set_rvtest_home(self, path): 197 self.rvtest_home = path 198 199 def set_wave_home(self, path): 200 print(f"set wave home to {path}") 201 self.wave_home = path 202 203# XiangShan environment 204class XiangShan(object): 205 def __init__(self, args): 206 self.args = XSArgs(args) 207 self.timeout = args.timeout 208 209 def show(self): 210 self.args.show() 211 212 def make_clean(self): 213 print("Clean up CI workspace") 214 self.show() 215 return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean') 216 return return_code 217 218 def generate_verilog(self): 219 print("Generating XiangShan verilog with the following configurations:") 220 self.show() 221 sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 222 make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 223 return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}') 224 return return_code 225 226 def generate_sim_verilog(self): 227 print("Generating XiangShan sim-verilog with the following configurations:") 228 self.show() 229 sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 230 make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 231 return_code = self.__exec_cmd(f'make -C $NOOP_HOME sim-verilog SIM_ARGS="{sim_args}" {make_args}') 232 return return_code 233 234 def build_emu(self): 235 print("Building XiangShan emu with the following configurations:") 236 self.show() 237 sim_args = " ".join(self.args.get_chisel_args(prefix="--")) 238 make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 239 return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}') 240 return return_code 241 242 def build_simv(self): 243 print("Building XiangShan simv with the following configurations") 244 self.show() 245 make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args())) 246 # TODO: make the following commands grouped as unseen scripts 247 return_code = self.__exec_cmd(f'\ 248 eval `/usr/bin/modulecmd zsh load license`;\ 249 eval `/usr/bin/modulecmd zsh load synopsys/vcs/Q-2020.03-SP2`;\ 250 eval `/usr/bin/modulecmd zsh load synopsys/verdi/S-2021.09-SP1`;\ 251 VERDI_HOME=/nfs/tools/synopsys/verdi/S-2021.09-SP1 \ 252 make -C $NOOP_HOME simv {make_args} CONSIDER_FSDB=1') # set CONSIDER_FSDB for compatibility 253 return return_code 254 255 def run_emu(self, workload): 256 print("Running XiangShan emu with the following configurations:") 257 self.show() 258 emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args())) 259 print("workload:", workload) 260 numa_args = "" 261 if self.args.numa: 262 numa_info = get_free_cores(self.args.threads) 263 numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}" 264 fork_args = "--enable-fork" if self.args.fork else "" 265 diff_args = "--no-diff" if self.args.disable_diff else "" 266 chiseldb_args = "--dump-db" if not self.args.disable_db else "" 267 gcpt_restore_args = f"-r {self.args.gcpt_restore_bin}" if len(self.args.gcpt_restore_bin) != 0 else "" 268 return_code = self.__exec_cmd(f'ulimit -s {32 * 1024}; {numa_args} $NOOP_HOME/build/emu -i {workload} {emu_args} {fork_args} {diff_args} {chiseldb_args} {gcpt_restore_args}') 269 return return_code 270 271 def run_simv(self, workload): 272 print("Running XiangShan simv with the following configurations:") 273 self.show() 274 diff_args = "$NOOP_HOME/"+ args.diff 275 assert_args = "-assert finish_maxfail=30 -assert global_finish_maxfail=10000" 276 return_code = self.__exec_cmd(f'cd $NOOP_HOME/build && ./simv +workload={workload} +diff={diff_args} +dump-wave=fsdb {assert_args} | tee simv.log') 277 with open(f"{self.args.noop_home}/build/simv.log") as f: 278 content = f.read() 279 if "Offending" in content or "HIT GOOD TRAP" not in content: 280 return 1 281 return return_code 282 283 def run(self, args): 284 if args.ci is not None: 285 return self.run_ci(args.ci) 286 if args.ci_vcs is not None: 287 return self.run_ci_vcs(args.ci_vcs) 288 actions = [ 289 (args.generate, lambda _ : self.generate_verilog()), 290 (args.vcs_gen, lambda _ : self.generate_sim_verilog()), 291 (args.build, lambda _ : self.build_emu()), 292 (args.vcs_build, lambda _ : self.build_simv()), 293 (args.workload, lambda args: self.run_emu(args.workload)), 294 (args.clean, lambda _ : self.make_clean()) 295 ] 296 valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions)) 297 for i, action in enumerate(valid_actions): 298 print(f"Action {i}:") 299 ret = action(args) 300 if ret: 301 return ret 302 return 0 303 304 def __exec_cmd(self, cmd): 305 env = dict(os.environ) 306 env.update(self.args.get_env_variables()) 307 print("subprocess call cmd:", cmd) 308 start = time.time() 309 proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid) 310 try: 311 return_code = proc.wait(self.timeout) 312 end = time.time() 313 print(f"Elapsed time: {end - start} seconds") 314 return return_code 315 except (KeyboardInterrupt, subprocess.TimeoutExpired): 316 os.killpg(os.getpgid(proc.pid), signal.SIGINT) 317 print(f"KeyboardInterrupt or TimeoutExpired.") 318 return 0 319 320 def __get_ci_cputest(self, name=None): 321 # base_dir = os.path.join(self.args.am_home, "tests/cputest/build") 322 base_dir = "/nfs/home/share/ci-workloads/nexus-am-workloads/tests/cputest" 323 cputest = os.listdir(base_dir) 324 cputest = filter(lambda x: x.endswith(".bin"), cputest) 325 cputest = map(lambda x: os.path.join(base_dir, x), cputest) 326 return cputest 327 328 def __get_ci_rvtest(self, name=None): 329 base_dir = os.path.join(self.args.rvtest_home, "isa/build") 330 riscv_tests = os.listdir(base_dir) 331 riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests) 332 all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud", "rv64mi"] 333 riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests) 334 riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests) 335 return riscv_tests 336 337 def __get_ci_misc(self, name=None): 338 base_dir = "/nfs/home/share/ci-workloads" 339 workloads = [ 340 "bitmanip/bitMisc.bin", 341 "crypto/crypto-riscv64-noop.bin", 342 # "coremark_rv64gc_o2/coremark-riscv64-xs.bin", 343 # "coremark_rv64gc_o3/coremark-riscv64-xs.bin", 344 # "coremark_rv64gcb_o3/coremark-riscv64-xs.bin", 345 "nexus-am-workloads/amtest/external_intr-riscv64-xs.bin", 346 "nexus-am-workloads/tests/aliastest/aliastest-riscv64-xs.bin", 347 "Svinval/rv64mi-p-svinval.bin", 348 "pmp/pmp.riscv.bin", 349 "nexus-am-workloads/amtest/pmp_test-riscv64-xs.bin", 350 "nexus-am-workloads/amtest/sv39_hp_atom_test-riscv64-xs.bin", 351 "asid/asid.bin", 352 "isa_misc/xret_clear_mprv.bin", 353 "isa_misc/satp_ppn.bin", 354 "cache-management/softprefetchtest-riscv64-xs.bin", 355 "smstateen/rvh_test.bin", 356 "zacas/zacas-riscv64-xs.bin", 357 "Svpbmt/rvh_test.bin", 358 "Svnapot/svnapot-test.bin" 359 ] 360 misc_tests = map(lambda x: os.path.join(base_dir, x), workloads) 361 return misc_tests 362 363 def __get_ci_rvhtest(self, name=None): 364 base_dir = "/nfs/home/share/ci-workloads/H-extension-tests" 365 workloads = [ 366 "riscv-hyp-tests/rvh_test.bin", 367 "xvisor_wboxtest/checkpoint.gz", 368 "pointer-masking-test/M_HS_test/rvh_test.bin", 369 "pointer-masking-test/U_test/hint_UMode_hupmm2/rvh_test.bin", 370 "pointer-masking-test/U_test/vu_senvcfgpmm2/rvh_test.bin" 371 ] 372 rvh_tests = map(lambda x: os.path.join(base_dir, x), workloads) 373 return rvh_tests 374 375 def __get_ci_rvvbench(self, name=None): 376 base_dir = "/nfs/home/share/ci-workloads" 377 workloads = [ 378 "rvv-bench/poly1305.bin", 379 "rvv-bench/mergelines.bin" 380 ] 381 rvvbench = map(lambda x: os.path.join(base_dir, x), workloads) 382 return rvvbench 383 384 def __get_ci_rvvtest(self, name=None): 385 base_dir = "/nfs/home/share/ci-workloads/V-extension-tests" 386 workloads = [ 387 "rvv-test/vluxei32.v-0.bin", 388 "rvv-test/vlsseg4e32.v-0.bin", 389 "rvv-test/vlseg4e32.v-0.bin", 390 "rvv-test/vsetvl-0.bin", 391 "rvv-test/vsetivli-0.bin", 392 "rvv-test/vsuxei32.v-0.bin", 393 "rvv-test/vse16.v-0.bin", 394 "rvv-test/vsse16.v-1.bin", 395 "rvv-test/vlse32.v-0.bin", 396 "rvv-test/vsetvli-0.bin", 397 "rvv-test/vle16.v-0.bin", 398 "rvv-test/vle32.v-0.bin", 399 "rvv-test/vfsgnj.vv-0.bin", 400 "rvv-test/vfadd.vf-0.bin", 401 "rvv-test/vfsub.vf-0.bin", 402 "rvv-test/vslide1down.vx-0.bin" 403 ] 404 rvv_test = map(lambda x: os.path.join(base_dir, x), workloads) 405 return rvv_test 406 407 def __get_ci_F16test(self, name=None): 408 base_dir = "/nfs/home/share/ci-workloads/vector/F16-tests/build" 409 workloads = [ 410 "rv64uzfhmin-p-fzfhmincvt.bin", 411 "rv64uzfh-p-fadd.bin", 412 "rv64uzfh-p-fclass.bin", 413 "rv64uzfh-p-fcmp.bin", 414 "rv64uzfh-p-fcvt.bin", 415 "rv64uzfh-p-fcvt_w.bin", 416 "rv64uzfh-p-fdiv.bin", 417 "rv64uzfh-p-fmadd.bin", 418 "rv64uzfh-p-fmin.bin", 419 "rv64uzfh-p-ldst.bin", 420 "rv64uzfh-p-move.bin", 421 "rv64uzfh-p-recoding.bin", 422 "rv64uzvfh-p-vfadd.bin", 423 "rv64uzvfh-p-vfclass.bin", 424 "rv64uzvfh-p-vfcvtfx.bin", 425 "rv64uzvfh-p-vfcvtfxu.bin", 426 "rv64uzvfh-p-vfcvtrxf.bin", 427 "rv64uzvfh-p-vfcvtrxuf.bin", 428 "rv64uzvfh-p-vfcvtxf.bin", 429 "rv64uzvfh-p-vfcvtxuf.bin", 430 "rv64uzvfh-p-vfdiv.bin", 431 "rv64uzvfh-p-vfdown.bin", 432 "rv64uzvfh-p-vfmacc.bin", 433 "rv64uzvfh-p-vfmadd.bin", 434 "rv64uzvfh-p-vfmax.bin", 435 "rv64uzvfh-p-vfmerge.bin", 436 "rv64uzvfh-p-vfmin.bin", 437 "rv64uzvfh-p-vfmsac.bin", 438 "rv64uzvfh-p-vfmsub.bin", 439 "rv64uzvfh-p-vfmul.bin", 440 "rv64uzvfh-p-vfmv.bin", 441 "rv64uzvfh-p-vfncvtff.bin", 442 "rv64uzvfh-p-vfncvtfx.bin", 443 "rv64uzvfh-p-vfncvtfxu.bin", 444 "rv64uzvfh-p-vfncvtrff.bin", 445 "rv64uzvfh-p-vfncvtrxf.bin", 446 "rv64uzvfh-p-vfncvtrxuf.bin", 447 "rv64uzvfh-p-vfncvtxf.bin", 448 "rv64uzvfh-p-vfncvtxuf.bin", 449 "rv64uzvfh-p-vfnmacc.bin", 450 "rv64uzvfh-p-vfnmadd.bin", 451 "rv64uzvfh-p-vfnmsac.bin", 452 "rv64uzvfh-p-vfnmsub.bin", 453 "rv64uzvfh-p-vfrdiv.bin", 454 "rv64uzvfh-p-vfrec7.bin", 455 "rv64uzvfh-p-vfredmax.bin", 456 "rv64uzvfh-p-vfredmin.bin", 457 "rv64uzvfh-p-vfredosum.bin", 458 "rv64uzvfh-p-vfredusum.bin", 459 "rv64uzvfh-p-vfrsqrt7.bin", 460 "rv64uzvfh-p-vfrsub.bin", 461 "rv64uzvfh-p-vfsgnj.bin", 462 "rv64uzvfh-p-vfsgnjn.bin", 463 "rv64uzvfh-p-vfsgnjx.bin", 464 "rv64uzvfh-p-vfsqrt.bin", 465 "rv64uzvfh-p-vfsub.bin", 466 "rv64uzvfh-p-vfup.bin", 467 "rv64uzvfh-p-vfwadd.bin", 468 "rv64uzvfh-p-vfwadd-w.bin", 469 "rv64uzvfh-p-vfwcvtff.bin", 470 "rv64uzvfh-p-vfwcvtfx.bin", 471 "rv64uzvfh-p-vfwcvtfxu.bin", 472 "rv64uzvfh-p-vfwcvtrxf.bin", 473 "rv64uzvfh-p-vfwcvtrxuf.bin", 474 "rv64uzvfh-p-vfwcvtxf.bin", 475 "rv64uzvfh-p-vfwcvtxuf.bin", 476 "rv64uzvfh-p-vfwmacc.bin", 477 "rv64uzvfh-p-vfwmsac.bin", 478 "rv64uzvfh-p-vfwmul.bin", 479 "rv64uzvfh-p-vfwnmacc.bin", 480 "rv64uzvfh-p-vfwnmsac.bin", 481 "rv64uzvfh-p-vfwredosum.bin", 482 "rv64uzvfh-p-vfwredusum.bin", 483 "rv64uzvfh-p-vfwsub.bin", 484 "rv64uzvfh-p-vfwsub-w.bin", 485 "rv64uzvfh-p-vmfeq.bin", 486 "rv64uzvfh-p-vmfge.bin", 487 "rv64uzvfh-p-vmfgt.bin", 488 "rv64uzvfh-p-vmfle.bin", 489 "rv64uzvfh-p-vmflt.bin", 490 "rv64uzvfh-p-vmfne.bin" 491 ] 492 f16_test = map(lambda x: os.path.join(base_dir, x), workloads) 493 return f16_test 494 def __get_ci_zcbtest(self, name=None): 495 base_dir = "/nfs/home/share/ci-workloads/zcb-test" 496 workloads = [ 497 "zcb-test-riscv64-xs.bin" 498 ] 499 zcb_test = map(lambda x: os.path.join(base_dir, x), workloads) 500 return zcb_test 501 502 def __get_ci_mc(self, name=None): 503 base_dir = "/nfs/home/share/ci-workloads" 504 workloads = [ 505 "nexus-am-workloads/tests/dualcoretest/ldvio-riscv64-xs.bin" 506 ] 507 mc_tests = map(lambda x: os.path.join(base_dir, x), workloads) 508 return mc_tests 509 510 def __get_ci_nodiff(self, name=None): 511 base_dir = "/nfs/home/share/ci-workloads" 512 workloads = [ 513 "cache-management/cacheoptest-riscv64-xs.bin" 514 ] 515 tests = map(lambda x: os.path.join(base_dir, x), workloads) 516 return tests 517 518 def __am_apps_path(self, bench): 519 base_dir = '/nfs/home/share/ci-workloads/nexus-am-workloads/apps' 520 filename = f"{bench}-riscv64-xs.bin" 521 return [os.path.join(base_dir, bench, filename)] 522 523 def __get_ci_workloads(self, name): 524 workloads = { 525 "linux-hello": "bbl.bin", 526 "linux-hello-smp": "bbl.bin", 527 "linux-hello-opensbi": "fw_payload.bin", 528 "linux-hello-smp-opensbi": "fw_payload.bin", 529 "linux-hello-new": "bbl.bin", 530 "linux-hello-smp-new": "bbl.bin", 531 "povray": "_700480000000_.gz", 532 "mcf": "_17520000000_.gz", 533 "xalancbmk": "_266100000000_.gz", 534 "gcc": "_39720000000_.gz", 535 "namd": "_434640000000_.gz", 536 "milc": "_103620000000_.gz", 537 "lbm": "_140840000000_.gz", 538 "gromacs": "_275480000000_.gz", 539 "wrf": "_1916220000000_.gz", 540 "astar": "_122060000000_.gz" 541 } 542 if name in workloads: 543 return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])] 544 # select a random SPEC checkpoint 545 assert(name == "random") 546 all_cpt_dir = [ 547 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt", 548 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt", 549 "/nfs/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt", 550 "/nfs/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt", 551 "/nfs/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt", 552 "/nfs/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt", 553 "/nfs/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt", 554 "/nfs/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt", 555 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_O3_20m_gcc12.2.0-intFpcOff-jeMalloc/zstd-checkpoint-0-0-0", 556 "/nfs/home/share/checkpoints_profiles/spec06_gcc15_rv64gcbv_O3_lto_base_nemu_single_core_NEMU_archgroup_2024-10-12-16-05/checkpoint-0-0-0" 557 ] 558 all_gcpt = load_all_gcpt(all_cpt_dir) 559 return [random.choice(all_gcpt)] 560 561 def run_ci(self, test): 562 all_tests = { 563 "cputest": self.__get_ci_cputest, 564 "riscv-tests": self.__get_ci_rvtest, 565 "misc-tests": self.__get_ci_misc, 566 "mc-tests": self.__get_ci_mc, 567 "nodiff-tests": self.__get_ci_nodiff, 568 "rvh-tests": self.__get_ci_rvhtest, 569 "microbench": self.__am_apps_path, 570 "coremark": self.__am_apps_path, 571 "coremark-1-iteration": self.__am_apps_path, 572 "rvv-bench": self.__get_ci_rvvbench, 573 "rvv-test": self.__get_ci_rvvtest, 574 "f16_test": self.__get_ci_F16test, 575 "zcb-test": self.__get_ci_zcbtest 576 } 577 for target in all_tests.get(test, self.__get_ci_workloads)(test): 578 print(target) 579 ret = self.run_emu(target) 580 if ret: 581 if self.args.default_wave_home != self.args.wave_home: 582 print("copy wave file to " + self.args.wave_home) 583 self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME") 584 self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME") 585 self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME") 586 self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME") 587 return ret 588 return 0 589 590 def run_ci_vcs(self, test): 591 all_tests = { 592 "cputest": self.__get_ci_cputest, 593 "riscv-tests": self.__get_ci_rvtest, 594 "misc-tests": self.__get_ci_misc, 595 "mc-tests": self.__get_ci_mc, 596 "nodiff-tests": self.__get_ci_nodiff, 597 "rvh-tests": self.__get_ci_rvhtest, 598 "microbench": self.__am_apps_path, 599 "coremark": self.__am_apps_path, 600 "coremark-1-iteration": self.__am_apps_path, 601 "rvv-bench": self.__get_ci_rvvbench, 602 "rvv-test": self.__get_ci_rvvtest, 603 "f16_test": self.__get_ci_F16test, 604 "zcb-test": self.__get_ci_zcbtest 605 } 606 for target in all_tests.get(test, self.__get_ci_workloads)(test): 607 print(target) 608 ret = self.run_simv(target) 609 if ret: 610 if self.args.default_wave_home != self.args.wave_home: 611 print("copy wave file to " + self.args.wave_home) 612 self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME") 613 self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME") 614 self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME") 615 self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME") 616 return ret 617 return 0 618 619def get_free_cores(n): 620 numa_re = re.compile(r'.*numactl +.*-C +([0-9]+)-([0-9]+).*') 621 while True: 622 disable_cores = [] 623 for proc in psutil.process_iter(): 624 try: 625 joint = ' '.join(proc.cmdline()) 626 numa_match = numa_re.match(joint) 627 if numa_match and 'ssh' not in proc.name(): 628 disable_cores.extend(range(int(numa_match.group(1)), int(numa_match.group(2)) + 1)) 629 except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): 630 pass 631 num_logical_core = psutil.cpu_count(logical=False) 632 core_usage = psutil.cpu_percent(interval=1, percpu=True) 633 num_window = num_logical_core // n 634 for i in range(num_window): 635 if set(disable_cores) & set(range(i * n, i * n + n)): 636 continue 637 window_usage = core_usage[i * n : i * n + n] 638 if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage): 639 return (((i * n) % num_logical_core) // (num_logical_core // 2), i * n, i * n + n - 1) 640 print(f"No free {n} cores found. CPU usage: {core_usage}\n") 641 time.sleep(random.uniform(1, 60)) 642 643if __name__ == "__main__": 644 parser = argparse.ArgumentParser(description='Python wrapper for XiangShan') 645 parser.add_argument('workload', nargs='?', type=str, default="", 646 help='input workload file in binary format') 647 # actions 648 parser.add_argument('--build', action='store_true', help='build XS emu') 649 parser.add_argument('--generate', action='store_true', help='generate XS verilog') 650 parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs') 651 parser.add_argument('--vcs-build', action='store_true', help='build XS simv') 652 parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests') 653 parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv') 654 parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace') 655 parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)') 656 # environment variables 657 parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu') 658 parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am') 659 parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3') 660 parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests') 661 parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave') 662 # chisel arguments 663 parser.add_argument('--enable-log', action='store_true', help='enable log') 664 parser.add_argument('--num-cores', type=int, help='number of cores') 665 # makefile arguments 666 parser.add_argument('--release', action='store_true', help='enable release') 667 parser.add_argument('--spike', action='store_true', help='enable spike diff') 668 parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3') 669 parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads') 670 parser.add_argument('--trace', action='store_true', help='enable vcd waveform') 671 parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform') 672 parser.add_argument('--config', nargs='?', type=str, help='config') 673 parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter') 674 parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs') 675 # emu arguments 676 parser.add_argument('--numa', action='store_true', help='use numactl') 677 parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so') 678 parser.add_argument('--max-instr', nargs='?', type=int, help='max instr') 679 parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS') 680 parser.add_argument('--no-diff', action='store_true', help='disable difftest') 681 parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)') 682 parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt") 683 # both makefile and emu arguments 684 parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump') 685 parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)') 686 parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo') 687 parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo') 688 parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC') 689 690 args = parser.parse_args() 691 692 xs = XiangShan(args) 693 ret = xs.run(args) 694 695 sys.exit(ret) 696