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
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
以下是执行该脚本时的主要函数调用顺序:
main()
:
issrun_opts
和 log_format
。verilog_style_check
参数,则运行 Verilog 风格检查。run_test
(如果指定了 c_tests
、elf_tests
或 asm_tests
参数):
openhw_process_regression_list
(如果没有指定定向测试且未设置 --co
参数):
gen
(如果 steps
参数包含 gen
或 all
):
get_generator_cmd
:
do_compile
:
do_simulate
:
gcc_compile
(如果 steps
参数包含 gcc_compile
或 all
):
iss_sim
(如果 steps
参数包含 iss_sim
或 all
):
iss_cmp
(如果 steps
参数包含 iss_cmp
或 all
):
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
函数开始,逐步分析在指定输入参数情况下 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
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()
执行时,流程如下:
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-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
:使用特定的链接脚本。load_config(args, cwd)
:
--testlist
、--target
、--iss_yaml
、--linker
等参数正确。cva6.yaml
),获取 ISS 运行方式。--target
设置 ISA
(如 rv64gc
)、MABI
等。基于给定的命令 load_config
函数的执行逻辑:
- 命令参数:
- 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
- iss_yaml 被设置为 cva6.yaml(由命令行参数指定)
- csr_yaml 使用默认值: cwd + "/yaml/csr_template.yaml"
- simulator_yaml 使用默认值: cwd + "/cva6-simulator.yaml"
- base = "cv64a6_imafdc_sv39"(来自target参数)
- 根据base值设置:
- args.mabi = "lp64d"
- args.isa = "rv64gc_zba_zbb_zbs_zbc_zbkb"
- args.core_setting_dir = cwd + "/dv/target/rv64gc_zba_zbb_zbs_zbc_zbkb"
- 由于ISA包含"g"扩展,不会添加额外的zicsr和zifencei扩展
- isa_extension_list 根据--isa_extension参数进行设置(此例中为空)
- 使用命令行指定的链接器路径:
../tests/riscv-arch-test/riscv-target/spike/link.ld
执行流程主要是根据目标架构(cv64a6_imafdc_sv39)设置相应的ISA、ABI和其他配置参数,为后续的测试运行准备环境。该配置特别针对64位RISC-V核心,支持RV64GC指令集以及一些位操作扩展(zba, zbb, zbs, zbc, zbkb)。
check_tools_version()
:
>= 11
)。core-v-verif
。如果版本不符合,程序会退出。
check_tools_version()
函数主要检查三个关键工具的版本:
check_cc_version()
)- 要求GCC版本 >= 11
- 执行步骤:
1. 获取GCC路径: RISCV_CC环境变量
2. 运行 `{cc_path} --version` 获取版本信息
3. 解析版本号
4. 检查版本是否满足要求(>=11)
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目录内容等)
- 提示用户重新安装
check_verilator_version()
)- 要求Verilator版本 = 5.008
- 执行步骤:
1. 运行 `verilator --version` 获取版本信息
2. 解析版本号
3. 检查是否为要求的5.008版本
- 当版本检查失败时调用 `incorrect_version_exit()`:
1. 对Spike特别处理:提示清理步骤
2. 显示错误信息:当前版本vs要求版本
3. 建议用安装脚本重新安装
4. 退出程序(返回失败状态码)
1. check_cc_version()
2. check_spike_version()
3. check_verilator_version()
任何一个检查失败都会导致程序退出
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_"
: 默认输出目录前缀if output:
# 使用用户指定的输出目录
output_dir = output
else:
# 使用默认目录,添加时间戳
output_dir = default_dir + strftime("%Y%m%d_%H%M%S")
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)
os.makedirs(output_dir)
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
参数,所以会:
cwd+"/out_"+timestamp
noclean=True
,则保留原目录内容