FPGA应用技巧之AXI4-FULL-uiFDMA IP仿真验证

文章目录

  • 概述
  • [1 axi-full-slave IP](#1 axi-full-slave IP)
    • [1.1 IP 概述](#1.1 IP 概述)
    • [1.2 创建 axi4-full-slave 总线接口 IP](#1.2 创建 axi4-full-slave 总线接口 IP)
    • [1.3 代码分析](#1.3 代码分析)
      • [1.3.1 axi-full-slave 的 axi_awready](#1.3.1 axi-full-slave 的 axi_awready)
      • [1.3.2 axi-full-slave 的 axi_awaddr](#1.3.2 axi-full-slave 的 axi_awaddr)
      • [1.3.3 axi-full-slave 的 axi_wready](#1.3.3 axi-full-slave 的 axi_wready)
      • [1.3.4 axi-full-slave 的 axi_bvalid 信号](#1.3.4 axi-full-slave 的 axi_bvalid 信号)
      • [1.3.5 axi-full-slave 的 axi_arready 信号](#1.3.5 axi-full-slave 的 axi_arready 信号)
      • [1.3.6 axi-full-slave 的 axi_araddr 信号](#1.3.6 axi-full-slave 的 axi_araddr 信号)
      • [1.3.7 axi-full-slave 的 axi_rvalid 信号](#1.3.7 axi-full-slave 的 axi_rvalid 信号)
      • [1.3.8 数据保存到 block ram](#1.3.8 数据保存到 block ram)
    • [1.4 仿真结果](#1.4 仿真结果)
  • [2 saxi_full_mem IP 介绍](#2 saxi_full_mem IP 介绍)
  • [3 创建 FPGA 逻辑工程](#3 创建 FPGA 逻辑工程)
  • [4 添加 FDMA 接口控制代码](#4 添加 FDMA 接口控制代码)
  • [5 仿真文件](#5 仿真文件)
  • [6 实验结果](#6 实验结果)

概述

本文试验中对上一篇: FPGA应用技巧之AXI4-FULL-MASTER IP FDMA(Fast Direct Memory)详细介绍描写的 FDMA IP 进行仿真验证。

1 axi-full-slave IP

这里为什么要先给大家梳理axi-full-slave IP的相关内容呢,是因为关于AXI4-FULL-uiFDMA IP的应用其实都是基于VIVADO 工具产生 AXI 协议模板进行拓展的。因此这一部分主要目的在于:

1:掌握基于 VIVADO 工具产生 AXI 协议模板

2:掌握通过 VIVADO 工具产生 AXI-full-slave 代码

3:理解 AXI-full-slave 中自定义寄存器的地址分配

4:掌握通过 VIVADO 封装 AXI-full-slave 图形化 IP

1.1 IP 概述

使用 XILINX 的软件工具 VIVADO 以及 XILINX 的 7 代以上的 FPGA 或者 SOC 掌握 AXI-4 总线结束, 并且可以灵活使用 AXI-4 总线技术完成数据的交换,可以让我们在构建强大的 FPGA 内部总线数据互联通信方面取得高效、高速、 标准化的优势。

1.2 创建 axi4-full-slave 总线接口 IP

新建工程我们不再赘述。

新建完成工程后, 单击菜单栏 Tools->Create and Package New IP, 开始创建一个 AXI4-Full 接口总线 IP

选择使用 vivado 自带的 AXI 总线模板创建一个 AXI4-FULL 接口 IP。

设置 IP 的名字为 saxi_full。

模板支持 3 种协议, 分别是 AXI4-Full , AXI4-Lite , AXI4-Stream, 这里选择 full;总线包括 Master 和 Slave 两种模式, 这里选择 Slave 模式。

这里选择 Verify Peripheral IP using AXI4 VIP 可以对 AXI4-FULL 快速验证。

单击 Finish 后展开 VIVADO 自动产生的 demo, 单击 Block Design 的工程, 可以看到如下 2 个 IP。 其中 saxi_full_0 就是我们自定义的 IP, 另外一个 master_0 是用来读写我们自定义的 saxi_full_0, 以此验证我们的 IP 正确性。

采用默认地址分配即可。

继续再看代码看看里面有什么东西。

文件夹saxi_full_1.0/hdl 路径下的 saxi_full_v1_0_S00_AXI.v 就是我们的源码。 另外一个 saxi_full_v1_0.v是软件产生了一个接口文件, 如果我们自己定义 IP 可有可无。

1.3 代码分析

1.3.1 axi-full-slave 的 axi_awready

当满足条件(~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag &&~axi_arv_arr_flag)=1 的时候表示可以进行一次 AXI-FULL 的 burst 写操作了, 这个时候 AXI-FULL-SLAVE 设置 axi_awready <= 1'b1 和 axi_awv_awr_flag<= 1'b1。

c 复制代码
	// Implement axi_awready generation

	// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
	// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
	// de-asserted when reset is low.

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_awready <= 1'b0;
	      axi_awv_awr_flag <= 1'b0;
	    end 
	  else
	    begin    
	      if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
	        begin
	          // slave is ready to accept an address and
	          // associated control signals
	          axi_awready <= 1'b1;
	          axi_awv_awr_flag  <= 1'b1; 
	          // used for generation of bresp() and bvalid
	        end
	      else if (S_AXI_WLAST && axi_wready)          
	      // preparing to accept next address after current write burst tx completion
	        begin
	          axi_awv_awr_flag  <= 1'b0;
	        end
	      else        
	        begin
	          axi_awready <= 1'b0;
	        end
	    end 
	end 

1.3.2 axi-full-slave 的 axi_awaddr

AXI 的 burst 模式包括 3 种:

1:fixed burst 这种模式下地址都是相同的

2: incremental burst 这种模式下地址递增

3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。

本文 demo 中以下三种模式的具体代码如下:

c 复制代码
	// Implement axi_awaddr latching

	// This process is used to latch the address when both 
	// S_AXI_AWVALID and S_AXI_WVALID are valid. 

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_awaddr <= 0;
	      axi_awlen_cntr <= 0;
	      axi_awburst <= 0;
	      axi_awlen <= 0;
	    end 
	  else
	    begin    
	      if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)
	        begin
	          // address latching 
	          axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0];  
	           axi_awburst <= S_AXI_AWBURST; 
	           axi_awlen <= S_AXI_AWLEN;     
	          // start address of transfer
	          axi_awlen_cntr <= 0;
	        end   
	      else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)        
	        begin

	          axi_awlen_cntr <= axi_awlen_cntr + 1;

	          case (axi_awburst)
	            2'b00: // fixed burst
	            // The write address for all the beats in the transaction are fixed
	              begin
	                axi_awaddr <= axi_awaddr;          
	                //for awsize = 4 bytes (010)
	              end   
	            2'b01: //incremental burst
	            // The write address for all the beats in the transaction are increments by awsize
	              begin
	                axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
	                //awaddr aligned to 4 byte boundary
	                axi_awaddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
	                //for awsize = 4 bytes (010)
	              end   
	            2'b10: //Wrapping burst
	            // The write address wraps when the address reaches wrap boundary 
	              if (aw_wrap_en)
	                begin
	                  axi_awaddr <= (axi_awaddr - aw_wrap_size); 
	                end
	              else 
	                begin
	                  axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
	                  axi_awaddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}}; 
	                end                      
	            default: //reserved (incremental burst for example)
	              begin
	                axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
	                //for awsize = 4 bytes (010)
	              end
	          endcase              
	        end
	    end 
	end       

1.3.3 axi-full-slave 的 axi_wready

当满足条件( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)==1 设置 axi_wready 为 1.这里可以看出,S_AXI_WVALID 必须在一次 burst 中持续有效,直到满足条件 (S_AXI_WLAST && axi_wready) ,否则AXI-FULL-SLAVE会出错, 这一点有别于 AXI-LITE-SLAVE 每次只读写一个数据。

c 复制代码
	// Implement axi_wready generation

	// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
	// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is 
	// de-asserted when reset is low. 

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_wready <= 1'b0;
	    end 
	  else
	    begin    
	      if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
	        begin
	          // slave can accept the write data
	          axi_wready <= 1'b1;
	        end
	      //else if (~axi_awv_awr_flag)
	      else if (S_AXI_WLAST && axi_wready)
	        begin
	          axi_wready <= 1'b0;
	        end
	    end 
	end       

1.3.4 axi-full-slave 的 axi_bvalid 信号

axi_bvalid 用于告知 axi master axi-slave 端已经完成数据接收了。

给出 ACK, 写操作 LAST 信号的下一个时钟, AXI-SLAVE 给出 ACK 信号。

c 复制代码
	// Implement write response logic generation

	// The write response and response valid signals are asserted by the slave 
	// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.  
	// This marks the acceptance of address and indicates the status of 
	// write transaction.

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_bvalid <= 0;
	      axi_bresp <= 2'b0;
	      axi_buser <= 0;
	    end 
	  else
	    begin    
	      if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
	        begin
	          axi_bvalid <= 1'b1;
	          axi_bresp  <= 2'b0; 
	          // 'OKAY' response 
	        end                   
	      else
	        begin
	          if (S_AXI_BREADY && axi_bvalid) 
	          //check if bready is asserted while bvalid is high) 
	          //(there is a possibility that bready is always asserted high)   
	            begin
	              axi_bvalid <= 1'b0; 
	            end  
	        end
	    end
	 end   

1.3.5 axi-full-slave 的 axi_arready 信号

当满足条件(~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag &&~axi_arv_arr_flag)=1 的时候表示可以进行一次 AXI-FULL 的 burst 读操作了, 这个时候 AXI -FULL-SLAVE 设置 axi_arready <= 1'b1 和 axi_arv_arr_flag <=1'b1。

c 复制代码
	// Implement axi_arready generation

	// axi_arready is asserted for one S_AXI_ACLK clock cycle when
	// S_AXI_ARVALID is asserted. axi_awready is 
	// de-asserted when reset (active low) is asserted. 
	// The read address is also latched when S_AXI_ARVALID is 
	// asserted. axi_araddr is reset to zero on reset assertion.

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_arready <= 1'b0;
	      axi_arv_arr_flag <= 1'b0;
	    end 
	  else
	    begin    
	      if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
	        begin
	          axi_arready <= 1'b1;
	          axi_arv_arr_flag <= 1'b1;
	        end
	      else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
	      // preparing to accept next address after current read completion
	        begin
	          axi_arv_arr_flag  <= 1'b0;
	        end
	      else        
	        begin
	          axi_arready <= 1'b0;
	        end
	    end 
	end 

1.3.6 axi-full-slave 的 axi_araddr 信号

AXI-的读写操作几乎是相对的代码, AXI 的 burst 模式包括 3 种:

1:fixed burst 这种模式下地址都是相同的

2: incremental burst 这种模式下地址递增

3: Wrapping burst 这只模式下地址达到设置的最大地址边界后返回原来的地址。

本文 demo 种以下三种模式的具体代码如下:

c 复制代码
	// Implement axi_araddr latching

	//This process is used to latch the address when both 
	//S_AXI_ARVALID and S_AXI_RVALID are valid. 
	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_araddr <= 0;
	      axi_arlen_cntr <= 0;
	      axi_arburst <= 0;
	      axi_arlen <= 0;
	      axi_rlast <= 1'b0;
	      axi_ruser <= 0;
	    end 
	  else
	    begin    
	      if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
	        begin
	          // address latching 
	          axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0]; 
	          axi_arburst <= S_AXI_ARBURST; 
	          axi_arlen <= S_AXI_ARLEN;     
	          // start address of transfer
	          axi_arlen_cntr <= 0;
	          axi_rlast <= 1'b0;
	        end   
	      else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)        
	        begin
	         
	          axi_arlen_cntr <= axi_arlen_cntr + 1;
	          axi_rlast <= 1'b0;
	        
	          case (axi_arburst)
	            2'b00: // fixed burst
	             // The read address for all the beats in the transaction are fixed
	              begin
	                axi_araddr       <= axi_araddr;        
	                //for arsize = 4 bytes (010)
	              end   
	            2'b01: //incremental burst
	            // The read address for all the beats in the transaction are increments by awsize
	              begin
	                axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1; 
	                //araddr aligned to 4 byte boundary
	                axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
	                //for awsize = 4 bytes (010)
	              end   
	            2'b10: //Wrapping burst
	            // The read address wraps when the address reaches wrap boundary 
	              if (ar_wrap_en) 
	                begin
	                  axi_araddr <= (axi_araddr - ar_wrap_size); 
	                end
	              else 
	                begin
	                axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1; 
	                //araddr aligned to 4 byte boundary
	                axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
	                end                      
	            default: //reserved (incremental burst for example)
	              begin
	                axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
	                //for arsize = 4 bytes (010)
	              end
	          endcase              
	        end
	      else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )   
	        begin
	          axi_rlast <= 1'b1;
	        end          
	      else if (S_AXI_RREADY)   
	        begin
	          axi_rlast <= 1'b0;
	        end          
	    end 
	end

1.3.7 axi-full-slave 的 axi_rvalid 信号

在用 VIVADO 模板产生的 demo 中, 读操作数据不是连续读的, 通过 axi_rvalid 设置 AXI-SLAVE FULL 读数据有效。

c 复制代码
	// Implement axi_arvalid generation

	// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both 
	// S_AXI_ARVALID and axi_arready are asserted. The slave registers 
	// data are available on the axi_rdata bus at this instance. The 
	// assertion of axi_rvalid marks the validity of read data on the 
	// bus and axi_rresp indicates the status of read transaction.axi_rvalid 
	// is deasserted on reset (active low). axi_rresp and axi_rdata are 
	// cleared to zero on reset (active low).  

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_rvalid <= 0;
	      axi_rresp  <= 0;
	    end 
	  else
	    begin    
	      if (axi_arv_arr_flag && ~axi_rvalid)
	        begin
	          axi_rvalid <= 1'b1;
	          axi_rresp  <= 2'b0; 
	          // 'OKAY' response
	        end   
	      else if (axi_rvalid && S_AXI_RREADY)
	        begin
	          axi_rvalid <= 1'b0;
	        end            
	    end
	end
	
	//Output register or memory read data

	always @( mem_data_out, axi_rvalid)
	begin
	  if (axi_rvalid) 
	    begin
	      // Read address mux
	      axi_rdata <= mem_data_out[0];
	    end   
	  else
	    begin
	      axi_rdata <= 32'h00000000;
	    end       
	end   

1.3.8 数据保存到 block ram

以下是利用 block ram 完成数据的保存和回读

c 复制代码
	// implement Block RAM(s)
	generate 
	  for(i=0; i<= USER_NUM_MEM-1; i=i+1)
	    begin:BRAM_GEN
	      wire mem_rden;
	      wire mem_wren;
	
	      assign mem_wren = axi_wready && S_AXI_WVALID ;
	
	      assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid
	     
	      for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)
	      begin:BYTE_BRAM_GEN
	        wire [8-1:0] data_in ;
	        wire [8-1:0] data_out;
	        reg  [8-1:0] byte_ram [0 : 15];
	        integer  j;
	     
	        //assigning 8 bit data
	        assign data_in  = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];
	        assign data_out = byte_ram[mem_address];
	     
	        always @( posedge S_AXI_ACLK )
	        begin
	          if (mem_wren && S_AXI_WSTRB[mem_byte_index])
	            begin
	              byte_ram[mem_address] <= data_in;
	            end   
	        end    
	      
	        always @( posedge S_AXI_ACLK )
	        begin
	          if (mem_rden)
	            begin
	              mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;
	            end   
	        end    
	               
	    end
	  end       
	endgenerate

1.4 仿真结果

2 saxi_full_mem IP 介绍

这个 IP 的源码可以基于 axi-full-slave 的模板简单修改就可以实现。 找到以下路径, 中 saxi_full_v1_0_S00_AXI.v文件, 并且对齐修改。

我们把修改后的代码命名为 saxi_full_mem.v 修改其中的部分代码, 关键部分是 memory 部分定义。

c 复制代码
	// ------------------------------------------
	// -- Example code to access user logic memory region
	// ------------------------------------------
	localparam MEM_LEN		= clogb2(USER_NUM_MEM)-1;
	     
	reg  [C_S_AXI_DATA_WIDTH-1:0] byte_ram [0 : USER_NUM_MEM -1];
	     
	assign axi_rdata = axi_rvalid ? byte_ram[axi_araddr[ADDR_LSB+MEM_LEN:ADDR_LSB]] : 0;
	     
	always @( posedge S_AXI_ACLK )begin
	    if (axi_wready && S_AXI_WVALID)begin
	            byte_ram[axi_awaddr[ADDR_LSB+MEM_LEN:ADDR_LSB]] <= S_AXI_WDATA;
	    end   
	end

修改的这部分代码支持 Memory 的任意长度设置(FPGA 内部 RAM 会消耗资源), 其中参数 USER_NUM_MEM用于定义 RAM 的长度, 我们一次 FDMA 的 burst 长度应该小于等于 USER_NUM_MEM 这个参数。

这个 IP 后面可以用于 AXI4 总线的仿真验证。

3 创建 FPGA 逻辑工程

设置 IP 路径。

添加已经创建好的 IP。

输入关键词 fdma, 在最后可以看到, 双击添加 Ip。

继续添加剩余 IP。

设置 IP 参数。

完成连线。

设置地址分配:

4 添加 FDMA 接口控制代码

添加完成后如下图:

fdma_axi_slave_test.v源码如下:

c 复制代码
`timescale 1ns / 1ps 
module fdma_axi_slave_test(
  input I_sysclk
);

wire [31:0]   fdma_raddr;
reg           fdma_rareq;
wire          fdma_rbusy;
wire [31:0]   fdma_rdata;
wire [15:0]   fdma_rsize;
wire          fdma_rvalid;
wire [31:0]   fdma_waddr;
reg           fdma_wareq;
wire          fdma_wbusy;
wire [31:0]   fdma_wdata;
wire [15:0]   fdma_wsize;
wire          fdma_wvalid;
wire          ui_clk;

parameter TEST_MEM_SIZE   = 32'd20000;
parameter FDMA_BURST_LEN  = 16'd500;
parameter ADDR_MEM_OFFSET = 0; 
parameter ADDR_INC = FDMA_BURST_LEN*4;
 
parameter WRITE1 = 0;
parameter WRITE2 = 1;
parameter WAIT   = 2;
parameter READ1  = 3;
parameter READ2  = 4;

reg [31: 0] t_data;
reg [31: 0] fdma_waddr_r;
reg [2  :0] T_S = 0;

assign fdma_waddr = fdma_waddr_r + ADDR_MEM_OFFSET;
assign fdma_raddr = fdma_waddr;

assign fdma_wsize = FDMA_BURST_LEN;
assign fdma_rsize = FDMA_BURST_LEN;
assign fdma_wdata ={t_data,t_data,t_data,t_data}; 
  
  
//delay reset
reg [8:0] rst_cnt = 0;
always @(posedge ui_clk)
    if(rst_cnt[8] == 1'b0)
         rst_cnt <= rst_cnt + 1'b1;
     else 
         rst_cnt <= rst_cnt;

always @(posedge ui_clk)begin
    if(rst_cnt[8] == 1'b0)begin
        T_S <=0;   
        fdma_wareq  <= 1'b0; 
        fdma_rareq  <= 1'b0; 
        t_data<=0;
        fdma_waddr_r <=0;       
    end 
    else begin
        case(T_S)      
        WRITE1:begin
            if(fdma_waddr_r==TEST_MEM_SIZE) fdma_waddr_r<=0; 
                if(!fdma_wbusy)begin
                    fdma_wareq  <= 1'b1; 
                    t_data  <= 0;
                end
                if(fdma_wareq&&fdma_wbusy)begin
                    fdma_wareq  <= 1'b0; 
                    T_S         <= WRITE2;
                end
        end
        WRITE2:begin
            if(!fdma_wbusy) begin
                 T_S <= WAIT;
                 t_data  <= 32'd0;
            end 
            else if(fdma_wvalid) begin
                t_data <= t_data + 1'b1;
            end
        end
        WAIT:begin//not needed
            T_S <= READ1;
        end
        READ1:begin
            if(!fdma_rbusy)begin
                fdma_rareq  <= 1'b1; 
                t_data   <= 0;
            end
            if(fdma_rareq&&fdma_rbusy)begin
                 fdma_rareq  <= 1'b0; 
                 T_S         <= READ2;
            end 
        end
        READ2:begin
            if(!fdma_rbusy) begin
                 T_S <= WRITE1;
                 t_data  <= 32'd0;
                 fdma_waddr_r  <= fdma_waddr_r + ADDR_INC;//128/8=16
            end 
            else if(fdma_rvalid) begin
                t_data <= t_data + 1'b1;
            end
        end   
        default:
            T_S <= WRITE1;     
        endcase
    end
  end
  
wire test_error = (fdma_rvalid && (t_data[15:0] != fdma_rdata[15:0]));

//ila_0 ila_dbg (
//	.clk(ui_clk), 
//	.probe0({fdma_wdata[15:0],fdma_wareq,fdma_wvalid,fdma_wbusy}),
//	.probe1({fdma_rdata[15:0],t_data[15:0],fdma_rvalid,fdma_rbusy,T_S,test_error})
//);

  system system_i
       (.FDMA_S_0_fdma_raddr(fdma_raddr),
        .FDMA_S_0_fdma_rareq(fdma_rareq),
        .FDMA_S_0_fdma_rbusy(fdma_rbusy),
        .FDMA_S_0_fdma_rdata(fdma_rdata),
        .FDMA_S_0_fdma_rready(1'b1),
        .FDMA_S_0_fdma_rsize(fdma_rsize),
        .FDMA_S_0_fdma_rvalid(fdma_rvalid),
        .FDMA_S_0_fdma_waddr(fdma_waddr),
        .FDMA_S_0_fdma_wareq(fdma_wareq),
        .FDMA_S_0_fdma_wbusy(fdma_wbusy),
        .FDMA_S_0_fdma_wdata(fdma_wdata),
        .FDMA_S_0_fdma_wready(1'b1),
        .FDMA_S_0_fdma_wsize(fdma_wsize),
        .FDMA_S_0_fdma_wvalid(fdma_wvalid),
        .sysclk(sysclk),
        .ui_clk(ui_clk)
        );        

endmodule

以上代码中调用的 system.bd 的图形代码接口。 在状态机中, 每次写 500 个长度 32bit 的数据, 再读出来判断数据是否正确,因此传输 20000 字节的数据需要传输 10 次,每次FDMA 传输的地址递增 2000。

5 仿真文件

添加仿真文件。

添加完成后:

仿真文件非常简单, 只要提供时钟激励就可以。

c 复制代码
module fdma_axi_slave_test_tb();

 reg I_sysclk;

 fdma_axi_slave_test fdma_axi_slave_test_inst
(
    .I_sysclk(I_sysclk)

 );
 
initial begin
     I_sysclk  = 0;
     #100;
end
    always #10 I_sysclk = ~I_sysclk;   

endmodule

6 实验结果

下图中红色框内分别代表了 FDMA 一次 burst 的写操作和一次 burst 的读操作。 FDMA 控制器会根据用户代码设置的 fdma_wsize 和 fdma_rsize 以及设置的 FDMA IP 参数中, AXI4 总线支持的最大 burst 长度, 来决定进行多少次的 AXI4 burst。

本次写传输中, wburst_len_req 自动发起计算 AXI4 总线需要发起的传输长度, 本次传输中, FDMA 的用户代码每次发起传输长度为 500, 因此 FDMA 部分会自动控制 AXI 部分的传输长度, 第一次 axi burst 长度为 256, 第二次 axi burst 长度为 244。

一次 FDMA 写传输的起始时序。

连续 burst,自动管理 burst 长度, 以及一次 FDMA 写传输结束时序。

本次读传输中, rburst_len_req 自动发起计算 AXI4 总线需要发起的传输长度, 本次传输中, FDMA 的用户代码每次发起传输长度为 500, 因此 FDMA 部分会自动控制 AXI 部分的传输长度, 第一次 axi burst 长度为 256, 第二次axi burst 长度为 244。

一次 FDMA 读传输的起始时序。

连续 burst,自动管理 burst 长度, 以及一次 FDMA 读传输结束时序。

另外放大后可以看到 rvalid 不是连续的, 这个读者也可以自己去优化 saxi_ful_mem ip 让这 IP 支持连续的 rvalid。


不要忘记关注我,点赞收藏,我会为你带来更多优质内容!

相关推荐
FLJwu1 小时前
硬件器件选型实战电源篇 03:TVS 管选型踩坑复盘|盲目选大电压,静电打坏运动相机主控,客户批量退货
经验分享·数码相机·音视频·相机·运动相机
wukong60261 小时前
新榜智汇GEO与即推GEO在功能价格效果上有何区别
经验分享
上海广测检测科技有限公司12 小时前
消费类智能联网设备安全分级国标征求意见要点解析:三级五维框架与跨境对标逻辑
网络·经验分享
hexiaoyan82713 小时前
高速图像采集卡处理板卡设计原理图:10-基于TMS320C6678+XC7K325T的6U CPCI Full Camera Link图像处理平台
fpga开发·高速图像采集·图像采集卡·c6678板卡
富唯智能机器人14 小时前
晶振弹匣上下料,复合机器人如何实现柔性自动化|富唯智能
经验分享
szxinmai主板定制专家14 小时前
储能EMS + 协调控制器实战:RK3568/3576/3588+FPGA+AI 故障录波与诊断
人工智能·深度学习·机器学习·fpga开发·rk3588·rk3576·协调控制器
小HANN16 小时前
华为云企业网站上云实战|从零搭建高可用WordPress全站架构(ECS+RDS+ELB+弹性伸缩+云监控闭环)
linux·运维·服务器·经验分享
GoppViper17 小时前
用户反馈驱动的智能搜索关键词动态优化系统
经验分享
星夜离尘5617 小时前
FPGA 定点数缩位:截断、舍入与饱和,避免溢出回绕
fpga开发·定点数