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