core-v-verif系列之cva6 cva6.py执行示例(1)

执行命令 bash verif/regress/dv-riscv-arch-test.sh

bash verif/regress/dv-riscv-arch-test.sh

备注, 这里我们设置了环境变量
export RISCV=RISCV_TOOLS
export DV_SIMULATORS=veri-testharness,spike
export NUM_JOBS=$(nproc)

执行输出日志

[install-verilator.sh] Entry values:
    VERILATOR_BUILD_DIR=''
    VERILATOR_INSTALL_DIR=''
Setting VERILATOR_INSTALL_DIR to '/cva6/tools/verilator-v5.008'...
Setting VERILATOR_BUILD_DIR to '/cva6/tools/verilator-v5.008/build-v5.008'...
Verilator already installed in '/cva6/tools/verilator-v5.008'.
Spike already installed in '/cva6/tools/spike'.
Spike already installed in '/cva6/tools/spike'.
Repo:   https://github.com/riscv-non-isa/riscv-arch-test
Branch: main
Hash:   a5a49fc9f244192649e57fe61b4513d9bc39b1e3
Spike dir: /cva6/verif/core-v-verif/vendor/riscv/riscv-isa-sim
Executing command: python3 cva6.py --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml --target cv64a6_imafdc_sv39 --iss_yaml=cva6.yaml --iss=veri-testharness,spike  --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld

执行python3 cva6.py调用顺序

Executing command: python3 cva6.py --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml --target cv64a6_imafdc_sv39 --iss_yaml=cva6.yaml --iss=veri-testharness,spike  --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld

以下是执行该脚本时的主要函数调用顺序:

  1. main():

    • 初始化全局变量。
    • 获取当前工作目录并解析命令行参数。
    • 加载配置文件。
    • 设置 issrun_optslog_format
    • 记录 UVM seed。
    • 设置日志记录器。
    • 检查工具版本。
    • 创建日志文件处理程序。
    • 创建输出目录。
    • 添加 ISA 扩展。
    • 如果设置了 verilog_style_check 参数,则运行 Verilog 风格检查。
  2. run_test(如果指定了 c_testself_testsasm_tests 参数):

    • 根据测试类型(C 文件、汇编文件或 ELF 文件)编译和运行定向测试。
  3. openhw_process_regression_list (如果没有指定定向测试且未设置 --co 参数):

    • 处理回归测试列表并获取匹配的测试。
  4. gen(如果 steps 参数包含 genall):

    • get_generator_cmd:
      • 设置编译和仿真命令。
    • do_compile:
      • 编译指令生成器。
    • do_simulate:
      • 运行指令生成器。
  5. gcc_compile(如果 steps 参数包含 gcc_compileall):

    • 使用 RISC-V GCC 工具链编译汇编程序。
  6. iss_sim(如果 steps 参数包含 iss_simall):

    • 运行 ISS 仿真。
  7. iss_cmp(如果 steps 参数包含 iss_cmpall):

    • 比较 ISS 仿真结果。
  8. sys.exit(RET_SUCCESS):

    • 成功完成后退出程序。

参数解释

  • --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml:指定回归测试列表文件。
  • --target=cv64a6_imafdc_sv39:指定目标配置。
  • --iss_yaml=cva6.yaml:指定 ISS 配置文件。
  • --iss=veri-testharness,spike:指定使用的 ISS。
  • --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE:指定 ISS 运行选项。
  • --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld:指定链接器文件。

总结

主要函数调用顺序从 main 函数开始,根据输入参数和配置文件逐步调用其他函数以完成编译、仿真和结果比较等任务。

main函数

函数调用顺序分析

下面我们将从 main 函数开始,逐步分析在指定输入参数情况下 cva6.py 脚本中各个函数的调用顺序。

