xref: /XiangShan/scripts/xiangshan.py (revision 991a33f048c073dc71079aca9759a3adff502ad8)
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