基于Axi4_lite协议的自定义IP仿真平台的搭建
做FPGA开发离不开仿真,仿真对于FPGA的调试开发起到了很好的辅助作用。对于新手而言仿真就更加重要了,老练的FPGA开发者时序了然于胸,对于简单的IP核开发是可以省略仿真的步骤,但是对于绝大多数的情况而言,仿真不仅能提高工作效率,而且能够提高开发者的时序分析能力。仿真是验证最常用的手段,虽说现在很多FPGA里头集成了逻辑分析,但是直接上板调试还是会损耗不少的时间,一方面是跑综合需要足够的时间,另一方面板上调试的资源有限,很多的错误并不能合理的排查。以此在写完代码后最好是先做仿真,仿真没问题了再上板调试。
正常情况下仿真都会用到仿真工具,我一般用的仿真工具是modelsim,vivado虽说也有自带的仿真,但是使用起来不仅麻烦,而且时序会跟modelsim的有些偏差。目前使用的modelsim版本为ModelSim SE-64 10.5,这是比较专业的仿真软件。对IP核进行仿真,除了以下的这种方式其实还可以使用更高级的仿真,那就是UVM,UVM对IP核的验证会更加专业及全面,不过UVM更像面向对象的语言,使用的是System Verilog,是一门方法学,比较难入门。它自带了完整的流程,IC验证方面用得可能更频繁,FPGA设计的仿真也可以使用这种方式。
下面的仿真平台的搭建就是基于前面所讲的axi4_lite协议,对自定义IP核GPIO进行仿真。仿真代码是用System Verilog语言来完成,学过C或C++的应该能比较容易看懂,会Verilog语言的看SV语言应该难度也不大。
一般封装好的IP会在一个文件夹下:
文件夹下会放doc文件及ip文件,doc用来存放IP核的寄存器说明及操作文档,ip则用来放IP核的相关代码。
ip文件下存放if,rtl,sim文件,if放封装时可能需自定义的接口,rtl放IP核源码,sim放仿真代码。
rtl:
sim:
使用的时候直接双击run.bat文件即可,modelsim自动打开并仿真。
run.bat代码:
1 modelsim -do sim.do
sim.do代码:
1 #删除work工作目录 2 file delete -force work 3 4 #设置uvm环境变量,指定uvm的dpi位置 5 set UVM_DPI_HOME D:/modeltech64_10.5/uvm-1.1d/win64 6 7 #创建work工作目录 8 vlib work 9 10 #vlog表示编译 *.sv表示do文件同级路径下所有.sv文件 -L表示添加库文件 11 vlog -L mtiAvm -L mtiOvm -L mtiUvm -L mtiUPF *.sv 12 13 #编译源文件,包含覆盖率测试 14 vlog -cover sbctf -coveropt 3 ../rtl/*.v 15 16 #执行仿真,打开覆盖率窗口,不使能优化,调用uvm库uvm_dpi,利用UVM_TEST_NAME从命令行中寻找测试用例的名字,创建它的实例并运行 17 vsim -coverage -novopt -c -sv_lib $UVM_DPI_HOME/uvm_dpi work.Gpio_sim +UVM_TESTNAME=case0 18 19 #观察DUT的信号波形 20 add wave -position insertpoint sim:/Gpio_sim/u_Axi4_Gpio/* 21 22 run 10ms
顶层仿真文件Gpio_sim.sv:
1 //************************************************************************** 2 // *** file name : Gpio_sim.sv 3 // *** version : 1.0 4 // *** Description : Gpio IP core testbech 5 // *** Blogs : https://www.cnblogs.com/WenGalois123/ 6 // *** Author : Galois_V 7 // *** Date : 2022.4.7 8 // *** Changes : 9 //************************************************************************** 10 `timescale 1ns/1ps 11 `define GPIO_NUM 32 12 13 module Gpio_sim(); 14 15 /******************************************************************************\ 16 Define AXI4-lite interface port 17 \******************************************************************************/ 18 wire w_axi_aclk ; 19 wire w_axi_aresetn ; 20 21 wire [31:0] w_axi_awaddr ; 22 wire w_axi_awvalid ; 23 wire w_axi_awready ; 24 25 wire [31:0] w_axi_wdata ; 26 wire [3:0] w_axi_wstrb ; 27 wire w_axi_wvalid ; 28 wire w_axi_wready ; 29 30 wire [1:0] w_axi_bresp ; 31 wire w_axi_bvalid ; 32 wire w_axi_bready ; 33 34 wire [31:0] w_axi_araddr ; 35 wire w_axi_arvalid ; 36 wire w_axi_arready ; 37 38 wire [31:0] w_axi_rdata ; 39 wire [1:0] w_axi_rresp ; 40 wire w_axi_rvalid ; 41 wire w_axi_rready ; 42 43 /******************************************************************************\ 44 Instantiate AXI4-lite master module 45 \******************************************************************************/ 46 m_axi4_lite_if u_m_axi4_lite_if 47 ( 48 .o_sys_clk (w_axi_aclk ), 49 .o_sys_rstn (w_axi_aresetn ), 50 .o_m_axi_awaddr (w_axi_awaddr ), 51 .o_m_axi_awvalid (w_axi_awvalid ), 52 .i_m_axi_awready (w_axi_awready ), 53 .o_m_axi_wdata (w_axi_wdata ), 54 .o_m_axi_wstrb (w_axi_wstrb ), 55 .o_m_axi_wvalid (w_axi_wvalid ), 56 .i_m_axi_wready (w_axi_wready ), 57 .i_m_axi_bresp (w_axi_bresp ), 58 .i_m_axi_bvalid (w_axi_bvalid ), 59 .o_m_axi_bready (w_axi_bready ), 60 .o_m_axi_araddr (w_axi_araddr ), 61 .o_m_axi_arvalid (w_axi_arvalid ), 62 .i_m_axi_arready (w_axi_arready ), 63 .i_m_axi_rdata (w_axi_rdata ), 64 .i_m_axi_rresp (w_axi_rresp ), 65 .i_m_axi_rvalid (w_axi_rvalid ), 66 .o_m_axi_rready (w_axi_rready ) 67 ); 68 69 /******************************************************************************\ 70 Instantiate AXI4-lite slave IP core 71 \******************************************************************************/ 72 reg [31:0] r_gpio_i; 73 Axi4_Gpio 74 #( 75 .IO_NUM (`GPIO_NUM) 76 ) 77 u_Axi4_Gpio 78 ( 79 .i_s_axi_aclk (w_axi_aclk ), 80 .i_s_axi_aresetn (w_axi_aresetn ), 81 .i_s_axi_awaddr (w_axi_awaddr ), 82 .i_s_axi_awprot ('d0 ), 83 .i_s_axi_awvalid (w_axi_awvalid ), 84 .o_s_axi_awready (w_axi_awready ), 85 .i_s_axi_wdata (w_axi_wdata ), 86 .i_s_axi_wstrb (w_axi_wstrb ), 87 .i_s_axi_wvalid (w_axi_wvalid ), 88 .o_s_axi_wready (w_axi_wready ), 89 .o_s_axi_bresp (w_axi_bresp ), 90 .o_s_axi_bvalid (w_axi_bvalid ), 91 .i_s_axi_bready (w_axi_bready ), 92 .i_s_axi_araddr (w_axi_araddr ), 93 .i_s_axi_arprot ('d0 ), 94 .i_s_axi_arvalid (w_axi_arvalid ), 95 .o_s_axi_arready (w_axi_arready ), 96 .o_s_axi_rdata (w_axi_rdata ), 97 .o_s_axi_rresp (w_axi_rresp ), 98 .o_s_axi_rvalid (w_axi_rvalid ), 99 .i_s_axi_rready (w_axi_rready ), 100 101 .i_gpio_if_i (r_gpio_i ), 102 .o_gpio_if_o ( ), 103 .o_gpio_if_t ( ) 104 ); 105 106 107 108 /******************************************************************************\ 109 Perform the steps of IP core in sequence 110 \******************************************************************************/ 111 reg [31:0] r_cpu_rd_data; 112 initial r_cpu_rd_data = 0; 113 114 115 initial 116 begin 117 $display("start"); 118 #2000; 119 u_m_axi4_lite_if.arm_write_data(32'h00000000,32'h5aa5a55a,4'hf); 120 #200; 121 u_m_axi4_lite_if.arm_write_data(32'h00000004,32'h5a00005a,4'hf); 122 #200; 123 u_m_axi4_lite_if.arm_read_data(32'h00000000,r_cpu_rd_data); 124 #200; 125 u_m_axi4_lite_if.arm_read_data(32'h00000004,r_cpu_rd_data); 126 #200; 127 r_gpio_i = 32'h1234567; 128 u_m_axi4_lite_if.arm_read_data(32'h00000008,r_cpu_rd_data); 129 $display("sim complete"); 130 end 131 132 endmodule
产生激励模块m_axi4_lite_if.sv:
1 //************************************************************************** 2 // *** file name : m_axi4_lite_if.sv 3 // *** version : 1.0 4 // *** Description : axi4_lite_if master 5 // *** Blogs : https://www.cnblogs.com/WenGalois123/ 6 // *** Author : Galois_V 7 // *** Date : 2022.4.7 8 // *** Changes : 9 //************************************************************************** 10 `timescale 1ns/1ps 11 12 module m_axi4_lite_if 13 ( 14 output reg o_sys_clk, 15 output reg o_sys_rstn, 16 17 output reg [31:0] o_m_axi_awaddr, 18 output reg o_m_axi_awvalid, 19 input i_m_axi_awready, 20 21 output reg [31:0] o_m_axi_wdata, 22 output reg [3:0] o_m_axi_wstrb, 23 output reg o_m_axi_wvalid, 24 input i_m_axi_wready, 25 26 input [1:0] i_m_axi_bresp, 27 input i_m_axi_bvalid, 28 output reg o_m_axi_bready, 29 30 output reg [31:0] o_m_axi_araddr, 31 output reg o_m_axi_arvalid, 32 input i_m_axi_arready, 33 34 input [31:0] i_m_axi_rdata, 35 input [1:0] i_m_axi_rresp, 36 input i_m_axi_rvalid, 37 output reg o_m_axi_rready 38 ); 39 40 parameter SYS_CLK = 100_000_000 , 41 TIME_1S = 1000_000_000 ; 42 43 44 real clk_cnt = TIME_1S/SYS_CLK; 45 /******************************************************************************\ 46 产生仿真环境需要的时钟与复位信号,这里的时钟信号为100MHz 47 \******************************************************************************/ 48 initial//所有的initial模块都是并行的,initial模块的语句是按顺序执行的。 49 begin 50 o_sys_clk = 0; 51 o_sys_rstn = 0; 52 #1000; 53 o_sys_rstn = 1; 54 end 55 56 always #(clk_cnt/2) o_sys_clk = ~o_sys_clk; 57 58 /******************************************************************************\ 59 先初始化axi4-lite的输出量信号 60 \******************************************************************************/ 61 initial 62 begin 63 o_m_axi_awaddr = 'd0; 64 o_m_axi_awvalid = 'd0; 65 o_m_axi_wdata = 'd0; 66 o_m_axi_wstrb = 'd0; 67 o_m_axi_wvalid = 'd0; 68 o_m_axi_bready = 'd0; 69 o_m_axi_araddr = 'd0; 70 o_m_axi_arvalid = 'd0; 71 o_m_axi_rready = 'd0; 72 end 73 /******************************************************************************\ 74 通过axi4-lite总线写数据,写task 75 \******************************************************************************/ 76 task arm_write_data 77 ( 78 input [31:0] i_addr , 79 input [31:0] i_data , 80 input [3:0] i_byte_en 81 ); 82 begin 83 @(posedge o_sys_clk) 84 begin 85 o_m_axi_awaddr = i_addr; 86 o_m_axi_awvalid = 1'b1; 87 o_m_axi_wdata = i_data; 88 o_m_axi_wstrb = i_byte_en; 89 o_m_axi_wvalid = 1'b1; 90 o_m_axi_bready = 1'b1; 91 end 92 @(posedge o_sys_clk); 93 begin 94 while(~(i_m_axi_awready & i_m_axi_wready)) // 95 begin 96 @(posedge o_sys_clk) 97 begin 98 o_m_axi_awvalid = 1'b0; 99 o_m_axi_wvalid = 1'b0; 100 end 101 end 102 end 103 104 @(posedge o_sys_clk) 105 begin 106 while(~i_m_axi_bvalid) 107 begin 108 @(posedge o_sys_clk) 109 begin 110 if(i_m_axi_bresp != 2'b00) 111 begin 112 $display("write fail"); 113 end 114 end 115 end 116 end 117 118 o_m_axi_bready = 1'b0; 119 end 120 endtask 121 122 123 /******************************************************************************\ 124 通过axi4-lite总线读数据,读task 125 \******************************************************************************/ 126 task arm_read_data 127 ( 128 input [31:0] i_addr, 129 output [31:0] o_rdata 130 ); 131 begin 132 @(posedge o_sys_clk) 133 begin 134 o_m_axi_araddr = i_addr; 135 o_m_axi_arvalid = 1'b1; 136 o_m_axi_rready = 1'b1; 137 end 138 139 @(posedge o_sys_clk) 140 begin 141 while(~i_m_axi_arready) 142 begin 143 @(posedge o_sys_clk) 144 begin 145 o_m_axi_arvalid = 1'b0; 146 end 147 end 148 end 149 @(posedge o_sys_clk) 150 begin 151 o_rdata = i_m_axi_rdata; 152 while(~i_m_axi_rvalid) 153 begin 154 if(i_m_axi_rresp!=2'b00) 155 $display("read fail"); 156 end 157 end 158 159 o_m_axi_rready = 1'b0; 160 end 161 endtask 162 163 endmodule
以上便是整个axi4_lite 的仿真平台搭建,可以根据实际需要增加新的接口功能,比如axi4_stream接口这些等等。
仿真的结果如下图: