详解并掌握AXI4总线协议(四)、AXI4_FULL_SLAVE接口源码分析以及仿真验证

系列文章目录

详解并掌握AXI4总线协议(一)、AXI4-FULL接口介绍
详解并掌握AXI4总线协议(二)、AXI4_FULL_MASTER接口源码分析以及仿真验证
详解并掌握AXI4总线协议(三)、基于AXI4_FULL接口的BRAM读写仿真验证


一、前言

  在上前面几篇文章中,我们了解了AXI4协议的架构、传输机制以及各个信号的功能,以及AXI4_FULL_MASTERd在本文中,我们来研究Xilinx中AXI4_FULL_master接口是怎么实现、以及通过仿真来验证并且加深我们对AXI4协议的理解。

二、生成axi4_full_slave接口模板

  首先在Tools栏点击创建和打包新的IP

在这里插入图片描述
  然后点击创建AXI4接口

在这里插入图片描述
  然后自己命名
在这里插入图片描述

  然后选择FULL接口,Slave模式

在这里插入图片描述

  最后选择vip快速验证

在这里插入图片描述

三、分析axi4_full_slave接口代码

  我们先打开代码,然后逐步分析代码,整个代码如下:


`timescale 1 ns / 1 ps

	module axi4_full_slave_v1_0_S_AXI #
	(
		// Users to add parameters here

		// User parameters ends
		// Do not modify the parameters beyond this line

		// Width of ID for for write address, write data, read address and read data
		parameter integer C_S_AXI_ID_WIDTH	= 1,
		// Width of S_AXI data bus
		parameter integer C_S_AXI_DATA_WIDTH	= 32,
		// Width of S_AXI address bus
		parameter integer C_S_AXI_ADDR_WIDTH	= 6,
		// Width of optional user defined signal in write address channel
		parameter integer C_S_AXI_AWUSER_WIDTH	= 0,
		// Width of optional user defined signal in read address channel
		parameter integer C_S_AXI_ARUSER_WIDTH	= 0,
		// Width of optional user defined signal in write data channel
		parameter integer C_S_AXI_WUSER_WIDTH	= 0,
		// Width of optional user defined signal in read data channel
		parameter integer C_S_AXI_RUSER_WIDTH	= 0,
		// Width of optional user defined signal in write response channel
		parameter integer C_S_AXI_BUSER_WIDTH	= 0
	)
	(
		// Users to add ports here

		// User ports ends
		// Do not modify the ports beyond this line

		// Global Clock Signal
		input wire  S_AXI_ACLK,
		// Global Reset Signal. This Signal is Active LOW
		input wire  S_AXI_ARESETN,
		// Write Address ID
		input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
		// Write address
		input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
		// Burst length. The burst length gives the exact number of transfers in a burst
		input wire [7 : 0] S_AXI_AWLEN,
		// Burst size. This signal indicates the size of each transfer in the burst
		input wire [2 : 0] S_AXI_AWSIZE,
		// Burst type. The burst type and the size information, 
    // determine how the address for each transfer within the burst is calculated.
		input wire [1 : 0] S_AXI_AWBURST,
		// Lock type. Provides additional information about the
    // atomic characteristics of the transfer.
		input wire  S_AXI_AWLOCK,
		// Memory type. This signal indicates how transactions
    // are required to progress through a system.
		input wire [3 : 0] S_AXI_AWCACHE,
		// Protection type. This signal indicates the privilege
    // and security level of the transaction, and whether
    // the transaction is a data access or an instruction access.
		input wire [2 : 0] S_AXI_AWPROT,
		// Quality of Service, QoS identifier sent for each
    // write transaction.
		input wire [3 : 0] S_AXI_AWQOS,
		// Region identifier. Permits a single physical interface
    // on a slave to be used for multiple logical interfaces.
		input wire [3 : 0] S_AXI_AWREGION,
		// Optional User-defined signal in the write address channel.
		input wire [C_S_AXI_AWUSER_WIDTH-1 : 0] S_AXI_AWUSER,
		// Write address valid. This signal indicates that
    // the channel is signaling valid write address and
    // control information.
		input wire  S_AXI_AWVALID,
		// Write address ready. This signal indicates that
    // the slave is ready to accept an address and associated
    // control signals.
		output wire  S_AXI_AWREADY,
		// Write Data
		input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
		// Write strobes. This signal indicates which byte
    // lanes hold valid data. There is one write strobe
    // bit for each eight bits of the write data bus.
		input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
		// Write last. This signal indicates the last transfer
    // in a write burst.
		input wire  S_AXI_WLAST,
		// Optional User-defined signal in the write data channel.
		input wire [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER,
		// Write valid. This signal indicates that valid write
    // data and strobes are available.
		input wire  S_AXI_WVALID,
		// Write ready. This signal indicates that the slave
    // can accept the write data.
		output wire  S_AXI_WREADY,
		// Response ID tag. This signal is the ID tag of the
    // write response.
		output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
		// Write response. This signal indicates the status
    // of the write transaction.
		output wire [1 : 0] S_AXI_BRESP,
		// Optional User-defined signal in the write response channel.
		output wire [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER,
		// Write response valid. This signal indicates that the
    // channel is signaling a valid write response.
		output wire  S_AXI_BVALID,
		// Response ready. This signal indicates that the master
    // can accept a write response.
		input wire  S_AXI_BREADY,
		// Read address ID. This signal is the identification
    // tag for the read address group of signals.
		input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
		// Read address. This signal indicates the initial
    // address of a read burst transaction.
		input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
		// Burst length. The burst length gives the exact number of transfers in a burst
		input wire [7 : 0] S_AXI_ARLEN,
		// Burst size. This signal indicates the size of each transfer in the burst
		input wire [2 : 0] S_AXI_ARSIZE,
		// Burst type. The burst type and the size information, 
    // determine how the address for each transfer within the burst is calculated.
		input wire [1 : 0] S_AXI_ARBURST,
		// Lock type. Provides additional information about the
    // atomic characteristics of the transfer.
		input wire  S_AXI_ARLOCK,
		// Memory type. This signal indicates how transactions
    // are required to progress through a system.
		input wire [3 : 0] S_AXI_ARCACHE,
		// Protection type. This signal indicates the privilege
    // and security level of the transaction, and whether
    // the transaction is a data access or an instruction access.
		input wire [2 : 0] S_AXI_ARPROT,
		// Quality of Service, QoS identifier sent for each
    // read transaction.
		input wire [3 : 0] S_AXI_ARQOS,
		// Region identifier. Permits a single physical interface
    // on a slave to be used for multiple logical interfaces.
		input wire [3 : 0] S_AXI_ARREGION,
		// Optional User-defined signal in the read address channel.
		input wire [C_S_AXI_ARUSER_WIDTH-1 : 0] S_AXI_ARUSER,
		// Write address valid. This signal indicates that
    // the channel is signaling valid read address and
    // control information.
		input wire  S_AXI_ARVALID,
		// Read address ready. This signal indicates that
    // the slave is ready to accept an address and associated
    // control signals.
		output wire  S_AXI_ARREADY,
		// Read ID tag. This signal is the identification tag
    // for the read data group of signals generated by the slave.
		output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
		// Read Data
		output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
		// Read response. This signal indicates the status of
    // the read transfer.
		output wire [1 : 0] S_AXI_RRESP,
		// Read last. This signal indicates the last transfer
    // in a read burst.
		output wire  S_AXI_RLAST,
		// Optional User-defined signal in the read address channel.
		output wire [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER,
		// Read valid. This signal indicates that the channel
    // is signaling the required read data.
		output wire  S_AXI_RVALID,
		// Read ready. This signal indicates that the master can
    // accept the read data and response information.
		input wire  S_AXI_RREADY
	);

	// AXI4FULL signals
	reg [C_S_AXI_ADDR_WIDTH-1 : 0] 	axi_awaddr;
	reg  	axi_awready;
	reg  	axi_wready;
	reg [1 : 0] 	axi_bresp;
	reg [C_S_AXI_BUSER_WIDTH-1 : 0] 	axi_buser;
	reg  	axi_bvalid;
	reg [C_S_AXI_ADDR_WIDTH-1 : 0] 	axi_araddr;
	reg  	axi_arready;
	reg [C_S_AXI_DATA_WIDTH-1 : 0] 	axi_rdata;
	reg [1 : 0] 	axi_rresp;
	reg  	axi_rlast;
	reg [C_S_AXI_RUSER_WIDTH-1 : 0] 	axi_ruser;
	reg  	axi_rvalid;
	// aw_wrap_en determines wrap boundary and enables wrapping
	wire aw_wrap_en;
	// ar_wrap_en determines wrap boundary and enables wrapping
	wire ar_wrap_en;
	// aw_wrap_size is the size of the write transfer, the
	// write address wraps to a lower address if upper address
	// limit is reached
	wire [31:0]  aw_wrap_size ; 
	// ar_wrap_size is the size of the read transfer, the
	// read address wraps to a lower address if upper address
	// limit is reached
	wire [31:0]  ar_wrap_size ; 
	// The axi_awv_awr_flag flag marks the presence of write address valid
	reg axi_awv_awr_flag;
	//The axi_arv_arr_flag flag marks the presence of read address valid
	reg axi_arv_arr_flag; 
	// The axi_awlen_cntr internal write address counter to keep track of beats in a burst transaction
	reg [7:0] axi_awlen_cntr;
	//The axi_arlen_cntr internal read address counter to keep track of beats in a burst transaction
	reg [7:0] axi_arlen_cntr;
	reg [1:0] axi_arburst;
	reg [1:0] axi_awburst;
	reg [7:0] axi_arlen;
	reg [7:0] axi_awlen;
	//local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
	//ADDR_LSB is used for addressing 32/64 bit registers/memories
	//ADDR_LSB = 2 for 32 bits (n downto 2) 
	//ADDR_LSB = 3 for 42 bits (n downto 3)

	localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32)+ 1;
	localparam integer OPT_MEM_ADDR_BITS = 3;
	localparam integer USER_NUM_MEM = 1;
	//----------------------------------------------
	//-- Signals for user logic memory space example
	//------------------------------------------------
	wire [OPT_MEM_ADDR_BITS:0] mem_address;
	wire [USER_NUM_MEM-1:0] mem_select;
	reg [C_S_AXI_DATA_WIDTH-1:0] mem_data_out[0 : USER_NUM_MEM-1];

	genvar i;
	genvar j;
	genvar mem_byte_index;

	// I/O Connections assignments

	assign S_AXI_AWREADY	= axi_awready;
	assign S_AXI_WREADY	= axi_wready;
	assign S_AXI_BRESP	= axi_bresp;
	assign S_AXI_BUSER	= axi_buser;
	assign S_AXI_BVALID	= axi_bvalid;
	assign S_AXI_ARREADY	= axi_arready;
	assign S_AXI_RDATA	= axi_rdata;
	assign S_AXI_RRESP	= axi_rresp;
	assign S_AXI_RLAST	= axi_rlast;
	assign S_AXI_RUSER	= axi_ruser;
	assign S_AXI_RVALID	= axi_rvalid;
	assign S_AXI_BID = S_AXI_AWID;
	assign S_AXI_RID = S_AXI_ARID;
	assign  aw_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_awlen)); 
	assign  ar_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_arlen)); 
	assign  aw_wrap_en = ((axi_awaddr & aw_wrap_size) == aw_wrap_size)? 1'b1: 1'b0;
	assign  ar_wrap_en = ((axi_araddr & ar_wrap_size) == ar_wrap_size)? 1'b1: 1'b0;

	// 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       
	// 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       
	// 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       
	// 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   
	// 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       
	// 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       
	// 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    
	// ------------------------------------------
	// -- Example code to access user logic memory region
	// ------------------------------------------

	generate
	  if (USER_NUM_MEM >= 1)
	    begin
	      assign mem_select  = 1;
	      assign mem_address = (axi_arv_arr_flag? axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:(axi_awv_awr_flag? axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:0));
	    end
	endgenerate
	     
	// 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
	//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    

	// Add user logic here

	// User logic ends

	endmodule

3.1 输入输出接口信号分析

3.1.1 自定义参数

		parameter integer C_S_AXI_ID_WIDTH		= 1,	//读写ID的数据位宽
		parameter integer C_S_AXI_DATA_WIDTH	= 32,	//读写数据的位宽
		parameter integer C_S_AXI_ADDR_WIDTH	= 6,	//读写地址的位宽
		parameter integer C_S_AXI_AWUSER_WIDTH	= 0,	//用户自定义信号位宽,没使用就不管它
		parameter integer C_S_AXI_ARUSER_WIDTH	= 0,
		parameter integer C_S_AXI_WUSER_WIDTH	= 0,
		parameter integer C_S_AXI_RUSER_WIDTH	= 0,
		parameter integer C_S_AXI_BUSER_WIDTH	= 0

3.1.2 AXI4接口信号

		input wire  S_AXI_ACLK,								//全局时钟
		input wire  S_AXI_ARESETN,							//全局复位信号,低电平有效
		//写地址通道信号
		input 	wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,		
		input 	wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
		input 	wire [7 : 0] S_AXI_AWLEN,
		input 	wire [2 : 0] S_AXI_AWSIZE,
		input 	wire [1 : 0] S_AXI_AWBURST,
		input 	wire  S_AXI_AWLOCK,
		input 	wire [3 : 0] S_AXI_AWCACHE,
		input 	wire [2 : 0] S_AXI_AWPROT,
		input 	wire [3 : 0] S_AXI_AWQOS,
		input 	wire [3 : 0] S_AXI_AWREGION,
		input 	wire [C_S_AXI_AWUSER_WIDTH-1 : 0] S_AXI_AWUSER,
		input 	wire  S_AXI_AWVALID,
		output 	wire  S_AXI_AWREADY,
		//写数据通道
		input 	wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
		input 	wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
		input 	wire  S_AXI_WLAST,
		input 	wire [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER,
		input 	wire  S_AXI_WVALID,
		output 	wire  S_AXI_WREADY,
		//写响应通道
		output 	wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
		output 	wire [1 : 0] S_AXI_BRESP,
		output 	wire [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER,
		output 	wire  S_AXI_BVALID,
		input 	wire  S_AXI_BREADY,
		//读地址通道
		input 	wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
		input 	wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
		input 	wire [7 : 0] S_AXI_ARLEN,
		input 	wire [2 : 0] S_AXI_ARSIZE,
		input 	wire [1 : 0] S_AXI_ARBURST,
		input 	wire  S_AXI_ARLOCK,
		input 	wire [3 : 0] S_AXI_ARCACHE,
		input 	wire [2 : 0] S_AXI_ARPROT,
		input 	wire [3 : 0] S_AXI_ARQOS,
		input 	wire [3 : 0] S_AXI_ARREGION,
		input 	wire [C_S_AXI_ARUSER_WIDTH-1 : 0] S_AXI_ARUSER,
		input 	wire  S_AXI_ARVALID,
		output 	wire  S_AXI_ARREADY,
		//读数据通道
		output 	wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
		output 	wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
		output 	wire [1 : 0] S_AXI_RRESP,
		output 	wire  S_AXI_RLAST,
		output 	wire [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER,
		output 	wire  S_AXI_RVALID,
		input 	wire  S_AXI_RREADY

  AXI4的信号已在上一文章中详细介绍,这里就不再赘述。

3.2 局部参数定义

	reg             [C_S_AXI_ADDR_WIDTH-1 : 0]          axi_awaddr  ;		//写地址
    reg                                                 axi_awready ;		//写地址准备信号
    reg                                                 axi_wready  ;		//写数据准备信号
    reg             [1 : 0]                             axi_bresp   ;		//写响应
    reg             [C_S_AXI_BUSER_WIDTH-1 : 0]         axi_buser   ;		//写响应用户自定义信号
    reg                                                 axi_bvalid  ;		//写响应valid信号
    reg             [C_S_AXI_ADDR_WIDTH-1 : 0]          axi_araddr  ;		//读地址信号
    reg                                                 axi_arready ;		//读地址准备信号
    reg             [C_S_AXI_DATA_WIDTH-1 : 0]          axi_rdata   ;		//读数据
    reg             [1 : 0]                             axi_rresp   ;		//读响应信号
    reg                                                 axi_rlast   ;		//读突发最后一个数据信号
    reg             [C_S_AXI_RUSER_WIDTH-1 : 0]         axi_ruser   ;		//读通道用户自定义信号
    reg                                                 axi_rvalid  ;		//读valid信号
	wire                                                aw_wrap_en  ;		//写突发类型中的包模式,确定包装边界并启用包
    wire                                                ar_wrap_en  ;		//读突发类型中的包模式,确定包装边界并启用包
    wire            [31:0]                              aw_wrap_size ;		//写传输的大小,如果达到地址上限,则写地址将回到最低的地址
    wire            [31:0]                              ar_wrap_size ; 		//读传输的大小,如果达到地址上限,则读地址将回到最低的地址
    reg                                                 axi_awv_awr_flag ;	//整个写操作标志信号
    reg                                                 axi_arv_arr_flag ; 	//整个读操作标志信号
    reg             [7:0]                               axi_awlen_cntr  ;	//一次突发中,写数据个数计数器
    reg             [7:0]                               axi_arlen_cntr  ;	//一次突发中,读数据个数计数器
    reg             [1:0]                               axi_arburst ;		//读突发类型
    reg             [1:0]                               axi_awburst ;		//写突发类型
    reg             [7:0]                               axi_arlen   ;		//读突发长度
    reg             [7:0]                               axi_awlen   ;		//写突发长度
	localparam  integer                                 ADDR_LSB    = (C_S_AXI_DATA_WIDTH/32)+ 1;//地址的最低变化位,AXI中,一个字节占用一个地址,一个写数据32位占用4个字节。因此写一次数据,地址+4
    localparam  integer                                 OPT_MEM_ADDR_BITS   = 3;				//存储器地址位宽
    localparam  integer                                 USER_NUM_MEM    = 1;					//存储器地址的数量
    wire            [OPT_MEM_ADDR_BITS:0]               mem_address ;							//存储器地址
    wire            [USER_NUM_MEM-1:0]                  mem_select  ;							//选择寄存器信号
    reg             [C_S_AXI_DATA_WIDTH-1:0]            mem_data_out    [0 : USER_NUM_MEM-1];	//定义一个32位宽的寄存器数组

3.3 写地址通道代码分析

  从前面几篇文章我们知道,AXI一共有五个传输通道,每个通道都有对应的握手信号,因此要想AXI传输成功,就必须重点实现握手信号的时序。

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)		//当主机发出valid信号,并且当前没有读和没有写的时候,拉高aw_ready信号
	        begin
	          axi_awready <= 1'b1;
	          axi_awv_awr_flag  <= 1'b1; 
	        end
	      else if (S_AXI_WLAST && axi_wready)   //当写完最后一个数据后,拉低axi_awv_awr_flag表示一次突发写完成   					
	        begin
	          axi_awv_awr_flag  <= 1'b0;
	        end
	      else        
	        begin
	          axi_awready <= 1'b0;
	        end
	    end 
	end

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)				//当前没有写操作时,aw信号握手成功后,将主机发送的写地址信号以及写突发类型,写突发长度暂存下来
	        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)  //每当写数据握手成功一次, axi_awlen_cntr累加一次,直到达到一次写突发长度     
	        begin

	          axi_awlen_cntr <= axi_awlen_cntr + 1;

	          case (axi_awburst)												//根据突发类型来更改写入存储器里写地址
	            2'b00: 															//固定地址突发模式,地址一直固定不变
	              begin
	                axi_awaddr <= axi_awaddr;          
	              end   
	            2'b01: 															//增量模式突发
	              begin
	                axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;	//写数据握手成功,从地址第三位开始累加1
	                axi_awaddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   												//低两位不变,所以相当于每写一次地址+4
	              end   
	            2'b10: 															//包类型突发,类似于回环
	              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: 
	              begin
	                axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
	              end
	          endcase              
	        end
	    end 
	end       

3.4 写数据通道代码分析

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)	//当前在wr_flag信号写,主机发出valid信号时候,拉高wready信号
	        begin
	          axi_wready <= 1'b1;
	        end
	      else if (S_AXI_WLAST && axi_wready)					//写完最后一个数据后,拉低wready信号
	        begin
	          axi_wready <= 1'b0;
	        end
	    end 
	end

3.5 写响应通道代码分析

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 ) //当写完最后一个数据后 拉高bvalid信号以及回复OKAY
	        begin
	          axi_bvalid <= 1'b1;
	          axi_bresp  <= 2'b0; 
	        end                   
	      else
	        begin
	          if (S_AXI_BREADY && axi_bvalid) 								//握手成功后拉低
	            begin
	              axi_bvalid <= 1'b0; 
	            end  
	        end
	    end
	 end 

3.6 读地址通道代码分析

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)	//当前没有在写和读时候,当主机发出arvalid信号时,拉高arready信号
	        begin
	          axi_arready <= 1'b1;
	          axi_arv_arr_flag <= 1'b1;													//并且拉高rdflag信号
	        end
	      else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)			//当最后一个数据读出并且握手成功后,拉低rdflag信号
	        begin
	          axi_arv_arr_flag  <= 1'b0;
	        end
	      else        
	        begin
	          axi_arready <= 1'b0;
	        end
	    end 
	end


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
	          axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0]; 		
	          axi_arburst <= S_AXI_ARBURST; 
	          axi_arlen <= S_AXI_ARLEN;     
	          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: 
	              begin
	                axi_araddr       <= axi_araddr;        
	
	              end   
	            2'b01:
	              begin
	                axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1; 
	                axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
	              end   
	            2'b10: 
	              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; 
	                axi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   
	                end                      
	            default: 
	              begin
	                axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
	              end
	          endcase              
	        end
	      else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )   //当准备发送最后一个读数据时,拉高rlast信号
	        begin
	          axi_rlast <= 1'b1;
	        end          
	      else if (S_AXI_RREADY)   //握手成功后拉低
	        begin
	          axi_rlast <= 1'b0;
	        end          
	    end 
	end

3.7 读数据通道代码分析

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;		//当读标志有效时,拉高rvalid
	          axi_rresp  <= 2'b0; 		//回复 ok
	        end   
	      else if (axi_rvalid && S_AXI_RREADY)
	        begin
	          axi_rvalid <= 1'b0;
	        end            
	    end
	end 


//定义存储器+
generate
	  if (USER_NUM_MEM >= 1)
	    begin
	      assign mem_select  = 1;
	      assign mem_address = (axi_arv_arr_flag? axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:(axi_awv_awr_flag? axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:0));
	    end
	endgenerate
	     
	// 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

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 

四、仿真结果

  因为本次验证,直接添加的VIP快速验证,因此直接开始跑仿真即可,将不同通道信号用不同颜色区分开来,仿真如下:

在这里插入图片描述

4.1 观察写通道仿真

在这里插入图片描述

  1. 首先等待写地址通道握手成功后,写起始地址从0开始,突发类型为增量突发,突发长度为8
  2. 然后等待写地址通道握手成功后,写数据通道给出写数据以及valid信号,每次握手成功,写数据累加1,然后与从机握手7次后,最后一次数据伴随着wlast,等待握手
  3. 当一次写突发完成后,等待从机回复信号握手成功

  整个仿真结果与手册给出的通道示意图一致:

在这里插入图片描述

4.2 观察读通道仿真

在这里插入图片描述

  当主机给出读地址信号以及arvalid信号,与从机握手成功后,从机根据突发大小以及突发类型给出读数据,此时给出的读数据是从1-8,与前面写入的数据一致。

  整个读通道的时序图和AXI协议给出的整体框图一致:

在这里插入图片描述
  因此整个AXI4_FULL_SLAVE接口的读写仿真验证已经完成。

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: AXI4是一种高性能、高带宽的接口协议,在使用时会用到AXI4 Master和AXI4 Slave两个模块。对于AXI4 Master和AXI4 Slave的源代码对应分析,需要从两个模块的功能和架构入手。 AXI4 Master是连接处理器、DMA、FPGA以及其他数据源的主机总线接口,主要用于发送读/写事务请求,以及接收响应数据。其源代码的实现需要遵循AXI4协议的规定,包括发出READ/WRITE数据请求等操作。 而AXI4 Slave是连接存储器、FIFO、寄存器、设备控制器等外设的从机总线接口,主要负责响应AXI4 Master的读/写事务请求。其源代码的实现需要实现AXI4 Slave接口的各类功能,例如收到接收到READ/WRITE数据请求时进行响应的机制等。 总的来说,AXI4 Master和AXI4 Slave的源代码对应分析需要具有扎实的计算机系统结构基础、嵌入式系统开发经验及VHDL或Verilog语言编程技能。在使用时,需要遵循AXI4协议规定,进行必要的代码优化,以提高系统的性能和稳定性。 ### 回答2: AXI4是一种高性能、低功耗、低复杂度的总线协议,被广泛用于FPGA和SoC芯片中。在AXI4中,Master和Slave是两个重要概念,Master可以去向Slave发起读写请求,Slave提供相应的数据或状态返回。 在AXI4 Master Slave源码对应分析中,我们需要先了解AXI4协议的基本原理和结构。AXI4的数据传输包括地址、数据和控制信号三个部分。其中,地址和控制信号一般由Master控制发送,数据由Slave提供返回。Master和Slave之间的通讯可以通过总线信号实现,如时钟、使能、读写标志等。 在源码分析过程中,我们需要先理清楚设计的框架结构和各个模块之间的关系。一般来说,一个AXI4 Master Slave的设计包括Master和Slave两个主模块,以及一些必要的逻辑模块。Master可以是一个外部设备,如CPU,也可以是FPGA内部的逻辑模块;同样地,Slave也可以是一个外部设备,如存储器,也可以是FPGA内部的逻辑模块。在设计内部逻辑模块时,需要考虑合理的接口设计和信号传输方式,以充分利用AXI4协议的特点,实现高效稳定的数据传输。 在进行源码分析时,需要对每个模块的具体功能做详细的了解,如输入输出端口、状态寄存器、控制信号等。此外,还需要仔细考虑各个模块的时序要求,以避免数据传输时的不一致和错误。在分析过程中,可以借助FPGA开发工具的仿真功能,对源码进行模拟验证,以确保设计的正确性和可靠性。 总之,AXI4 Master Slave源码对应分析是一个相对较为复杂和细致的工作,需要对AXI4协议有深刻理解和丰富的实践经验,同时还需要熟练掌握FPGA开发环境和设计工具的使用。只有通过不断地实践和积累,才能在设计中发挥出AXI4协议的最大潜力,实现高性能、低功耗的数据传输。 ### 回答3: AXI4是ARM公司推出的一种高性能片上总线协议,支持多核、功耗优化、多带宽等特性,应用广泛。本文将对AXI4 Master和Slave源码进行分析AXI4 Master部分的源码是通用的,可以配置成读写、反悔等各种操作,实现起来比较简单。具体实现代码可以参考Xilinx公司提供的axi_master_burst.v文件。 AXI4 Slave部分的源码比较复杂,需要支持读写反悔各种操作,还要处理数据乱序、地址捕获等问题。通常是通过Finite State Machine(有限状态机)来实现AXI4 Slave端的逻辑。具体实现代码可以参考Xilinx公司提供的axi_slave_lite.v文件。 AXI4协议中的控制信号包括:地址、数据、控制、状态和辅助等。其中,地址信号用于指定操作的地址,控制信号用于指定读写类型等操作,状态信号用于反映操作是否完成,辅助信号提供了一些附加信息。 AXI4 Master和Slave源码是嵌入式系统设计中非常重要的实现部分,掌握其实现原理对于理解AXI4协议及其应用场景非常有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱奔跑的虎子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值