验证自己的处理器——基于riscv-tests
在使用riscv-tests之前我们需要安装riscv-tool-chain 编译链并将 RISCV 环境变量设置为 RISC-V 工具 install 路径。可以参考之前的文章ubuntu20.04 riscv-gnu-toolchain编译链极简安装_ubuntu安装risv-gun-tools-CSDN博客安装好编译链后构建最新的riscv-tests$ git clone https://github.com/riscv/riscv-tests $ cd riscv-tests $ git submodule update --init --recursive $ autoconf $ ./configure --prefix$RISCV/target $ make $ make installriscv-tests的链接脚本link.ld在riscv-tests/env/p文件夹中可以根据自己的处理器内存映射进行修改不修改则默认连续。在/riscv-tests/isa路径下找到Makefile文件由于处理器目前只支持RV32I指令集因此对Makefile文件进行了修改# # Makefile for riscv-tests/isa #----------------------------------------------------------------------- XLEN ? 64 src_dir : . include $(src_dir)/rv64ui/Makefrag include $(src_dir)/rv32ui/Makefrag default: all #-------------------------------------------------------------------- # Build rules #-------------------------------------------------------------------- RISCV_PREFIX ? riscv64-unknown-elf- RISCV_GCC ? $(RISCV_PREFIX)gcc RISCV_GCC_OPTS ? -static -mcmodelmedany -fvisibilityhidden -nostdlib -nostartfiles RISCV_OBJDUMP ? $(RISCV_PREFIX)objdump --disassemble-all --disassemble-zeroes --section.text --section.text.startup --section.text.init --section.data RISCV_SIM ? spike vpath %.S $(src_dir) #------------------------------------------------------------ # Build assembly tests %.dump: % $(RISCV_OBJDUMP) $ $ %.out: % $(RISCV_SIM) --isarv64gch_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs --misaligned $ 2 $ %.out32: % $(RISCV_SIM) --isarv32gc_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs --misaligned $ 2 $ define compile_template $$($(1)_p_tests): $(1)-p-%: $(1)/%.S $$(RISCV_GCC) $(2) $$(RISCV_GCC_OPTS) -I$(src_dir)/../env/p -I$(src_dir)/macros/scalar -T$(src_dir)/../env/p/link.ld $$ -o $$ $(1)_tests $$($(1)_p_tests) $$($(1)_v_tests): $(1)-v-%: $(1)/%.S $$(RISCV_GCC) $(2) $$(RISCV_GCC_OPTS) -DENTROPY0x$$(shell echo \$$ | md5sum | cut -c 1-7) -stdgnu99 -O2 -I$(src_dir)/../env/v -I$(src_dir)/macros/scalar -T$(src_dir)/../env/v/link.ld $(src_dir)/../env/v/entry.S $(src_dir)/../env/v/*.c $$ -o $$ $(1)_tests $$($(1)_v_tests) $(1)_tests_dump $$(addsuffix .dump, $$($(1)_tests)) $(1): $$($(1)_tests_dump) .PHONY: $(1) COMPILER_SUPPORTS_$(1) : $$(shell $$(RISCV_GCC) $(2) -c -x c /dev/null -o /dev/null 2 /dev/null; echo $$$$?) ifeq ($$(COMPILER_SUPPORTS_$(1)),0) tests $$($(1)_tests) endif endef $(eval $(call compile_template,rv32ui,-marchrv32i -mabiilp32)) tests_dump $(addsuffix .dump, $(tests)) tests_hex $(addsuffix .hex, $(tests)) tests_out $(addsuffix .out, $(filter rv64%,$(tests))) tests32_out $(addsuffix .out32, $(filter rv32%,$(tests))) run: $(tests_out) $(tests32_out) junk $(tests) $(tests_dump) $(tests_hex) $(tests_out) $(tests32_out) #------------------------------------------------------------ # Default all: $(tests_dump) #------------------------------------------------------------ # Clean up clean: rm -rf $(junk)保存后退出输入make命令将在当前路径下生成rv32i指令集中指令的.hex文件和.dump文件如图所示将它们分别保存到两个不同的文件夹中方便后续使用进入hex文件夹创建hex2bin.py文件#!/usr/bin/env python3 import os import subprocess import glob def batch_generate_bin(): # 设置工具链路径 objcopy riscv64-unknown-elf-objcopy # 检查 objcopy 是否可用 try: subprocess.run([objcopy, --version], capture_outputTrue, checkTrue) except (subprocess.CalledProcessError, FileNotFoundError): print(f错误: 找不到 {objcopy}请检查工具链路径) return False # 创建输出目录 - 使用 ../bin output_dir ../bin os.makedirs(output_dir, exist_okTrue) # 查找所有 rv32ui-p-* 文件无后缀 test_files [f for f in glob.glob(rv32ui-p-*) if os.path.isfile(f)] # 过滤掉可能误匹配的文件 test_files [f for f in test_files if not f.endswith((.bin, .dump, .S, .s, .c))] if not test_files: print(未找到任何 rv32ui-p-* 测试文件) return False success_count 0 failed_files [] print(f找到 {len(test_files)} 个测试文件) for test_file in test_files: # 生成 bin 文件名 - 使用 ../bin 目录 bin_file os.path.join(output_dir, f{test_file}.bin) try: # 执行转换命令 result subprocess.run( [objcopy, -O, binary, test_file, bin_file], capture_outputTrue, textTrue, checkTrue ) if os.path.exists(bin_file) and os.path.getsize(bin_file) 0: success_count 1 else: print(f ✗ 生成的文件为空或不存在) failed_files.append(test_file) except subprocess.CalledProcessError as e: print(f ✗ 转换失败: {e.stderr.strip()}) failed_files.append(test_file) # 输出总结 print(\n *50) print(f批量转换完成) print(f成功: {success_count}/{len(test_files)}) if failed_files: print(f失败的文件:) for f in failed_files: print(f - {f}) return len(failed_files) 0 if __name__ __main__: batch_generate_bin()运行生成的.bin文件在/MyCPU_test/bin目录下。接下来进行编译与仿真建议安装iverilog与gtkwave$ git clone https://github.com/steveicarus/iverilog.git $ sudo apt-get install autoconf gperf flex bison build-essential $ sh autoconf.sh $ ./configure $ make $ make install $ sudo ln -s /usr/bin/python3.8 /usr/bin/python $ sudo apt-get install gtkwave在当前目录下添加rtl文件夹将所有.v文件放入文件夹中并添加compile_rtl.py文件import sys import subprocess import os def main(): rtl_dir ./rtl if not os.path.exists(rtl_dir): print(f错误: RTL 目录不存在: {rtl_dir}) return 1 # 构建 iverilog 命令 iverilog_cmd [iverilog, -o, out.vvp, -I, rtl_dir, -D, OUTPUTsignature.output] # 添加你的testbench 文件 tb_file os.path.join(rtl_dir, Top_tb.v) if not os.path.exists(tb_file): print(f错误: 找不到 testbench 文件: {tb_file}) return 1 iverilog_cmd.append(tb_file) # RTL 文件列表 rtl_files [ ALU.v, IDecoder.v, Regfile.v, cpu.v #...你的rtl文件 ] # 添加所有 RTL 文件 for rtl_file in rtl_files: full_path os.path.join(rtl_dir, rtl_file) if os.path.exists(full_path): iverilog_cmd.append(full_path) else: print(f警告: 文件不存在: {full_path}) # 执行编译 try: print(开始编译...) result subprocess.run(iverilog_cmd, capture_outputTrue, textTrue, timeout30) if result.returncode 0: print(编译成功!) return 0 else: print(编译失败!) print(STDERR:, result.stderr) return result.returncode except subprocess.TimeoutExpired: print(编译超时!) return 1 except Exception as e: print(f编译过程中出错: {e}) return 1 if __name__ __main__: sys.exit(main())在dump文件中找到add.dump文件观察到该指令通过测试的标志为:gp(x3)1a7(x17)930x5da0(x10)0并注意到最新版测试文件中包含了CSR指令因此建议测试前进行相关实现或处理tb文件如下需要修改对应的寄存器和存储器以适配你的设计并注意指令存储器和数据存储器都需要用inst.data初始化inst.data将在后面生成。timescale 1ns / 1ps define TEST_PROG 1 module tb (); reg clk; reg rst_n; //需要修改 wire [31:0] x3 u1.u_Regfile.regs[3]; wire [31:0] x10 u1.u_Regfile.regs[10]; wire [31:0] x17 u1.u_Regfile.regs[17]; integer r; initial begin ifdef TEST_PROG wait (x3 32d1) //测试结束 #100; if (x10 0) begin //通过测试 $display(~~~~~~~~~~~~~~~~~~~ TEST_PASS ~~~~~~~~~~~~~~~~~~~); $display(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~); $display(~~~~~~~~~ ##### ## #### #### ~~~~~~~~~); $display(~~~~~~~~~ # # # # # # ~~~~~~~~~); $display(~~~~~~~~~ # # # # #### #### ~~~~~~~~~); $display(~~~~~~~~~ ##### ###### # #~~~~~~~~~); $display(~~~~~~~~~ # # # # # # #~~~~~~~~~); $display(~~~~~~~~~ # # # #### #### ~~~~~~~~~); $display(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~); end else begin $display(~~~~~~~~~~~~~~~~~~~ TEST_FAIL ~~~~~~~~~~~~~~~~~~~~); $display(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~); $display(~~~~~~~~~~###### ## # # ~~~~~~~~~~); $display(~~~~~~~~~~# # # # # ~~~~~~~~~~); $display(~~~~~~~~~~##### # # # # ~~~~~~~~~~); $display(~~~~~~~~~~# ###### # # ~~~~~~~~~~); $display(~~~~~~~~~~# # # # # ~~~~~~~~~~); $display(~~~~~~~~~~# # # # ######~~~~~~~~~~); $display(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~); $display(fail testnum %2d, x3); for (r 0; r 32; r r 1) $display(x%2d 0x%x, r, u1.u_Regfile.regs[r]); end #100 $finish; endif end initial begin rst_n0; clk0; for (integer i 0; i 8191; i i 1) begin u1.u_data_ram.u_dram.mem_array[i] 32d0; u1.u_IFetch.u_irom.mem_array[i]32d0; end //需要修改成你的存储器对应的reg数组 $readmemh(inst.data, u1.u_data_ram.u_dram.mem_array); $display(Initialize dram with dram.data); //指令存储器和数据存储器的初始化文件相同 $readmemh(inst.data, u1.u_IFetch.u_irom.mem_array); $display(Initialize iram with inst.data); #100 rst_n1; end always #5 clk ~clk; // sim timeout initial begin #5000000 $display(Time Out.); $finish; end //例化你的处理器 riscv_mcu u1 ( .clk (clk), .rst_n (rst_n) ); //生成vcd文件使用gtkwave观察 initial begin $dumpfile(test.vcd); $dumpvars(0, Top_tb); end endmodule运行编译完成后进行仿真工作的准备在MyCPU_tests目录下新增BinToMem.py和sim.py如下BinToMem.py# BinToMem.py import sys import os def bin_to_mem(infile, outfile): try: with open(infile, rb) as binfile: binfile_content binfile.read() with open(outfile, w) as datafile: # 确保字节数是4的倍数不足则补0 remainder len(binfile_content) % 4 if remainder ! 0: binfile_content b\x00 * (4 - remainder) print(f警告: 文件字节数不是4的倍数已填充 {4-remainder} 个零字节) # 按32位字处理 for i in range(0, len(binfile_content), 4): word binfile_content[i:i4] # 转换为大端序反转字节顺序 big_endian_word word[::-1] hex_string big_endian_word.hex() datafile.write(hex_string \n) print(f成功转换: {infile} - {outfile}) print(f生成 {len(binfile_content)//4} 条指令) except FileNotFoundError: print(f错误: 文件 {infile} 不存在) return False except Exception as e: print(f转换过程中出错: {e}) return False return True if __name__ __main__: if len(sys.argv) 3: success bin_to_mem(sys.argv[1], sys.argv[2]) sys.exit(0 if success else 1) else: print(Usage: python BinToMem_CLI.py binfile datafile) sys.exit(1)sim.py:将文件编译集成到了sim.py中因此每次修改.v文件后仿真只需运行sim.py文件。import sys import filecmp import subprocess import sys import os # 主函数 def main(): #print(sys.argv[0] sys.argv[1] sys.argv[2]) # 1.编译rtl文件 cmd rpython compile_rtl.py f os.popen(cmd) f.close() # 2.将bin文件转成mem文件 cmd rpython BinToMem.py sys.argv[1] sys.argv[2] f os.popen(cmd) f.close() # 3.运行 vvp_cmd [rvvp] vvp_cmd.append(rout.vvp) process subprocess.Popen(vvp_cmd) try: process.wait(timeout10) except subprocess.TimeoutExpired: print(!!!Fail, vvp exec timeout!!!) if __name__ __main__: sys.exit(main())对add指令进行仿真$ python sim.py ./bin/rv32ui-p-add.bin inst.data仿真通过。对sh指令进行仿真$ python sim.py ./bin/rv32ui-p-sh.bin inst.data可以用gtkwave查看test.vcd文件可以看到x3寄存器保存了test num的值如果通过测试则x3的最终值为1x10的最终值为0。也可以将rtl文件、tb文件和待测指令的inst.data文件导入modelsim工作目录下使用modelsim进行编译与仿真最后我们可以利用test_all_isa脚本对所有指令进行测试test_all_isa.pyimport os import subprocess import sys import signal # 处理 BrokenPipeError signal.signal(signal.SIGPIPE, signal.SIG_DFL) # 找出path目录下的所有bin文件 def list_binfiles(path): files [] list_dir os.walk(path) for maindir, subdir, all_file in list_dir: for filename in all_file: apath os.path.join(maindir, filename) if apath.endswith(.bin): files.append(apath) return files def run_single_test(file): 运行单个测试文件 try: # 使用 subprocess.run 替代 os.popen cmd [python, sim.py, file, inst.data] result subprocess.run(cmd, capture_outputTrue, textTrue, timeout60) # 检查输出中是否包含 TEST_PASS if TEST_PASS in result.stdout: return True, result.stdout else: return False, result.stdout result.stderr except subprocess.TimeoutExpired: return False, TIMEOUT except Exception as e: return False, fERROR: {str(e)} # 主函数 def main(): bin_files list_binfiles(./bin) if not bin_files: print(错误: 在 ./bin 目录中未找到任何 .bin 文件) return 1 anyfail False passed_count 0 total_count len(bin_files) # 对每一个bin文件进行测试 for i, file in enumerate(bin_files, 1): print(f[{i}/{total_count}] 测试: {os.path.basename(file)}, end, flushTrue) success, output run_single_test(file) if success: print( - PASS) passed_count 1 else: print( - !!!FAIL!!!) print(f 失败原因: {output.split(chr(10))[0] if output else Unknown}) anyfail True # 如果希望一个失败就停止取消下面的注释 # break print( * 60) if not anyfail: print(f恭喜所有测试通过! ({passed_count}/{total_count})) return 0 else: print(f测试完成有测试失败。通过: {passed_count}/{total_count}) return 1 if __name__ __main__: sys.exit(main())运行测试结束。2026.3.3 更新处理器通过RV32IM指令集测试2026.3.12 更新处理器通过RV32IMF指令集测试参考资料tinyriscv: tiny开源riscvDeepseek
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2408205.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!