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

本文主要是介绍详解并掌握AXI4总线协议(四)、AXI4_FULL_SLAVE接口源码分析以及仿真验证,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

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

文章目录

  • 系列文章目录
  • 一、前言
  • 二、生成axi4_full_slave接口模板
  • 三、分析axi4_full_slave接口代码
    • 3.1 输入输出接口信号分析
      • 3.1.1 自定义参数
      • 3.1.2 AXI4接口信号
    • 3.2 局部参数定义
    • 3.3 写地址通道代码分析
    • 3.4 写数据通道代码分析
    • 3.5 写响应通道代码分析
    • 3.6 读地址通道代码分析
    • 3.7 读数据通道代码分析
  • 四、仿真结果
    • 4.1 观察写通道仿真
    • 4.2 观察读通道仿真


一、前言

  在上前面几篇文章中,我们了解了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 psmodule 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 dataparameter integer C_S_AXI_ID_WIDTH	= 1,// Width of S_AXI data busparameter integer C_S_AXI_DATA_WIDTH	= 32,// Width of S_AXI address busparameter integer C_S_AXI_ADDR_WIDTH	= 6,// Width of optional user defined signal in write address channelparameter integer C_S_AXI_AWUSER_WIDTH	= 0,// Width of optional user defined signal in read address channelparameter integer C_S_AXI_ARUSER_WIDTH	= 0,// Width of optional user defined signal in write data channelparameter integer C_S_AXI_WUSER_WIDTH	= 0,// Width of optional user defined signal in read data channelparameter integer C_S_AXI_RUSER_WIDTH	= 0,// Width of optional user defined signal in write response channelparameter 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 Signalinput wire  S_AXI_ACLK,// Global Reset Signal. This Signal is Active LOWinput wire  S_AXI_ARESETN,// Write Address IDinput wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,// Write addressinput wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,// Burst length. The burst length gives the exact number of transfers in a burstinput wire [7 : 0] S_AXI_AWLEN,// Burst size. This signal indicates the size of each transfer in the burstinput 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 Datainput 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 burstinput wire [7 : 0] S_AXI_ARLEN,// Burst size. This signal indicates the size of each transfer in the burstinput 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 Dataoutput 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 signalsreg [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 wrappingwire aw_wrap_en;// ar_wrap_en determines wrap boundary and enables wrappingwire 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 reachedwire [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 reachedwire [31:0]  ar_wrap_size ; // The axi_awv_awr_flag flag marks the presence of write address validreg axi_awv_awr_flag;//The axi_arv_arr_flag flag marks the presence of read address validreg axi_arv_arr_flag; // The axi_awlen_cntr internal write address counter to keep track of beats in a burst transactionreg [7:0] axi_awlen_cntr;//The axi_arlen_cntr internal read address counter to keep track of beats in a burst transactionreg [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 assignmentsassign 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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_awready <= 1'b0;axi_awv_awr_flag <= 1'b0;end elsebegin    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 signalsaxi_awready <= 1'b1;axi_awv_awr_flag  <= 1'b1; // used for generation of bresp() and bvalidendelse if (S_AXI_WLAST && axi_wready)          // preparing to accept next address after current write burst tx completionbeginaxi_awv_awr_flag  <= 1'b0;endelse        beginaxi_awready <= 1'b0;endend 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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_awaddr <= 0;axi_awlen_cntr <= 0;axi_awburst <= 0;axi_awlen <= 0;end elsebegin    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 transferaxi_awlen_cntr <= 0;end   else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)        beginaxi_awlen_cntr <= axi_awlen_cntr + 1;case (axi_awburst)2'b00: // fixed burst// The write address for all the beats in the transaction are fixedbeginaxi_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 awsizebeginaxi_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 boundaryaxi_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)beginaxi_awaddr <= (axi_awaddr - aw_wrap_size); endelse beginaxi_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)beginaxi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;//for awsize = 4 bytes (010)endendcase              endend 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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_wready <= 1'b0;end elsebegin    if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)begin// slave can accept the write dataaxi_wready <= 1'b1;end//else if (~axi_awv_awr_flag)else if (S_AXI_WLAST && axi_wready)beginaxi_wready <= 1'b0;endend 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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_bvalid <= 0;axi_bresp <= 2'b0;axi_buser <= 0;end elsebegin    if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )beginaxi_bvalid <= 1'b1;axi_bresp  <= 2'b0; // 'OKAY' response end                   elsebeginif (S_AXI_BREADY && axi_bvalid) //check if bready is asserted while bvalid is high) //(there is a possibility that bready is always asserted high)   beginaxi_bvalid <= 1'b0; end  endendend   // 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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_arready <= 1'b0;axi_arv_arr_flag <= 1'b0;end elsebegin    if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)beginaxi_arready <= 1'b1;axi_arv_arr_flag <= 1'b1;endelse if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)// preparing to accept next address after current read completionbeginaxi_arv_arr_flag  <= 1'b0;endelse        beginaxi_arready <= 1'b0;endend 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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_araddr <= 0;axi_arlen_cntr <= 0;axi_arburst <= 0;axi_arlen <= 0;axi_rlast <= 1'b0;axi_ruser <= 0;end elsebegin    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 transferaxi_arlen_cntr <= 0;axi_rlast <= 1'b0;end   else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)        beginaxi_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 fixedbeginaxi_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 awsizebeginaxi_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 boundaryaxi_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) beginaxi_araddr <= (axi_araddr - ar_wrap_size); endelse beginaxi_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 boundaryaxi_araddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   end                      default: //reserved (incremental burst for example)beginaxi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;//for arsize = 4 bytes (010)endendcase              endelse if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )   beginaxi_rlast <= 1'b1;end          else if (S_AXI_RREADY)   beginaxi_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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_rvalid <= 0;axi_rresp  <= 0;end elsebegin    if (axi_arv_arr_flag && ~axi_rvalid)beginaxi_rvalid <= 1'b1;axi_rresp  <= 2'b0; // 'OKAY' responseend   else if (axi_rvalid && S_AXI_RREADY)beginaxi_rvalid <= 1'b0;end            endend    // ------------------------------------------// -- Example code to access user logic memory region// ------------------------------------------generateif (USER_NUM_MEM >= 1)beginassign 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));endendgenerate// implement Block RAM(s)generate for(i=0; i<= USER_NUM_MEM-1; i=i+1)begin:BRAM_GENwire mem_rden;wire mem_wren;assign mem_wren = axi_wready && S_AXI_WVALID ;assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalidfor(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)begin:BYTE_BRAM_GENwire [8-1:0] data_in ;wire [8-1:0] data_out;reg  [8-1:0] byte_ram [0 : 15];integer  j;//assigning 8 bit dataassign data_in  = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];assign data_out = byte_ram[mem_address];always @( posedge S_AXI_ACLK )beginif (mem_wren && S_AXI_WSTRB[mem_byte_index])beginbyte_ram[mem_address] <= data_in;end   end    always @( posedge S_AXI_ACLK )beginif (mem_rden)beginmem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;end   end    endend       endgenerate//Output register or memory read dataalways @( mem_data_out, axi_rvalid)beginif (axi_rvalid) begin// Read address muxaxi_rdata <= mem_data_out[0];end   elsebeginaxi_rdata <= 32'h00000000;end       end    // Add user logic here// User logic endsendmodule

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个字节。因此写一次数据,地址+4localparam  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 )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_awready <= 1'b0;axi_awv_awr_flag <= 1'b0;end elsebegin    if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)		//当主机发出valid信号,并且当前没有读和没有写的时候,拉高aw_ready信号beginaxi_awready <= 1'b1;axi_awv_awr_flag  <= 1'b1; endelse if (S_AXI_WLAST && axi_wready)   //当写完最后一个数据后,拉低axi_awv_awr_flag表示一次突发写完成   					beginaxi_awv_awr_flag  <= 1'b0;endelse        beginaxi_awready <= 1'b0;endend endalways @( posedge S_AXI_ACLK )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_awaddr <= 0;axi_awlen_cntr <= 0;axi_awburst <= 0;axi_awlen <= 0;end elsebegin    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 transferaxi_awlen_cntr <= 0;end   else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)  //每当写数据握手成功一次, axi_awlen_cntr累加一次,直到达到一次写突发长度     beginaxi_awlen_cntr <= axi_awlen_cntr + 1;case (axi_awburst)												//根据突发类型来更改写入存储器里写地址2'b00: 															//固定地址突发模式,地址一直固定不变beginaxi_awaddr <= axi_awaddr;          end   2'b01: 															//增量模式突发beginaxi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;	//写数据握手成功,从地址第三位开始累加1axi_awaddr[ADDR_LSB-1:0]  <= {ADDR_LSB{1'b0}};   												//低两位不变,所以相当于每写一次地址+4end   2'b10: 															//包类型突发,类似于回环if (aw_wrap_en)begin														//当地址到包边界时,地址重新回到最低位axi_awaddr <= (axi_awaddr - aw_wrap_size); 				endelse beginaxi_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: beginaxi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;endendcase              endend end       

