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

news2024/11/27 5:25:47

系列文章目录

详解并掌握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 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接口的读写仿真验证已经完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2062955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费好用!阿里这5款AI神器,太强了,打工人必备!(建议收藏)

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 《黑神话&#xff1a;悟空》太爆了&#xff01;最近感觉都要被…

Eclipse部署一个项目到Tomcat和部署多个项目到Tomcat

Eclipse部署一个项目到Tomcat&#xff1a; https://blog.csdn.net/weixin_42334396/article/details/105902994 Eclipse部署多个项目到Tomcat&#xff1a; https://blog.csdn.net/zhanglin1220/article/details/82056185 使用cmd方法强制关闭端口&#xff0c;解除端口占用方法&…

2.初识springcloud

文章目录 1.什么是SpringCloud1.1版本的介绍 2.Spring Cloud实现方案3.环境搭建4.服务拆分原则5.数据准备5.1订单服务5.2商品服务 大家好&#xff0c;我是晓星航。今天为大家带来的是 初识springcloud 相关的讲解&#xff01;&#x1f600; 1.什么是SpringCloud 简单来说&…

Excel表格添加趋势线_数据拟合

一个曲线通过补偿算法拟合为另一个曲线&#xff0c;通常可以通过多种数学和计算技术实现。这里也可以通过Excel表格添加趋势线&#xff0c;然后对趋势线进行拟合&#xff0c;得到趋势预测公式来达到数据补偿。 通过把你需要的数据导入到Excel表格中。 通过 “ 插入 ” --> “…

从单一到互联:KNX网关如何改变你的家居生活

从单一到互联&#xff1a;KNX网关如何改变你的家居生活 在科技日新月异的今天&#xff0c;家居生活正经历着一场前所未有的变革。从过去单一、孤立的设备操作&#xff0c;到如今智能、互联的生态系统&#xff0c;KNX网关作为这一转变的关键角色&#xff0c;正悄然改变着我们的日…

探索Unity3D URP后处理在UI控件Image上的应用

探索Unity3D URP后处理在UI控件Image上的应用 前言初识URP配置后处理效果将后处理应用于UI控件方法一&#xff1a;自定义Shader方法二&#xff1a;RenderTexture的使用 实践操作步骤一&#xff1a;创建RenderTexture步骤二&#xff1a;UI渲染至RenderTexture步骤三&#xff1a;…

养宠家庭一定要试 希喂、美的两大品牌宠物空气净化器性能对比

随着养宠家庭的增多&#xff0c;宠物市场上产品也在不断丰富。这段时间最热门的产品非宠物空气净化器莫属&#xff0c;讨论度非常高&#xff0c;大家对它的评价褒贬不一。作为三只毛孩子的家长&#xff0c;它们的毛发清理问题一直令我头大&#xff0c;所以决定试试号称专为吸附…

科技守护健康:氧气检测仪的工作原理与应用深探

科技守护健康&#xff1a;氧气检测仪的工作原理与应用深探 在科技飞速进步的今天&#xff0c;我们身边的许多设备都融入了科技的智慧&#xff0c;其中氧气检测仪便是守护人类健康与安全的重要一环。它以其独特的工作原理&#xff0c;在工业生产、医疗护理等多个领域发挥着至关重…

数字图像处理【15】特征检测——SIFT特征检测

一、引入SIFT算法 上一篇文章我们重温学习了Harris角点检测算法的基本原理&#xff0c;但在实际生产使用Harris检测角点的时候&#xff0c;会发现一个问题&#xff0c;就是用于检测的输入图像的尺寸大小会直接影响到Harris的检测结果。这是为什么呢&#xff1f;主要是Harris角…

引领未来的NVR方案:海思3520D芯片与全套NVR模组源代码解析

随着视频监控技术的发展&#xff0c;NVR&#xff08;网络视频录像机&#xff09;已成为安全保障系统的核心设备。市场上NVR芯片方案主要由几大厂商主导&#xff0c;其中海思35XX系列在中高端市场占据主导地位&#xff0c;特别适用于图像处理和多种应用场景。 我们研发了一款基…

探索提示工程 Prompt Engineering的奥妙

