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 "Zawrs/Zawrs-zawrs.bin" 364 ] 365 misc_tests = map(lambda x: os.path.join(base_dir, x), workloads) 366 return misc_tests 367 368 def __get_ci_rvhtest(self, name=None): 369 base_dir = "/nfs/home/share/ci-workloads/H-extension-tests" 370 workloads = [ 371 "riscv-hyp-tests/rvh_test.bin", 372 "xvisor_wboxtest/checkpoint.gz", 373 "pointer-masking-test/M_HS_test/rvh_test.bin", 374 "pointer-masking-test/U_test/hint_UMode_hupmm2/rvh_test.bin", 375 "pointer-masking-test/U_test/vu_senvcfgpmm2/rvh_test.bin" 376 ] 377 rvh_tests = map(lambda x: os.path.join(base_dir, x), workloads) 378 return rvh_tests 379 380 def __get_ci_rvvbench(self, name=None): 381 base_dir = "/nfs/home/share/ci-workloads" 382 workloads = [ 383 "rvv-bench/poly1305.bin", 384 "rvv-bench/mergelines.bin" 385 ] 386 rvvbench = map(lambda x: os.path.join(base_dir, x), workloads) 387 return rvvbench 388 389 def __get_ci_rvvtest(self, name=None): 390 base_dir = "/nfs/home/share/ci-workloads/V-extension-tests" 391 workloads = [ 392 "rvv-test/vluxei32.v-0.bin", 393 "rvv-test/vlsseg4e32.v-0.bin", 394 "rvv-test/vlseg4e32.v-0.bin", 395 "rvv-test/vsetvl-0.bin", 396 "rvv-test/vsetivli-0.bin", 397 "rvv-test/vsuxei32.v-0.bin", 398 "rvv-test/vse16.v-0.bin", 399 "rvv-test/vsse16.v-1.bin", 400 "rvv-test/vlse32.v-0.bin", 401 "rvv-test/vsetvli-0.bin", 402 "rvv-test/vle16.v-0.bin", 403 "rvv-test/vle32.v-0.bin", 404 "rvv-test/vfsgnj.vv-0.bin", 405 "rvv-test/vfadd.vf-0.bin", 406 "rvv-test/vfsub.vf-0.bin", 407 "rvv-test/vslide1down.vx-0.bin" 408 ] 409 rvv_test = map(lambda x: os.path.join(base_dir, x), workloads) 410 return rvv_test 411 412 def __get_ci_F16test(self, name=None): 413 base_dir = "/nfs/home/share/ci-workloads/vector/F16-tests/build" 414 workloads = [ 415 "rv64uzfhmin-p-fzfhmincvt.bin", 416 "rv64uzfh-p-fadd.bin", 417 "rv64uzfh-p-fclass.bin", 418 "rv64uzfh-p-fcmp.bin", 419 "rv64uzfh-p-fcvt.bin", 420 "rv64uzfh-p-fcvt_w.bin", 421 "rv64uzfh-p-fdiv.bin", 422 "rv64uzfh-p-fmadd.bin", 423 "rv64uzfh-p-fmin.bin", 424 "rv64uzfh-p-ldst.bin", 425 "rv64uzfh-p-move.bin", 426 "rv64uzfh-p-recoding.bin", 427 "rv64uzvfh-p-vfadd.bin", 428 "rv64uzvfh-p-vfclass.bin", 429 "rv64uzvfh-p-vfcvtfx.bin", 430 "rv64uzvfh-p-vfcvtfxu.bin", 431 "rv64uzvfh-p-vfcvtrxf.bin", 432 "rv64uzvfh-p-vfcvtrxuf.bin", 433 "rv64uzvfh-p-vfcvtxf.bin", 434 "rv64uzvfh-p-vfcvtxuf.bin", 435 "rv64uzvfh-p-vfdiv.bin", 436 "rv64uzvfh-p-vfdown.bin", 437 "rv64uzvfh-p-vfmacc.bin", 438 "rv64uzvfh-p-vfmadd.bin", 439 "rv64uzvfh-p-vfmax.bin", 440 "rv64uzvfh-p-vfmerge.bin", 441 "rv64uzvfh-p-vfmin.bin", 442 "rv64uzvfh-p-vfmsac.bin", 443 "rv64uzvfh-p-vfmsub.bin", 444 "rv64uzvfh-p-vfmul.bin", 445 "rv64uzvfh-p-vfmv.bin", 446 "rv64uzvfh-p-vfncvtff.bin", 447 "rv64uzvfh-p-vfncvtfx.bin", 448 "rv64uzvfh-p-vfncvtfxu.bin", 449 "rv64uzvfh-p-vfncvtrff.bin", 450 "rv64uzvfh-p-vfncvtrxf.bin", 451 "rv64uzvfh-p-vfncvtrxuf.bin", 452 "rv64uzvfh-p-vfncvtxf.bin", 453 "rv64uzvfh-p-vfncvtxuf.bin", 454 "rv64uzvfh-p-vfnmacc.bin", 455 "rv64uzvfh-p-vfnmadd.bin", 456 "rv64uzvfh-p-vfnmsac.bin", 457 "rv64uzvfh-p-vfnmsub.bin", 458 "rv64uzvfh-p-vfrdiv.bin", 459 "rv64uzvfh-p-vfrec7.bin", 460 "rv64uzvfh-p-vfredmax.bin", 461 "rv64uzvfh-p-vfredmin.bin", 462 "rv64uzvfh-p-vfredosum.bin", 463 "rv64uzvfh-p-vfredusum.bin", 464 "rv64uzvfh-p-vfrsqrt7.bin", 465 "rv64uzvfh-p-vfrsub.bin", 466 "rv64uzvfh-p-vfsgnj.bin", 467 "rv64uzvfh-p-vfsgnjn.bin", 468 "rv64uzvfh-p-vfsgnjx.bin", 469 "rv64uzvfh-p-vfsqrt.bin", 470 "rv64uzvfh-p-vfsub.bin", 471 "rv64uzvfh-p-vfup.bin", 472 "rv64uzvfh-p-vfwadd.bin", 473 "rv64uzvfh-p-vfwadd-w.bin", 474 "rv64uzvfh-p-vfwcvtff.bin", 475 "rv64uzvfh-p-vfwcvtfx.bin", 476 "rv64uzvfh-p-vfwcvtfxu.bin", 477 "rv64uzvfh-p-vfwcvtrxf.bin", 478 "rv64uzvfh-p-vfwcvtrxuf.bin", 479 "rv64uzvfh-p-vfwcvtxf.bin", 480 "rv64uzvfh-p-vfwcvtxuf.bin", 481 "rv64uzvfh-p-vfwmacc.bin", 482 "rv64uzvfh-p-vfwmsac.bin", 483 "rv64uzvfh-p-vfwmul.bin", 484 "rv64uzvfh-p-vfwnmacc.bin", 485 "rv64uzvfh-p-vfwnmsac.bin", 486 "rv64uzvfh-p-vfwredosum.bin", 487 "rv64uzvfh-p-vfwredusum.bin", 488 "rv64uzvfh-p-vfwsub.bin", 489 "rv64uzvfh-p-vfwsub-w.bin", 490 "rv64uzvfh-p-vmfeq.bin", 491 "rv64uzvfh-p-vmfge.bin", 492 "rv64uzvfh-p-vmfgt.bin", 493 "rv64uzvfh-p-vmfle.bin", 494 "rv64uzvfh-p-vmflt.bin", 495 "rv64uzvfh-p-vmfne.bin" 496 ] 497 f16_test = map(lambda x: os.path.join(base_dir, x), workloads) 498 return f16_test 499 def __get_ci_zcbtest(self, name=None): 500 base_dir = "/nfs/home/share/ci-workloads/zcb-test" 501 workloads = [ 502 "zcb-test-riscv64-xs.bin" 503 ] 504 zcb_test = map(lambda x: os.path.join(base_dir, x), workloads) 505 return zcb_test 506 507 def __get_ci_mc(self, name=None): 508 base_dir = "/nfs/home/share/ci-workloads" 509 workloads = [ 510 "nexus-am-workloads/tests/dualcoretest/ldvio-riscv64-xs.bin" 511 ] 512 mc_tests = map(lambda x: os.path.join(base_dir, x), workloads) 513 return mc_tests 514 515 def __get_ci_nodiff(self, name=None): 516 base_dir = "/nfs/home/share/ci-workloads" 517 workloads = [ 518 "cache-management/cacheoptest-riscv64-xs.bin" 519 ] 520 tests = map(lambda x: os.path.join(base_dir, x), workloads) 521 return tests 522 523 def __am_apps_path(self, bench): 524 base_dir = '/nfs/home/share/ci-workloads/nexus-am-workloads/apps' 525 filename = f"{bench}-riscv64-xs.bin" 526 return [os.path.join(base_dir, bench, filename)] 527 528 def __get_ci_workloads(self, name): 529 workloads = { 530 "linux-hello": "bbl.bin", 531 "linux-hello-smp": "bbl.bin", 532 "linux-hello-opensbi": "fw_payload.bin", 533 "linux-hello-smp-opensbi": "fw_payload.bin", 534 "linux-hello-new": "bbl.bin", 535 "linux-hello-smp-new": "bbl.bin", 536 "povray": "_700480000000_.gz", 537 "mcf": "_17520000000_.gz", 538 "xalancbmk": "_266100000000_.gz", 539 "gcc": "_39720000000_.gz", 540 "namd": "_434640000000_.gz", 541 "milc": "_103620000000_.gz", 542 "lbm": "_140840000000_.gz", 543 "gromacs": "_275480000000_.gz", 544 "wrf": "_1916220000000_.gz", 545 "astar": "_122060000000_.gz", 546 "hmmer-Vector": "_6598_0.250135_.zstd" 547 } 548 if name in workloads: 549 return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])] 550 # select a random SPEC checkpoint 551 assert(name == "random") 552 all_cpt_dir = [ 553 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt", 554 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt", 555 "/nfs/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt", 556 "/nfs/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt", 557 "/nfs/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt", 558 "/nfs/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt", 559 "/nfs/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt", 560 "/nfs/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt", 561 "/nfs/home/share/checkpoints_profiles/spec06_rv64gcb_O3_20m_gcc12.2.0-intFpcOff-jeMalloc/zstd-checkpoint-0-0-0", 562 "/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" 563 ] 564 all_gcpt = load_all_gcpt(all_cpt_dir) 565 return [random.choice(all_gcpt)] 566 567 def run_ci(self, test): 568 all_tests = { 569 "cputest": self.__get_ci_cputest, 570 "riscv-tests": self.__get_ci_rvtest, 571 "misc-tests": self.__get_ci_misc, 572 "mc-tests": self.__get_ci_mc, 573 "nodiff-tests": self.__get_ci_nodiff, 574 "rvh-tests": self.__get_ci_rvhtest, 575 "microbench": self.__am_apps_path, 576 "coremark": self.__am_apps_path, 577 "coremark-1-iteration": self.__am_apps_path, 578 "rvv-bench": self.__get_ci_rvvbench, 579 "rvv-test": self.__get_ci_rvvtest, 580 "f16_test": self.__get_ci_F16test, 581 "zcb-test": self.__get_ci_zcbtest 582 } 583 for target in all_tests.get(test, self.__get_ci_workloads)(test): 584 print(target) 585 ret = self.run_emu(target) 586 if ret: 587 if self.args.default_wave_home != self.args.wave_home: 588 print("copy wave file to " + self.args.wave_home) 589 self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME") 590 self.__exec_cmd(f"cp $NOOP_HOME/build/*.fst $WAVE_HOME") 591 self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME") 592 self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME") 593 self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME") 594 return ret 595 return 0 596 597 def run_ci_vcs(self, test): 598 all_tests = { 599 "cputest": self.__get_ci_cputest, 600 "riscv-tests": self.__get_ci_rvtest, 601 "misc-tests": self.__get_ci_misc, 602 "mc-tests": self.__get_ci_mc, 603 "nodiff-tests": self.__get_ci_nodiff, 604 "rvh-tests": self.__get_ci_rvhtest, 605 "microbench": self.__am_apps_path, 606 "coremark": self.__am_apps_path, 607 "coremark-1-iteration": self.__am_apps_path, 608 "rvv-bench": self.__get_ci_rvvbench, 609 "rvv-test": self.__get_ci_rvvtest, 610 "f16_test": self.__get_ci_F16test, 611 "zcb-test": self.__get_ci_zcbtest 612 } 613 for target in all_tests.get(test, self.__get_ci_workloads)(test): 614 print(target) 615 ret = self.run_simv(target) 616 if ret: 617 if self.args.default_wave_home != self.args.wave_home: 618 print("copy wave file to " + self.args.wave_home) 619 self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME") 620 self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME") 621 self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME") 622 self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME") 623 return ret 624 return 0 625 626def get_free_cores(n): 627 numa_re = re.compile(r'.*numactl +.*-C +([0-9]+)-([0-9]+).*') 628 while True: 629 disable_cores = [] 630 for proc in psutil.process_iter(): 631 try: 632 joint = ' '.join(proc.cmdline()) 633 numa_match = numa_re.match(joint) 634 if numa_match and 'ssh' not in proc.name(): 635 disable_cores.extend(range(int(numa_match.group(1)), int(numa_match.group(2)) + 1)) 636 except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): 637 pass 638 num_logical_core = psutil.cpu_count(logical=False) 639 core_usage = psutil.cpu_percent(interval=1, percpu=True) 640 num_window = num_logical_core // n 641 for i in range(num_window): 642 if set(disable_cores) & set(range(i * n, i * n + n)): 643 continue 644 window_usage = core_usage[i * n : i * n + n] 645 if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage): 646 return (((i * n) % num_logical_core) // (num_logical_core // 2), i * n, i * n + n - 1) 647 print(f"No free {n} cores found. CPU usage: {core_usage}\n") 648 time.sleep(random.uniform(1, 60)) 649 650if __name__ == "__main__": 651 parser = argparse.ArgumentParser(description='Python wrapper for XiangShan') 652 parser.add_argument('workload', nargs='?', type=str, default="", 653 help='input workload file in binary format') 654 # actions 655 parser.add_argument('--build', action='store_true', help='build XS emu') 656 parser.add_argument('--generate', action='store_true', help='generate XS verilog') 657 parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs') 658 parser.add_argument('--vcs-build', action='store_true', help='build XS simv') 659 parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests') 660 parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv') 661 parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace') 662 parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)') 663 # environment variables 664 parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu') 665 parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am') 666 parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3') 667 parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests') 668 parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave') 669 # chisel arguments 670 parser.add_argument('--enable-log', action='store_true', help='enable log') 671 parser.add_argument('--num-cores', type=int, help='number of cores') 672 # makefile arguments 673 parser.add_argument('--release', action='store_true', help='enable release') 674 parser.add_argument('--spike', action='store_true', help='enable spike diff') 675 parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3') 676 parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads') 677 parser.add_argument('--trace', action='store_true', help='enable vcd waveform') 678 parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform') 679 parser.add_argument('--config', nargs='?', type=str, help='config') 680 parser.add_argument('--yaml-config', nargs='?', type=str, help='yaml config') 681 parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter') 682 parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs') 683 parser.add_argument('--issue', nargs='?', type=str, help='CHI issue') 684 # emu arguments 685 parser.add_argument('--numa', action='store_true', help='use numactl') 686 parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so') 687 parser.add_argument('--max-instr', nargs='?', type=int, help='max instr') 688 parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS') 689 parser.add_argument('--no-diff', action='store_true', help='disable difftest') 690 parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)') 691 parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt") 692 # both makefile and emu arguments 693 parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump') 694 parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)') 695 parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo') 696 parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo') 697 parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC') 698 699 args = parser.parse_args() 700 701 xs = XiangShan(args) 702 ret = xs.run(args) 703 704 sys.exit(ret) 705