3.4 写数据通道代码分析

always @( posedge S_AXI_ACLK )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_wready <= 1'b0;end elsebegin    if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)	//当前在wr_flag信号写,主机发出valid信号时候,拉高wready信号beginaxi_wready <= 1'b1;endelse if (S_AXI_WLAST && axi_wready)					//写完最后一个数据后,拉低wready信号beginaxi_wready <= 1'b0;endend end

3.5 写响应通道代码分析

always @( posedge S_AXI_ACLK )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_bvalid <= 0;axi_bresp <= 2'b0;axi_buser <= 0;end elsebegin    if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST ) //当写完最后一个数据后 拉高bvalid信号以及回复OKAYbeginaxi_bvalid <= 1'b1;axi_bresp  <= 2'b0; end                   elsebeginif (S_AXI_BREADY && axi_bvalid) 								//握手成功后拉低beginaxi_bvalid <= 1'b0; end  endendend 

3.6 读地址通道代码分析

always @( posedge S_AXI_ACLK )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_arready <= 1'b0;axi_arv_arr_flag <= 1'b0;end elsebegin    if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)	//当前没有在写和读时候,当主机发出arvalid信号时,拉高arready信号beginaxi_arready <= 1'b1;axi_arv_arr_flag <= 1'b1;													//并且拉高rdflag信号endelse if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)			//当最后一个数据读出并且握手成功后,拉低rdflag信号beginaxi_arv_arr_flag  <= 1'b0;endelse        beginaxi_arready <= 1'b0;endend endalways @( posedge S_AXI_ACLK )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_araddr <= 0;axi_arlen_cntr <= 0;axi_arburst <= 0;axi_arlen <= 0;axi_rlast <= 1'b0;axi_ruser <= 0;end elsebegin    if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)			//当地址通道信号握手成功后,将读地址信号,读突发类型,读突发长度信号暂存下来beginaxi_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) //每次读数据握手成功后,读数据数量计数器累加一       beginaxi_arlen_cntr <= axi_arlen_cntr + 1;axi_rlast <= 1'b0;case (axi_arburst)					//根据突发类型选择地址,跟上面写通道一致2'b00: beginaxi_araddr       <= axi_araddr;        end   2'b01:beginaxi_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) beginaxi_araddr <= (axi_araddr - ar_wrap_size); endelse beginaxi_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: beginaxi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;endendcase              endelse if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )   //当准备发送最后一个读数据时,拉高rlast信号beginaxi_rlast <= 1'b1;end          else if (S_AXI_RREADY)   //握手成功后拉低beginaxi_rlast <= 1'b0;end          end end

