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