输入参数
python3 cva6.py --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml --target=cv64a6_imafdc_sv39 --iss_yaml=cva6.yaml --iss=veri-testharness,spike --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld
main 函数
def main():
    try:
        global issrun_opts
        global test_iteration
        global log_format
        cwd = os.path.dirname(os.path.realpath(__file__))
        args = parse_args(cwd)
        load_config(args, cwd)

        if args.axi_active == "yes":
            args.issrun_opts = args.issrun_opts + " +uvm_set_config_int=*uvm_test_top,force_axi_mode,1"
        elif args.axi_active == "no":
            args.issrun_opts = args.issrun_opts + " +uvm_set_config_int=uvm_test_top,force_axi_mode,0"

        if args.gen_sv_seed > 0 and args.sv_seed != "1":
            logging.error('You cannot use gen_sv_seed and sv_seed options at the same time')

        if args.gen_sv_seed > 0:
            args.issrun_opts = args.issrun_opts + " +ntb_random_seed_automatic"
            log_format = 1
        elif args.gen_sv_seed == 0:
            args.issrun_opts = args.issrun_opts + " +ntb_random_seed=" + args.sv_seed
            args.gen_sv_seed = 1
            log_format = 0
        else:
            logging.error('gen_sv_seed can not take a negative value')

        log_uvm_seed(args.sv_seed)

        issrun_opts = "\""+args.issrun_opts+"\""

        global isspostrun_opts
        isspostrun_opts = "\""+args.isspostrun_opts+"\""
        global isscomp_opts
        isscomp_opts = "\""+args.isscomp_opts+"\""
        setup_logging(args.verbose)
        logg = logging.getLogger()

        if args.iss:
            args_list = args.iss.split(',')
            if 'spike' in args_list:
                args_list.remove('spike')
                args_list.insert(0, 'spike')
            args.iss = ','.join(args_list)

        check_tools_version()

        fh = logging.FileHandler('logfile.log')
        fh.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
        fh.setFormatter(formatter)
        logg.addHandler(fh)

        output_dir = create_output(args.o, args.noclean, cwd + "/out_")

        if isa_extension_list != ['']:
            for i in isa_extension_list:
                if i != "":
                    args.isa += (f"_{i}")

        if args.verilog_style_check:
            logging.debug("Run style check")
            style_err = run_cmd("verilog_style/run.sh")
            if style_err: logging.info("Found style error: \nERROR: " + style_err)

        for i in range(args.gen_sv_seed):
            test_executed = 0
            test_iteration = i
            print("")
            logging.info("Iteration number: %s" % (i + 1))

            tests = ""
            if args.c_tests != "":
                tests = args.c_tests.split(',')
            elif args.elf_tests != "":
                tests = args.elf_tests.split(',')
            elif args.asm_tests != "":
                tests = args.asm_tests.split(',')
            if tests != "":
                for path_test in tests:
                    full_path = os.path.expanduser(path_test)
                    if os.path.isfile(full_path) or args.debug:
                        run_test(full_path, args.iss_yaml, args.isa, args.target, args.mabi, args.gcc_opts,
                                 args.iss, output_dir, args.core_setting_dir, args.debug, args.linker,
                                 args.priv, args.spike_params, iss_timeout=args.iss_timeout)
                    else:
                        logging.error('%s does not exist or is not a file' % full_path)
                        sys.exit(RET_FAIL)
                    test_executed = 1

            run_cmd_output(["mkdir", "-p", ("%s/asm_tests" % output_dir)])
            matched_list = []
            asm_directed_list = []
            c_directed_list = []

            if test_executed == 0:
                if not args.co:
                    openhw_process_regression_list(args.testlist, args.test, args.iterations, matched_list, cwd)
                    logging.info('CVA6 Configuration is %s and target is %s' % (args.hwconfig_opts, args.target))
                    for entry in list(matched_list):
                        yaml_needs = entry["needs"] if "needs" in entry else []
                        if yaml_needs:
                            needs = dict()
                            for i in range(len(yaml_needs)):
                                needs.update(yaml_needs[i])
                            for keys in needs.keys():
                                if args.hwconfig_opts[keys] != needs[keys]:
                                    logging.info('Removing test %s CVA6 configuration can not run it' % entry['test'])
                                    matched_list.remove(entry)
                                    break
                    for t in list(matched_list):
                        try:
                            t['gcc_opts'] = re.sub(r"\", get_env_var(t['path_var']), t['gcc_opts'])
                        except KeyError:
                            continue

                        if 'asm_tests' in t:
                            if 'gen_test' in t or 'c_tests' in t:
                                logging.error('asm_tests must not be defined in the testlist '
                                              'together with the gen_test or c_tests field')
                                sys.exit(RET_FATAL)
                            t['asm_tests'] = re.sub(r"\", get_env_var(t['path_var']), t['asm_tests'])
                            asm_directed_list.append(t)
                            matched_list.remove(t)

                        if 'c_tests' in t:
                            if 'gen_test' in t or 'asm_tests' in t:
                                logging.error('c_tests must not be defined in the testlist '
                                              'together with the gen_test or asm_tests field')
                                sys.exit(RET_FATAL)
                            t['c_tests'] = re.sub(r"\", get_env_var(t['path_var']), t['c_tests'])
                            c_directed_list.append(t)
                            matched_list.remove(t)

                    if len(matched_list) == 0 and len(asm_directed_list) == 0 and len(c_directed_list) == 0:
                        sys.exit("Cannot find %s in %s" % (args.test, args.testlist))

                    for t in c_directed_list:
                        copy = re.sub(r'(.*)\/(.*).c$', r'cp \1/\2.c \1/', t['c_tests']) + t['test'] + '.c'
                        run_cmd("%s" % copy)
                        t['c_tests'] = re.sub(r'(.*)\/(.*).c$', r'\1/', t['c_tests']) + t['test'] + '.c'

            directed_tests_list = asm_directed_list + c_directed_list
            if args.steps == "all" or re.match(".*gen.*", args.steps):
                if len(directed_tests_list) != 0:
                    for test_entry in asm_directed_list:
                        gcc_opts = args.gcc_opts
                        gcc_opts += test_entry.get('gcc_opts', '')
                        path_test = os.path.expanduser(test_entry.get('asm_tests'))
                        if path_test:
                            if os.path.isfile(path_test):
                                run_test(path_test, args.iss_yaml, args.isa, args.target, args.mabi, gcc_opts,
                                         args.iss, output_dir, args.core_setting_dir, args.debug, args.linker,
                                         args.priv, args.spike_params, test_entry['test'], iss_timeout=args.iss_timeout, testlist=args.testlist)
                            else:
                                if not args.debug:
                                    logging.error('%s does not exist' % path_test)
                                    sys.exit(RET_FAIL)

                gen(matched_list, args, output_dir, cwd)

            if not args.co:
                if args.steps == "all" or re.match(".*gcc_compile.*", args.steps):
                    gcc_compile(matched_list, output_dir, args.isa, args.mabi,
                                args.gcc_opts, args.debug, args.linker)

                if args.steps == "all" or re.match(".*iss_sim.*", args.steps):
                    iss_sim(matched_list, output_dir, args.iss, args.iss_yaml, args.iss_opts,
                            args.isa, args.target, args.core_setting_dir, args.iss_timeout, args.debug,
                            args.priv, args.spike_params)

                if args.steps == "all" or re.match(".*iss_cmp.*", args.steps):
                    iss_cmp(matched_list, args.iss, args.target, output_dir, args.stop_on_first_error,
                            args.exp, args.debug)

        sys.exit(RET_SUCCESS)
    except KeyboardInterrupt:
        logging.info("\nExited Ctrl-C from user request.")
        sys.exit(130)

if __name__ == "__main__":
    sys.path.append(os.getcwd() + "/../../util")
    import user_config
    main()

当你执行以下命令运行 cva6.py 脚本时:

python3 cva6.py --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml \
                --target cv64a6_imafdc_sv39 \
                --iss_yaml=cva6.yaml \
                --iss=veri-testharness,spike  \
                --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE \
                --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld

main() 函数的执行逻辑

main() 执行时,流程如下:


步骤 1:解析命令行参数

  • parse_args(cwd) 解析传递的命令行参数:
    • --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml:指定测试列表。
    • --target=cv64a6_imafdc_sv39:目标架构为 cv64a6_imafdc_sv39
    • --iss_yaml=cva6.yaml:指定 ISS(指令集仿真器)的配置文件。
    • --iss=veri-testharness,spike:选择 veri-testharnessspike 作为 ISS。
    • --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE:传递 ISS 运行选项。
    • --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld:使用特定的链接脚本。

步骤 2:加载配置

  • 调用 load_config(args, cwd)
    • 确保 --testlist--target--iss_yaml--linker 等参数正确。
    • 解析 YAML 配置(如 cva6.yaml),获取 ISS 运行方式。
    • 根据 --target 设置 ISA(如 rv64gc)、MABI 等。

基于给定的命令 load_config 函数的执行逻辑:

  1. 初始配置设置:
- 命令参数: 
  - target = cv64a6_imafdc_sv39
  - testlist = ../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml
  - iss_yaml = cva6.yaml
  - linker = ../tests/riscv-arch-test/riscv-target/spike/link.ld
  1. YAML文件路径处理:
- iss_yaml 被设置为 cva6.yaml(由命令行参数指定)
- csr_yaml 使用默认值: cwd + "/yaml/csr_template.yaml"
- simulator_yaml 使用默认值: cwd + "/cva6-simulator.yaml"
  1. 目标架构配置:
- base = "cv64a6_imafdc_sv39"(来自target参数)
- 根据base值设置:
  - args.mabi = "lp64d"
  - args.isa = "rv64gc_zba_zbb_zbs_zbc_zbkb"
  1. 核心目录设置:
- args.core_setting_dir = cwd + "/dv/target/rv64gc_zba_zbb_zbs_zbc_zbkb"
  1. ISA扩展处理:
- 由于ISA包含"g"扩展,不会添加额外的zicsr和zifencei扩展
- isa_extension_list 根据--isa_extension参数进行设置(此例中为空)
  1. 链接器设置:
- 使用命令行指定的链接器路径:
  ../tests/riscv-arch-test/riscv-target/spike/link.ld

执行流程主要是根据目标架构(cv64a6_imafdc_sv39)设置相应的ISA、ABI和其他配置参数,为后续的测试运行准备环境。该配置特别针对64位RISC-V核心,支持RV64GC指令集以及一些位操作扩展(zba, zbb, zbs, zbc, zbkb)。

步骤 3:工具版本检查

  • 调用 check_tools_version()
    • 检查 GCC 版本是否符合要求(>= 11)。
    • 检查 Spike 版本是否匹配 core-v-verif
    • 检查 Verilator 版本是否匹配。

如果版本不符合,程序会退出。


check_tools_version() 函数主要检查三个关键工具的版本:

1. GCC检查 (check_cc_version())
- 要求GCC版本 >= 11
- 执行步骤:
  1. 获取GCC路径: RISCV_CC环境变量
  2. 运行 `{cc_path} --version` 获取版本信息
  3. 解析版本号
  4. 检查版本是否满足要求(>=11)
2. Spike检查 (check_spike_version())
- 执行步骤:
  1. 获取标准Spike版本:
     - 从core-v-verif子模块获取git hash
     - 组合成标准版本字符串: "1.1.1-dev {hash}"
  2. 获取用户安装的Spike版本:
     - 运行 `$SPIKE_PATH/spike -v`
  3. 比较两个版本是否匹配
  4. 如果版本不匹配或检查失败:
     - 收集诊断信息(ldd输出、lib目录内容等)
     - 提示用户重新安装
3. Verilator检查 (check_verilator_version())
- 要求Verilator版本 = 5.008
- 执行步骤:
  1. 运行 `verilator --version` 获取版本信息
  2. 解析版本号
  3. 检查是否为要求的5.008版本
4. 错误处理
- 当版本检查失败时调用 `incorrect_version_exit()`:
  1. 对Spike特别处理:提示清理步骤
  2. 显示错误信息:当前版本vs要求版本
  3. 建议用安装脚本重新安装
  4. 退出程序(返回失败状态码)
5. 执行顺序
1. check_cc_version()
2. check_spike_version()
3. check_verilator_version()

任何一个检查失败都会导致程序退出

步骤 4:创建输出目录

  • 通过 create_output(args.o, args.noclean, cwd+"/out_") 创建或清理输出目录。

create_output 函数的主要作用是创建并准备输出目录。

函数调用
output_dir = create_output(args.o, args.noclean, cwd+"/out_")
参数说明
  • args.o: 用户指定的输出目录
  • args.noclean: 是否保留之前的输出(True表示不清理)
  • cwd+"/out_": 默认输出目录前缀
执行流程
  1. 判断输出目录
if output:
    # 使用用户指定的输出目录
    output_dir = output
else:
    # 使用默认目录,添加时间戳
    output_dir = default_dir + strftime("%Y%m%d_%H%M%S")
  1. 目录清理
if os.path.exists(output_dir):
    if noclean:
        # 如果目录存在且noclean=True,则保留原目录
        logging.info("Output directory {} already exists, not cleaning".format(output_dir))
    else:
        # 否则删除已存在的目录
        shutil.rmtree(output_dir)
  1. 创建目录
os.makedirs(output_dir)
  1. 返回目录路径
return output_dir
实际执行示例

对于命令:

python3 cva6.py --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml --target cv64a6_imafdc_sv39 --iss_yaml=cva6.yaml --iss=veri-testharness,spike --issrun_opts=+tb_performance_mode+debug_disable=1+UVM_VERBOSITY=UVM_NONE --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld

由于没有指定 -o 参数,所以会:

  1. 使用默认路径 cwd+"/out_"+timestamp
  2. 创建带时间戳的新目录
  3. 如果目录已存在且noclean=True,则保留原目录内容
  4. 返回创建的目录路径供后续使用

你可能感兴趣的:(cva6,core-v-verif)