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