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