xiangshan.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. # Simple version of xiangshan python wrapper
  18. import argparse
  19. import json
  20. import os
  21. import random
  22. import signal
  23. import subprocess
  24. import sys
  25. import time
  26. import shlex
  27. import psutil
  28. import re
  29. def load_all_gcpt(gcpt_path, json_path):
  30. all_gcpt = []
  31. with open(json_path) as f:
  32. data = json.load(f)
  33. for benchspec in data:
  34. for point in data[benchspec]:
  35. weight = data[benchspec][point]
  36. gcpt = os.path.join(gcpt_path, "_".join([benchspec, point, weight]))
  37. bin_dir = os.path.join(gcpt, "0")
  38. bin_file = list(os.listdir(bin_dir))
  39. assert(len(bin_file) == 1)
  40. bin_path = os.path.join(bin_dir, bin_file[0])
  41. assert(os.path.isfile(bin_path))
  42. all_gcpt.append(bin_path)
  43. return all_gcpt
  44. class XSArgs(object):
  45. script_path = os.path.realpath(__file__)
  46. # default path to the repositories
  47. noop_home = os.path.join(os.path.dirname(script_path), "..")
  48. nemu_home = os.path.join(noop_home, "../NEMU")
  49. am_home = os.path.join(noop_home, "../nexus-am")
  50. dramsim3_home = os.path.join(noop_home, "../DRAMsim3")
  51. rvtest_home = os.path.join(noop_home, "../riscv-tests")
  52. default_wave_home = os.path.join(noop_home, "build")
  53. wave_home = default_wave_home
  54. def __init__(self, args):
  55. # all path environment variables that should be set
  56. all_path = [
  57. # (python argument, environment variable, default, target function)
  58. (None, "NOOP_HOME", self.noop_home, self.set_noop_home),
  59. (args.nemu, "NEMU_HOME", self.nemu_home, self.set_nemu_home),
  60. (args.am, "AM_HOME", self.am_home, self.set_am_home),
  61. (args.dramsim3, "DRAMSIM3_HOME", self.dramsim3_home, self.set_dramsim3_home),
  62. (args.rvtest, "RVTEST_HOME", self.rvtest_home, self.set_rvtest_home),
  63. ]
  64. for (arg_in, env, default, set_func) in all_path:
  65. set_func(self.__extract_path(arg_in, env, default))
  66. # Chisel arguments
  67. self.enable_log = args.enable_log
  68. self.num_cores = args.num_cores
  69. # Makefile arguments
  70. self.threads = args.threads
  71. self.with_dramsim3 = 1 if args.with_dramsim3 else None
  72. self.is_release = 1 if args.release else None
  73. self.is_spike = "Spike" if args.spike else None
  74. self.trace = 1 if args.trace or not args.disable_fork and not args.trace_fst else None
  75. self.trace_fst = "fst" if args.trace_fst else None
  76. self.config = args.config
  77. self.emu_optimize = args.emu_optimize
  78. self.xprop = 1 if args.xprop else None
  79. self.with_chiseldb = 0 if args.no_db else 1
  80. # emu arguments
  81. self.max_instr = args.max_instr
  82. self.ram_size = args.ram_size
  83. self.seed = random.randint(0, 9999)
  84. self.numa = args.numa
  85. self.diff = args.diff
  86. if args.spike and "nemu" in args.diff:
  87. self.diff = self.diff.replace("nemu-interpreter", "spike")
  88. self.fork = not args.disable_fork
  89. self.disable_diff = args.no_diff
  90. self.disable_db = args.no_db
  91. self.gcpt_restore_bin = args.gcpt_restore_bin
  92. self.pgo = args.pgo
  93. self.pgo_max_cycle = args.pgo_max_cycle
  94. self.pgo_emu_args = args.pgo_emu_args
  95. self.llvm_profdata = args.llvm_profdata
  96. # wave dump path
  97. if args.wave_dump is not None:
  98. self.set_wave_home(args.wave_dump)
  99. else:
  100. self.set_wave_home(self.default_wave_home)
  101. def get_env_variables(self):
  102. all_env = {
  103. "NOOP_HOME" : self.noop_home,
  104. "NEMU_HOME" : self.nemu_home,
  105. "WAVE_HOME" : self.wave_home,
  106. "AM_HOME" : self.am_home,
  107. "DRAMSIM3_HOME": self.dramsim3_home,
  108. "MODULEPATH": "/usr/share/Modules/modulefiles:/etc/modulefiles"
  109. }
  110. return all_env
  111. def get_chisel_args(self, prefix=None):
  112. chisel_args = [
  113. (self.enable_log, "enable-log")
  114. ]
  115. args = map(lambda x: x[1], filter(lambda arg: arg[0], chisel_args))
  116. if prefix is not None:
  117. args = map(lambda x: prefix + x, args)
  118. return args
  119. def get_makefile_args(self):
  120. makefile_args = [
  121. (self.threads, "EMU_THREADS"),
  122. (self.with_dramsim3, "WITH_DRAMSIM3"),
  123. (self.is_release, "RELEASE"),
  124. (self.is_spike, "REF"),
  125. (self.trace, "EMU_TRACE"),
  126. (self.trace_fst, "EMU_TRACE"),
  127. (self.config, "CONFIG"),
  128. (self.num_cores, "NUM_CORES"),
  129. (self.emu_optimize, "EMU_OPTIMIZE"),
  130. (self.xprop, "ENABLE_XPROP"),
  131. (self.with_chiseldb, "WITH_CHISELDB"),
  132. (self.pgo, "PGO_WORKLOAD"),
  133. (self.pgo_max_cycle, "PGO_MAX_CYCLE"),
  134. (self.pgo_emu_args, "PGO_EMU_ARGS"),
  135. (self.llvm_profdata, "LLVM_PROFDATA"),
  136. ]
  137. args = filter(lambda arg: arg[0] is not None, makefile_args)
  138. args = [(shlex.quote(str(arg[0])), arg[1]) for arg in args] # shell escape
  139. return args
  140. def get_emu_args(self):
  141. emu_args = [
  142. (self.max_instr, "max-instr"),
  143. (self.diff, "diff"),
  144. (self.seed, "seed"),
  145. (self.ram_size, "ram-size"),
  146. ]
  147. args = filter(lambda arg: arg[0] is not None, emu_args)
  148. return args
  149. def show(self):
  150. print("Extra environment variables:")
  151. env = self.get_env_variables()
  152. for env_name in env:
  153. print(f"{env_name}: {env[env_name]}")
  154. print()
  155. print("Chisel arguments:")
  156. print(" ".join(self.get_chisel_args()))
  157. print()
  158. print("Makefile arguments:")
  159. for val, name in self.get_makefile_args():
  160. print(f"{name}={val}")
  161. print()
  162. print("emu arguments:")
  163. for val, name in self.get_emu_args():
  164. print(f"--{name} {val}")
  165. print()
  166. def __extract_path(self, path, env=None, default=None):
  167. if path is None and env is not None:
  168. path = os.getenv(env)
  169. if path is None and default is not None:
  170. path = default
  171. path = os.path.realpath(path)
  172. return path
  173. def set_noop_home(self, path):
  174. self.noop_home = path
  175. def set_nemu_home(self, path):
  176. self.nemu_home = path
  177. def set_am_home(self, path):
  178. self.am_home = path
  179. def set_dramsim3_home(self, path):
  180. self.dramsim3_home = path
  181. def set_rvtest_home(self, path):
  182. self.rvtest_home = path
  183. def set_wave_home(self, path):
  184. print(f"set wave home to {path}")
  185. self.wave_home = path
  186. # XiangShan environment
  187. class XiangShan(object):
  188. def __init__(self, args):
  189. self.args = XSArgs(args)
  190. self.timeout = args.timeout
  191. def show(self):
  192. self.args.show()
  193. def make_clean(self):
  194. print("Clean up CI workspace")
  195. self.show()
  196. return_code = self.__exec_cmd(f'make -C $NOOP_HOME clean')
  197. return return_code
  198. def generate_verilog(self):
  199. print("Generating XiangShan verilog with the following configurations:")
  200. self.show()
  201. sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
  202. make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
  203. return_code = self.__exec_cmd(f'make -C $NOOP_HOME verilog SIM_ARGS="{sim_args}" {make_args}')
  204. return return_code
  205. def generate_sim_verilog(self):
  206. print("Generating XiangShan sim-verilog with the following configurations:")
  207. self.show()
  208. sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
  209. make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
  210. return_code = self.__exec_cmd(f'make -C $NOOP_HOME sim-verilog SIM_ARGS="{sim_args}" {make_args}')
  211. return return_code
  212. def build_emu(self):
  213. print("Building XiangShan emu with the following configurations:")
  214. self.show()
  215. sim_args = " ".join(self.args.get_chisel_args(prefix="--"))
  216. make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
  217. return_code = self.__exec_cmd(f'make -C $NOOP_HOME emu -j200 SIM_ARGS="{sim_args}" {make_args}')
  218. return return_code
  219. def build_simv(self):
  220. print("Building XiangShan simv with the following configurations")
  221. self.show()
  222. make_args = " ".join(map(lambda arg: f"{arg[1]}={arg[0]}", self.args.get_makefile_args()))
  223. # TODO: make the following commands grouped as unseen scripts
  224. return_code = self.__exec_cmd(f'\
  225. eval `/usr/bin/modulecmd zsh load license`;\
  226. eval `/usr/bin/modulecmd zsh load synopsys/vcs/Q-2020.03-SP2`;\
  227. eval `/usr/bin/modulecmd zsh load synopsys/verdi/S-2021.09-SP1`;\
  228. VERDI_HOME=/nfs/tools/synopsys/verdi/S-2021.09-SP1 \
  229. make -C $NOOP_HOME simv {make_args} CONSIDER_FSDB=1') # set CONSIDER_FSDB for compatibility
  230. return return_code
  231. def run_emu(self, workload):
  232. print("Running XiangShan emu with the following configurations:")
  233. self.show()
  234. emu_args = " ".join(map(lambda arg: f"--{arg[1]} {arg[0]}", self.args.get_emu_args()))
  235. print("workload:", workload)
  236. numa_args = ""
  237. if self.args.numa:
  238. numa_info = get_free_cores(self.args.threads)
  239. numa_args = f"numactl -m {numa_info[0]} -C {numa_info[1]}-{numa_info[2]}"
  240. fork_args = "--enable-fork -X 10" if self.args.fork else ""
  241. diff_args = "--no-diff" if self.args.disable_diff else ""
  242. chiseldb_args = "--dump-db" if not self.args.disable_db else ""
  243. gcpt_restore_args = f"-r {self.args.gcpt_restore_bin}" if len(self.args.gcpt_restore_bin) != 0 else ""
  244. 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}')
  245. return return_code
  246. def run_simv(self, workload):
  247. print("Running XiangShan simv with the following configurations:")
  248. self.show()
  249. diff_args = "$NOOP_HOME/"+ args.diff
  250. assert_args = "-assert finish_maxfail=30 -assert global_finish_maxfail=10000"
  251. return_code = self.__exec_cmd(f'cd $NOOP_HOME/build && ./simv +workload={workload} +diff={diff_args} +dump-wave=fsdb {assert_args} | tee simv.log')
  252. with open(f"{self.args.noop_home}/build/simv.log") as f:
  253. content = f.read()
  254. if "Offending" in content or "HIT GOOD TRAP" not in content:
  255. return 1
  256. return return_code
  257. def run(self, args):
  258. if args.ci is not None:
  259. return self.run_ci(args.ci)
  260. if args.ci_vcs is not None:
  261. return self.run_ci_vcs(args.ci_vcs)
  262. actions = [
  263. (args.generate, lambda _ : self.generate_verilog()),
  264. (args.vcs_gen, lambda _ : self.generate_sim_verilog()),
  265. (args.build, lambda _ : self.build_emu()),
  266. (args.vcs_build, lambda _ : self.build_simv()),
  267. (args.workload, lambda args: self.run_emu(args.workload)),
  268. (args.clean, lambda _ : self.make_clean())
  269. ]
  270. valid_actions = map(lambda act: act[1], filter(lambda act: act[0], actions))
  271. for i, action in enumerate(valid_actions):
  272. print(f"Action {i}:")
  273. ret = action(args)
  274. if ret:
  275. return ret
  276. return 0
  277. def __exec_cmd(self, cmd):
  278. env = dict(os.environ)
  279. env.update(self.args.get_env_variables())
  280. print("subprocess call cmd:", cmd)
  281. start = time.time()
  282. proc = subprocess.Popen(cmd, shell=True, env=env, preexec_fn=os.setsid)
  283. try:
  284. return_code = proc.wait(self.timeout)
  285. end = time.time()
  286. print(f"Elapsed time: {end - start} seconds")
  287. return return_code
  288. except (KeyboardInterrupt, subprocess.TimeoutExpired):
  289. os.killpg(os.getpgid(proc.pid), signal.SIGINT)
  290. print(f"KeyboardInterrupt or TimeoutExpired.")
  291. return 0
  292. def __get_ci_cputest(self, name=None):
  293. # base_dir = os.path.join(self.args.am_home, "tests/cputest/build")
  294. base_dir = "/nfs/home/share/ci-workloads/nexus-am-workloads/tests/cputest"
  295. cputest = os.listdir(base_dir)
  296. cputest = filter(lambda x: x.endswith(".bin"), cputest)
  297. cputest = map(lambda x: os.path.join(base_dir, x), cputest)
  298. return cputest
  299. def __get_ci_rvtest(self, name=None):
  300. base_dir = os.path.join(self.args.rvtest_home, "isa/build")
  301. riscv_tests = os.listdir(base_dir)
  302. riscv_tests = filter(lambda x: x.endswith(".bin"), riscv_tests)
  303. all_rv_tests = ["rv64ui", "rv64um", "rv64ua", "rv64uf", "rv64ud", "rv64mi"]
  304. riscv_tests = filter(lambda x: x[:6] in all_rv_tests, riscv_tests)
  305. riscv_tests = map(lambda x: os.path.join(base_dir, x), riscv_tests)
  306. return riscv_tests
  307. def __get_ci_misc(self, name=None):
  308. base_dir = "/nfs/home/share/ci-workloads"
  309. workloads = [
  310. "bitmanip/bitMisc.bin",
  311. "crypto/crypto-riscv64-noop.bin",
  312. # "coremark_rv64gc_o2/coremark-riscv64-xs.bin",
  313. # "coremark_rv64gc_o3/coremark-riscv64-xs.bin",
  314. # "coremark_rv64gcb_o3/coremark-riscv64-xs.bin",
  315. "nexus-am-workloads/amtest/external_intr-riscv64-xs.bin",
  316. "nexus-am-workloads/tests/aliastest/aliastest-riscv64-xs.bin",
  317. "Svinval/rv64mi-p-svinval.bin",
  318. "pmp/pmp.riscv.bin",
  319. "nexus-am-workloads/amtest/pmp_test-riscv64-xs.bin",
  320. "nexus-am-workloads/amtest/sv39_hp_atom_test-riscv64-xs.bin",
  321. "asid/asid.bin",
  322. "isa_misc/xret_clear_mprv.bin",
  323. "isa_misc/satp_ppn.bin",
  324. "cache-management/softprefetchtest-riscv64-xs.bin"
  325. ]
  326. misc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
  327. return misc_tests
  328. def __get_ci_rvhtest(self, name=None):
  329. base_dir = "/nfs/home/share/ci-workloads/H-extension-tests"
  330. workloads = [
  331. "riscv-hyp-tests/rvh_test.bin",
  332. "xvisor_wboxtest/checkpoint.gz",
  333. ]
  334. rvh_tests = map(lambda x: os.path.join(base_dir, x), workloads)
  335. return rvh_tests
  336. def __get_ci_rvvbench(self, name=None):
  337. base_dir = "/nfs/home/share/ci-workloads"
  338. workloads = [
  339. "rvv-bench/poly1305.bin",
  340. "rvv-bench/mergelines.bin"
  341. ]
  342. rvvbench = map(lambda x: os.path.join(base_dir, x), workloads)
  343. return rvvbench
  344. def __get_ci_rvvtest(self, name=None):
  345. base_dir = "/nfs/home/share/ci-workloads/V-extension-tests"
  346. workloads = [
  347. "rvv-test/vluxei32.v-0.bin",
  348. "rvv-test/vlsseg4e32.v-0.bin",
  349. "rvv-test/vlseg4e32.v-0.bin",
  350. "rvv-test/vsetvl-0.bin",
  351. "rvv-test/vsetivli-0.bin",
  352. "rvv-test/vsuxei32.v-0.bin",
  353. "rvv-test/vse16.v-0.bin",
  354. "rvv-test/vsse16.v-1.bin",
  355. "rvv-test/vlse32.v-0.bin",
  356. "rvv-test/vsetvli-0.bin",
  357. "rvv-test/vle16.v-0.bin",
  358. "rvv-test/vle32.v-0.bin",
  359. "rvv-test/vfsgnj.vv-0.bin",
  360. "rvv-test/vfadd.vf-0.bin",
  361. "rvv-test/vfsub.vf-0.bin",
  362. "rvv-test/vslide1down.vx-0.bin"
  363. ]
  364. rvv_test = map(lambda x: os.path.join(base_dir, x), workloads)
  365. return rvv_test
  366. def __get_ci_mc(self, name=None):
  367. base_dir = "/nfs/home/share/ci-workloads"
  368. workloads = [
  369. "nexus-am-workloads/tests/dualcoretest/ldvio-riscv64-xs.bin"
  370. ]
  371. mc_tests = map(lambda x: os.path.join(base_dir, x), workloads)
  372. return mc_tests
  373. def __get_ci_nodiff(self, name=None):
  374. base_dir = "/nfs/home/share/ci-workloads"
  375. workloads = [
  376. "cache-management/cacheoptest-riscv64-xs.bin"
  377. ]
  378. tests = map(lambda x: os.path.join(base_dir, x), workloads)
  379. return tests
  380. def __am_apps_path(self, bench):
  381. base_dir = '/nfs/home/share/ci-workloads/nexus-am-workloads/apps'
  382. filename = f"{bench}-riscv64-xs.bin"
  383. return [os.path.join(base_dir, bench, filename)]
  384. def __get_ci_workloads(self, name):
  385. workloads = {
  386. "linux-hello": "bbl.bin",
  387. "linux-hello-smp": "bbl.bin",
  388. "linux-hello-opensbi": "fw_payload.bin",
  389. "linux-hello-smp-opensbi": "fw_payload.bin",
  390. "linux-hello-new": "bbl.bin",
  391. "linux-hello-smp-new": "bbl.bin",
  392. "povray": "_700480000000_.gz",
  393. "mcf": "_17520000000_.gz",
  394. "xalancbmk": "_266100000000_.gz",
  395. "gcc": "_39720000000_.gz",
  396. "namd": "_434640000000_.gz",
  397. "milc": "_103620000000_.gz",
  398. "lbm": "_140840000000_.gz",
  399. "gromacs": "_275480000000_.gz",
  400. "wrf": "_1916220000000_.gz",
  401. "astar": "_122060000000_.gz"
  402. }
  403. if name in workloads:
  404. return [os.path.join("/nfs/home/share/ci-workloads", name, workloads[name])]
  405. # select a random SPEC checkpoint
  406. assert(name == "random")
  407. all_cpt = [
  408. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/take_cpt",
  409. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/take_cpt",
  410. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/take_cpt",
  411. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/take_cpt",
  412. "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/take_cpt",
  413. "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/take_cpt",
  414. "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/take_cpt",
  415. "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/take_cpt"
  416. ]
  417. all_json = [
  418. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o2_20m/json/simpoint_summary.json",
  419. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gcb_o3_20m/simpoint_summary.json",
  420. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_20m/simpoint_summary.json",
  421. "/nfs-nvme/home/share/checkpoints_profiles/spec06_rv64gc_o2_50m/simpoint_summary.json",
  422. "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o2_20m/simpoint_summary.json",
  423. "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gcb_o3_20m/simpoint_summary.json",
  424. "/nfs-nvme/home/share/checkpoints_profiles/spec17_rv64gc_o2_50m/simpoint_summary.json",
  425. "/nfs-nvme/home/share/checkpoints_profiles/spec17_speed_rv64gcb_o3_20m/simpoint_summary.json"
  426. ]
  427. assert(len(all_cpt) == len(all_json))
  428. cpt_path, json_path = random.choice(list(zip(all_cpt, all_json)))
  429. all_gcpt = load_all_gcpt(cpt_path, json_path)
  430. return [random.choice(all_gcpt)]
  431. def run_ci(self, test):
  432. all_tests = {
  433. "cputest": self.__get_ci_cputest,
  434. "riscv-tests": self.__get_ci_rvtest,
  435. "misc-tests": self.__get_ci_misc,
  436. "mc-tests": self.__get_ci_mc,
  437. "nodiff-tests": self.__get_ci_nodiff,
  438. "rvh-tests": self.__get_ci_rvhtest,
  439. "microbench": self.__am_apps_path,
  440. "coremark": self.__am_apps_path,
  441. "coremark-1-iteration": self.__am_apps_path,
  442. "rvv-bench": self.__get_ci_rvvbench,
  443. "rvv-test": self.__get_ci_rvvtest
  444. }
  445. for target in all_tests.get(test, self.__get_ci_workloads)(test):
  446. print(target)
  447. ret = self.run_emu(target)
  448. if ret:
  449. if self.args.default_wave_home != self.args.wave_home:
  450. print("copy wave file to " + self.args.wave_home)
  451. self.__exec_cmd(f"cp $NOOP_HOME/build/*.vcd $WAVE_HOME")
  452. self.__exec_cmd(f"cp $NOOP_HOME/build/emu $WAVE_HOME")
  453. self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
  454. self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
  455. return ret
  456. return 0
  457. def run_ci_vcs(self, test):
  458. all_tests = {
  459. "cputest": self.__get_ci_cputest,
  460. "riscv-tests": self.__get_ci_rvtest,
  461. "misc-tests": self.__get_ci_misc,
  462. "mc-tests": self.__get_ci_mc,
  463. "nodiff-tests": self.__get_ci_nodiff,
  464. "rvh-tests": self.__get_ci_rvhtest,
  465. "microbench": self.__am_apps_path,
  466. "coremark": self.__am_apps_path,
  467. "coremark-1-iteration": self.__am_apps_path,
  468. "rvv-bench": self.__get_ci_rvvbench,
  469. "rvv-test": self.__get_ci_rvvtest
  470. }
  471. for target in all_tests.get(test, self.__get_ci_workloads)(test):
  472. print(target)
  473. ret = self.run_simv(target)
  474. if ret:
  475. if self.args.default_wave_home != self.args.wave_home:
  476. print("copy wave file to " + self.args.wave_home)
  477. self.__exec_cmd(f"cp $NOOP_HOME/build/*.fsdb $WAVE_HOME")
  478. self.__exec_cmd(f"cp $NOOP_HOME/build/simv $WAVE_HOME")
  479. self.__exec_cmd(f"cp $NOOP_HOME/build/rtl/SimTop.v $WAVE_HOME")
  480. self.__exec_cmd(f"cp $NOOP_HOME/build/*.db $WAVE_HOME")
  481. return ret
  482. return 0
  483. def get_free_cores(n):
  484. numa_re = re.compile(r'.*numactl +.*-C +([0-9]+)-([0-9]+).*')
  485. while True:
  486. disable_cores = []
  487. for proc in psutil.process_iter():
  488. try:
  489. joint = ' '.join(proc.cmdline())
  490. numa_match = numa_re.match(joint)
  491. if numa_match and 'ssh' not in proc.name():
  492. disable_cores.extend(range(int(numa_match.group(1)), int(numa_match.group(2)) + 1))
  493. except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
  494. pass
  495. num_logical_core = psutil.cpu_count(logical=False)
  496. core_usage = psutil.cpu_percent(interval=1, percpu=True)
  497. num_window = num_logical_core // n
  498. for i in range(num_window):
  499. if set(disable_cores) & set(range(i * n, i * n + n)):
  500. continue
  501. window_usage = core_usage[i * n : i * n + n]
  502. if sum(window_usage) < 30 * n and True not in map(lambda x: x > 90, window_usage):
  503. return (((i * n) % num_logical_core) // (num_logical_core // 2), i * n, i * n + n - 1)
  504. print(f"No free {n} cores found. CPU usage: {core_usage}\n")
  505. time.sleep(random.uniform(1, 60))
  506. if __name__ == "__main__":
  507. parser = argparse.ArgumentParser(description='Python wrapper for XiangShan')
  508. parser.add_argument('workload', nargs='?', type=str, default="",
  509. help='input workload file in binary format')
  510. # actions
  511. parser.add_argument('--build', action='store_true', help='build XS emu')
  512. parser.add_argument('--generate', action='store_true', help='generate XS verilog')
  513. parser.add_argument('--vcs-gen', action='store_true', help='generate XS sim verilog for vcs')
  514. parser.add_argument('--vcs-build', action='store_true', help='build XS simv')
  515. parser.add_argument('--ci', nargs='?', type=str, const="", help='run CI tests')
  516. parser.add_argument('--ci-vcs', nargs='?', type=str, const="", help='run CI tests on simv')
  517. parser.add_argument('--clean', action='store_true', help='clean up XiangShan CI workspace')
  518. parser.add_argument('--timeout', nargs='?', type=int, default=None, help='timeout (in seconds)')
  519. # environment variables
  520. parser.add_argument('--nemu', nargs='?', type=str, help='path to nemu')
  521. parser.add_argument('--am', nargs='?', type=str, help='path to nexus-am')
  522. parser.add_argument('--dramsim3', nargs='?', type=str, help='path to dramsim3')
  523. parser.add_argument('--rvtest', nargs='?', type=str, help='path to riscv-tests')
  524. parser.add_argument('--wave-dump', nargs='?', type=str , help='path to dump wave')
  525. # chisel arguments
  526. parser.add_argument('--enable-log', action='store_true', help='enable log')
  527. parser.add_argument('--num-cores', type=int, help='number of cores')
  528. # makefile arguments
  529. parser.add_argument('--release', action='store_true', help='enable release')
  530. parser.add_argument('--spike', action='store_true', help='enable spike diff')
  531. parser.add_argument('--with-dramsim3', action='store_true', help='enable dramsim3')
  532. parser.add_argument('--threads', nargs='?', type=int, help='number of emu threads')
  533. parser.add_argument('--trace', action='store_true', help='enable vcd waveform')
  534. parser.add_argument('--trace-fst', action='store_true', help='enable fst waveform')
  535. parser.add_argument('--config', nargs='?', type=str, help='config')
  536. parser.add_argument('--emu-optimize', nargs='?', type=str, help='verilator optimization letter')
  537. parser.add_argument('--xprop', action='store_true', help='enable xprop for vcs')
  538. # emu arguments
  539. parser.add_argument('--numa', action='store_true', help='use numactl')
  540. parser.add_argument('--diff', nargs='?', default="./ready-to-run/riscv64-nemu-interpreter-so", type=str, help='nemu so')
  541. parser.add_argument('--max-instr', nargs='?', type=int, help='max instr')
  542. parser.add_argument('--disable-fork', action='store_true', help='disable lightSSS')
  543. parser.add_argument('--no-diff', action='store_true', help='disable difftest')
  544. parser.add_argument('--ram-size', nargs='?', type=str, help='manually set simulation memory size (8GB by default)')
  545. parser.add_argument('--gcpt-restore-bin', type=str, default="", help="specify the bin used to restore from gcpt")
  546. # both makefile and emu arguments
  547. parser.add_argument('--no-db', action='store_true', help='disable chiseldb dump')
  548. parser.add_argument('--pgo', nargs='?', type=str, help='workload for pgo (null to disable pgo)')
  549. parser.add_argument('--pgo-max-cycle', nargs='?', default=400000, type=int, help='maximun cycle to train pgo')
  550. parser.add_argument('--pgo-emu-args', nargs='?', default='--no-diff', type=str, help='emu arguments for pgo')
  551. parser.add_argument('--llvm-profdata', nargs='?', type=str, help='corresponding llvm-profdata command of clang to compile emu, do not set with GCC')
  552. args = parser.parse_args()
  553. xs = XiangShan(args)
  554. ret = xs.run(args)
  555. sys.exit(ret)