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