一、探索提示工程 1. 介绍通用人工智能和专用人工智能 人工智能&#xff08;AI&#xff09;可以分为通用人工智能&#xff08;AGI&#xff09;和专用人工智能&#xff08;Narrow AI&#xff09;。AGI是一种能够理解、学习和执行任何人类可以完成的任务的智能。与此相对&#x…

嵌入式学习day17(数据结构)

大纲 数据结构、算法数据结构&#xff1a; 1. 线性表&#xff1a;顺序表、链表&#xff08;单向链表&#xff0c;单向循环链表&#xff0c;双向链表&#xff0c;双向循环链表&#xff09;、栈&#xff08;顺序栈&#xff0c;链式栈&#xff09;、队列&#xff08;循…

得物App白屏优化系列|网络篇

一、背景 图片加载作为重中之重的App体验指标&#xff0c;端侧的白屏问题则是其中最为严重的问题之一。想象一下如果你在浏览交易商品、社区帖子等核心场景下&#xff0c;图片无法完成加载是多么糟糕的体验。 网络作为图片资源加载的最主要来源途径&#xff0c;如果不能够快速…

医疗器械维修零基础也可以学吗?

最近您是不是在纠结&#xff1a;医疗器械维修&#xff0c;零基础能学吗&#xff1f;别担心&#xff0c;答案就在这里&#xff01;亲爱的朋友&#xff0c;零基 础绝对不是您追求医疗器械维修技能的绊脚石。我们有精心设计的课程&#xff0c;就像为您量身打造的成长阶梯。 无论您…

海外销量不错,长城汽车因“重大失信影响恶劣”被南方电网拉黑

《港湾商业观察》施子夫 被南方电网“拉黑”的长城汽车&#xff08;601633.SH&#xff1b;02333.HK&#xff09;最近深陷旋涡。最新的消息是长城汽车道歉了。 8月14日&#xff0c;中国南方电网供应链统一服务平台发布一则公告&#xff0c;公告称&#xff0c;为促进供应商诚信…

C语言之字节对齐

目录 1. 引言2.字节对齐原理3.字节对齐应用4.总结 1. 引言 字节对齐属于编译器的内容&#xff0c;决定数据实际的存放方式。主要有两个作用&#xff1a;1.优化数据储存&#xff0c;减少空间浪费 2.增加数据读取速率&#xff0c;本文将于以上两点展开&#xff0c;简述字节对齐的…

四川正信晟锦:同学借钱不还不回消息

在校园的微风轻拂下&#xff0c;我们彼此以赤诚的心交换着青春的誓言。那些日子里&#xff0c;友情如影随形&#xff0c;金钱在这份单纯中显得微不足道。然而&#xff0c;随着时间的流逝&#xff0c;一桩桩借钱不还的事件悄然滋生&#xff0c;如同夜幕下潜行的阴影&#xff0c;…

bC一体化助力终端动销 单日销量提升5倍

想象一下这样令人振奋的场景&#xff1a;某品牌在短短一天内的销量&#xff0c;竟然达到了过去一周的总销量&#xff01;又或者&#xff0c;一个品牌因其零售店的出色表现&#xff0c;就迅速在一个月内席卷了整个乡镇的所有零售店。 这样的终端动销效果&#xff0c;是否让你心…

AC自动机-2(AhoCorasickDoubleArrayTrie)

Aho-Corasick Double Array Trie (AC DAT) 是一种结合了Aho-Corasick算法和Double Array Trie的数据结构&#xff0c;DAT保证了较高的存储效率&#xff0c;AC保证了多模式字符串匹配效率。 一个经典的实现是hanlp的Java实现&#xff1a;AhoCorasickDoubleArrayTrie。 主要构造过…

2024精选:四款超实用免费视频剪辑软件推荐!

每一位内容创作者都离不开一款好用的视频剪辑软件。今天小编推荐几款实用的视频剪辑免费软件&#xff0c;帮助大家轻松制作出高质量的视频作品&#xff01; 福昕视频剪辑 链接&#xff1a;www.pdf365.cn/foxit-clip/ 福昕视频剪辑的多轨道编辑功能让我在视频制作过程中感到非…