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 } 546 if name in workloads: 547 return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])] 548 # select a random SPEC checkpoint 549 assert(name == "random") 550 all_cpt_dir = [ 551 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt", 552 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt", 553 "/nfs/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt", 554 "/nfs/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt", 555 "/nfs/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt", 556 "/nfs/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt", 557 "/nfs/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt", 558 "/nfs/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt", 559 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_O3_20m_gcc12.2.0-intFpcOff-jeMalloc/zstd-checkpoint-0-0-0", 560 "/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" 561 ] 562 all_gcpt = load_all_gcpt(all_cpt_dir) 563 return [random.choice(all_gcpt)] 564 565 def run_ci(self, test): 566 all_tests = { 567 "cputest": self.__get_ci_cputest, 568 "riscv-tests": self.__get_ci_rvtest, 569 "misc-tests": self.__get_ci_misc, 570 "mc-tests": self.__get_ci_mc, 571 "nodiff-tests": self.__get_ci_nodiff, 572 "rvh-tests": self.__get_ci_rvhtest, 573 "microbench": self.__am_apps_path, 574 "coremark": self.__am_apps_path, 575 "coremark-1-iteration": self.__am_apps_path, 576 "rvv-bench": self.__get_ci_rvvbench, 577 "rvv-test": self.__get_ci_rvvtest, 578 "f16_test": self.__get_ci_F16test, 579 "zcb-test": self.__get_ci_zcbtest 580 } 581 for target in all_tests.get(test, self.__get_ci_workloads)(test): 582 print(target) 583 ret = self.run_emu(target) 584 if ret: 585 if self.args.default_wave_home != self.args.wave_home: 586 print("copy wave file to " + self.args.wave_home) 587 self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME") 588 self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME") 589 self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME") 590 self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME") 591 return ret 592 return 0 593 594 def run_ci_vcs(self, test): 595 all_tests = { 596 "cputest": self.__get_ci_cputest, 597 "riscv-tests": self.__get_ci_rvtest, 598 "misc-tests": self.__get_ci_misc, 599 "mc-tests": self.__get_ci_mc, 600 "nodiff-tests": self.__get_ci_nodiff, 601 "rvh-tests": self.__get_ci_rvhtest, 602 "microbench": self.__am_apps_path, 603 "coremark": self.__am_apps_path, 604 "coremark-1-iteration": self.__am_apps_path, 605 "rvv-bench": self.__get_ci_rvvbench, 606 "rvv-test": self.__get_ci_rvvtest, 607 "f16_test": self.__get_ci_F16test, 608 "zcb-test": self.__get_ci_zcbtest 609 } 610 for target in all_tests.get(test, self.__get_ci_workloads)(test): 611 print(target) 612 ret = self.run_simv(target) 613 if ret: 614 if self.args.default_wave_home != self.args.wave_home: 615 print("copy wave file to " + self.args.wave_home) 616 self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME") 617 self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME") 618 self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME") 619 self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME") 620 return ret 621 return 0 622 623def get_free_cores(n): 624 numa_re = re.compile(r'.*numactl +.*-C +([0-9]+)-([0-9]+).*') 625 while True: 626 disable_cores = [] 627 for proc in psutil.process_iter(): 628 try: 629 joint = ' '.join(proc.cmdline()) 630 numa_match = numa_re.match(joint) 631 if numa_match and 'ssh' not in proc.name(): 632 disable_cores.extend(range(int(numa_match.group(1)), int(numa_match.group(2)) + 1)) 633 except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): 634 pass 635 num_logical_core = psutil.cpu_count(logical=False) 636 core_usage = psutil.cpu_percent(interval=1, percpu=True) 637 num_window = num_logical_core // n 638 for i in range(num_window): 639 if set(disable_cores) & set(range(i * n, i * n + n)): 640 continue 641 window_usage = core_usage[i * n : i * n + n] 642 if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage): 643 return (((i * n) % num_logical_core) // (num_logical_core // 2), i * n, i * n + n - 1) 644 print(f"No free {n} cores found. CPU usage: {core_usage}\n") 645 time.sleep(random.uniform(1, 60)) 646 647if __name__ == "__main__": 648 parser = argparse.ArgumentParser(description='Python wrapper for XiangShan') 649 parser.add_argument('workload', nargs='?', type=str, default="", 650 help='input workload file in binary format') 651 # actions 652 parser.add_argument('--build', action='store_true', help='build XS emu') 653 parser.add_argument('--generate', action='store_true', help='generate XS verilog') 654 parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs') 655 parser.add_argument('--vcs-build', action='store_true', help='build XS simv') 656 parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests') 657 parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv') 658 parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace') 659 parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)') 660 # environment variables 661 parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu') 662 parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am') 663 parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3') 664 parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests') 665 parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave') 666 # chisel arguments 667 parser.add_argument('--enable-log', action='store_true', help='enable log') 668 parser.add_argument('--num-cores', type=int, help='number of cores') 669 # makefile arguments 670 parser.add_argument('--release', action='store_true', help='enable release') 671 parser.add_argument('--spike', action='store_true', help='enable spike diff') 672 parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3') 673 parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads') 674 parser.add_argument('--trace', action='store_true', help='enable vcd waveform') 675 parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform') 676 parser.add_argument('--config', nargs='?', type=str, help='config') 677 parser.add_argument('--yaml-config', nargs='?', type=str, help='yaml config') 678 parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter') 679 parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs') 680 parser.add_argument('--issue', nargs='?', type=str, help='CHI issue') 681 # emu arguments 682 parser.add_argument('--numa', action='store_true', help='use numactl') 683 parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so') 684 parser.add_argument('--max-instr', nargs='?', type=int, help='max instr') 685 parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS') 686 parser.add_argument('--no-diff', action='store_true', help='disable difftest') 687 parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)') 688 parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt") 689 # both makefile and emu arguments 690 parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump') 691 parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)') 692 parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo') 693 parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo') 694 parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC') 695 696 args = parser.parse_args() 697 698 xs = XiangShan(args) 699 ret = xs.run(args) 700 701 sys.exit(ret) 702