3.7 读数据通道代码分析

always @( posedge S_AXI_ACLK )beginif ( S_AXI_ARESETN == 1'b0 )beginaxi_rvalid <= 0;axi_rresp  <= 0;end elsebegin    if (axi_arv_arr_flag && ~axi_rvalid)beginaxi_rvalid <= 1'b1;		//当读标志有效时,拉高rvalidaxi_rresp  <= 2'b0; 		//回复 okend   else if (axi_rvalid && S_AXI_RREADY)beginaxi_rvalid <= 1'b0;end            endend //定义存储器+
generateif (USER_NUM_MEM >= 1)beginassign 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));endendgenerate// implement Block RAM(s)generate for(i=0; i<= USER_NUM_MEM-1; i=i+1)begin:BRAM_GENwire mem_rden;wire mem_wren;assign mem_wren = axi_wready && S_AXI_WVALID ;assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalidfor(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)begin:BYTE_BRAM_GENwire [8-1:0] data_in ;wire [8-1:0] data_out;reg  [8-1:0] byte_ram [0 : 15];integer  j;//assigning 8 bit dataassign data_in  = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];assign data_out = byte_ram[mem_address];always @( posedge S_AXI_ACLK )beginif (mem_wren && S_AXI_WSTRB[mem_byte_index])beginbyte_ram[mem_address] <= data_in;end   end    always @( posedge S_AXI_ACLK )beginif (mem_rden)beginmem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;end   end    endend       endgeneratealways @( mem_data_out, axi_rvalid)beginif (axi_rvalid) begin// Read address muxaxi_rdata <= mem_data_out[0];end   elsebeginaxi_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接口的读写仿真验证已经完成。

这篇关于详解并掌握AXI4总线协议(四)、AXI4_FULL_SLAVE接口源码分析以及仿真验证的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1096675

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、