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