AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析

一、前言

        在之前的文章中,我们介绍了AXI-S协议的一些基础知识,这是我们进行本文学习的前置基础,因此建议在开始本文章的学习前,完整阅读以下两篇文章:

AXI-Stream协议详解(1)—— Introductionicon-default.png?t=N7T8https://blog.csdn.net/apple_53311083/article/details/134058532?spm=1001.2014.3001.5501AXI-Stream协议详解(2)—— Interface Signalsicon-default.png?t=N7T8https://blog.csdn.net/apple_53311083/article/details/134065597?spm=1001.2014.3001.5501

二、带AXI-Stream接口的IP核

1、IP核创建

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第1张图片

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第2张图片

        在这里我们选择最底下的一项,创建一个带有AXI接口的IP核

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第3张图片

        接下来我们设置IP核的一些细节信息,这里把名称改成了axis_m,代表这是AXI-Stream协议的主机。

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第4张图片

        选择stream协议,选择主机类型,相应地完成名称更改,这里的数据位宽我们暂时不做更改,保持默认的32bit就行。

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第5张图片

        最后这里直接添加到IP库里就完成了。

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第6张图片

        然后我们通过同样的方式可以完成axi_s(带有AXI-S接口的从机)的创建,这里就省略创建过程了。

2、IP核学习

        在IP Catlog下搜索找到我们之前创建的2个带有AXI-Stream协议的IP核

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第7张图片

        右击选择Edit in packager

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第8张图片

        我们先以从机为例,显示如何找到AXI-S的设计部分,直接点击OK直到打开一个新的vivado界面

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第9张图片

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第10张图片

        可以看到里面有两个模块 

 AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第11张图片

        我们依次打开两个文件,同时可以把axis_m IP核中的文件同时打开,方便我们进行学习,打开过程同上,这里不做重复。

        对于axis_s_v1_0和axis_m_v1_0这两个模块来说,只是完成了对底层模块的一个例化,所以没有什么可以过多赘述的。

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第12张图片

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第13张图片

        下面我们着重介绍axis_s_v1_0_S00_AXIS和axis_m_v1_0_M00_AXIS两个模块

三、AXI-Stream源代码学习

        其实Xilinx官方已经给出了非常详细的英文注释,可以帮助我们快速了解整个AXI-S协议的实现方式,写的真的非常好。这里也只是在其基础上做一个简单的翻译和补充,首先给出笔者中文注释版本,再给出官方的注释版本,推荐阅读后者。

1、AXIS主机部分

1.1 中文注释


`timescale 1 ns / 1 ps

	module axis_m_v1_0_M00_AXIS #
	(
		/*
			用户可以在此自定义参数
		*/
		
		parameter integer C_M_AXIS_TDATA_WIDTH	= 32,      // 发送数据的位宽
		// 初始化的最大计数时钟(等待系统稳定的时间)
		parameter integer C_M_START_COUNT	= 32
	)
	(
		/*
			用户可以在此自定义端口
		*/

		//全局信号
		input wire  M_AXIS_ACLK,                    			     // 时钟信号
		input wire  M_AXIS_ARESETN,                   				 // 复位信号(低电平有效)

		output wire  M_AXIS_TVALID,                   				 //有效信号,代表主机已经准备好了
		output wire [C_M_AXIS_TDATA_WIDTH-1 : 0] M_AXIS_TDATA,       //数据信号
		output wire [(C_M_AXIS_TDATA_WIDTH/8)-1 : 0] M_AXIS_TSTRB,	 //数据修饰符,辨别字节类型	
		output wire  M_AXIS_TLAST,                      		     //last信号,拉高代表是传输中的最后一个字节
		input wire  M_AXIS_TREADY                                    //ready信号,代表从机准备好了
	);
                                              
	localparam NUMBER_OF_OUTPUT_WORDS = 8;                           //发送数据的个数                                        
	                                                                                     
	//函数:以2为低求对数,用于计算位宽
	function integer clogb2 (input integer bit_depth);                                   
	  begin                                                                              
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                                      
	      bit_depth = bit_depth >> 1;                                                    
	  end                                                                                
	endfunction                                                                          
	                                                                                                                    
	localparam integer WAIT_COUNT_BITS = clogb2(C_M_START_COUNT-1);  //等待计时寄存器的位宽                      
	localparam bit_num  = clogb2(NUMBER_OF_OUTPUT_WORDS);            //发送数据寄存器位宽                 
	                                                                                          
	//状态机参数                              
	parameter [1:0] IDLE = 2'b00,         						     //初始状态                                                                                               
	                INIT_COUNTER  = 2'b01,                           //初始化计数器,等待计数值达到最大计数时钟,进入下一个状态
	                SEND_STREAM   = 2'b10;                           //数据发送状态
                                                                    
	reg [1:0] mst_exec_state;                                        //状态寄存器                                                                                                       
	reg [bit_num-1:0] read_pointer;                                  //FIFO读指针                          

	// AXIS内部信号
	reg [WAIT_COUNT_BITS-1 : 0] 	count;                           //等待计数器(实现我们之前说的计时功能)
	wire  	axis_tvalid;                                             //valid
	reg  	axis_tvalid_delay;                                       //延时一个时钟周期的valid
	wire  	axis_tlast;                                              //last
	reg  	axis_tlast_delay;                                        //延迟一个时钟周期的last

	reg [C_M_AXIS_TDATA_WIDTH-1 : 0] 	stream_data_out;             //data
	wire  	tx_en;                                                   //发送使能
	reg  	tx_done;                                                 //发送完成


	//赋值操作
	assign M_AXIS_TVALID	= axis_tvalid_delay;                    
	assign M_AXIS_TDATA	= stream_data_out;
	assign M_AXIS_TLAST	= axis_tlast_delay;
	assign M_AXIS_TSTRB	= {(C_M_AXIS_TDATA_WIDTH/8){1'b1}};         //全1 


	//控制状态机                          
	always @(posedge M_AXIS_ACLK)                                             
	begin                                                                     
	  if (!M_AXIS_ARESETN)                                                                                           
	    begin                                                                 
	      mst_exec_state <= IDLE;                                             
	      count    <= 0;                                                      
	    end                                                                   
	  else                                                                    
	    case (mst_exec_state)                                                 
	      IDLE:                                             //一个周期后直接进入下一个状态                                                                                                              
	            mst_exec_state  <= INIT_COUNTER;                                                                                                                                                             
	      INIT_COUNTER:                                     //计数器达到最大计数值,进入次态                                                         
	        if ( count == C_M_START_COUNT - 1 )                               
	          begin                                                           
	            mst_exec_state  <= SEND_STREAM;                               
	          end                                                             
	        else                                                              
	          begin                                                           
	            count <= count + 1;                                           
	            mst_exec_state  <= INIT_COUNTER;                              
	          end                                                             
	                                                                          
	      SEND_STREAM:                                    //发送状态,完成发送后回到初始态                                         
	        if (tx_done)                                                      
	          begin                                                           
	            mst_exec_state <= IDLE;                                       
	          end                                                             
	        else                                                              
	          begin                                                           
	            mst_exec_state <= SEND_STREAM;                                
	          end                                                             
	    endcase                                                               
	end                                                                       

	//valid信号(表示主机有没有准备好),当处于发送状态,读指针小于发送数据个数时(也就是处于发送状态且还有数据要发)生效
	assign axis_tvalid = ((mst_exec_state == SEND_STREAM) && (read_pointer < NUMBER_OF_OUTPUT_WORDS));
	                                                                                               
	//last信号(表示发送的最后一个字节),当读指针等于发送数据个数-1时生效
	assign axis_tlast = (read_pointer == NUMBER_OF_OUTPUT_WORDS-1);                                
	                                                                                               
	//完成axis_tvalid_delay,axis_tlast_delay(延迟一个时钟的valid和last)的赋值
	always @(posedge M_AXIS_ACLK)                                                                  
	begin                                                                                          
	  if (!M_AXIS_ARESETN)                                                                         
	    begin                                                                                      
	      axis_tvalid_delay <= 1'b0;                                                               
	      axis_tlast_delay <= 1'b0;                                                                
	    end                                                                                        
	  else                                                                                         
	    begin                                                                                      
	      axis_tvalid_delay <= axis_tvalid;                                                        
	      axis_tlast_delay <= axis_tlast;                                                          
	    end                                                                                        
	end                                                                                            

	//读指针
	always@(posedge M_AXIS_ACLK)                                               
	begin                                                                             
	  if(!M_AXIS_ARESETN)                                //复位                                             
	    begin                                                                        
	      read_pointer <= 0;                                                         
	      tx_done <= 1'b0;                                                           
	    end                                                                          
	  else                                                                           
	    if (read_pointer <= NUMBER_OF_OUTPUT_WORDS-1)    //读指针小于等于发送数据个数-1,如果tx_en(发送使能),读指针递增,发送完成信号为0                             
	      begin                                                                      
	        if (tx_en)                                                                                               
	          begin                                                                  
	            read_pointer <= read_pointer + 1;                                    
	            tx_done <= 1'b0;                                                     
	          end                                                                    
	      end                                                                        
	    else if (read_pointer == NUMBER_OF_OUTPUT_WORDS)                             
	      begin                                                                                                                            
	        tx_done <= 1'b1;                             //如果读指针等于发送数据个数,完成信号为1                                  
	      end                                                                        
	end                                                                              

	assign tx_en = M_AXIS_TREADY && axis_tvalid;      	//读使能信号(从机+主机准备好)
	                                                     
	    //生成数据输出      
	    always @( posedge M_AXIS_ACLK )                  
	    begin                                            
	      if(!M_AXIS_ARESETN)                            
	        begin                                        
	          stream_data_out <= 1;                      
	        end                                          
	      else if (tx_en)
	        begin                                        
	          stream_data_out <= read_pointer + 32'b1;   //定义数据为指针+1
	        end                                          
	    end     

	/* 
		实现用户逻辑
	*/

	endmodule

1.2 源码展示

`timescale 1 ns / 1 ps

	module axis_m_v1_0_M00_AXIS #
	(
		// Users to add parameters here

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

		// Width of S_AXIS address bus. The slave accepts the read and write addresses of width C_M_AXIS_TDATA_WIDTH.
		parameter integer C_M_AXIS_TDATA_WIDTH	= 32,
		// Start count is the number of clock cycles the master will wait before initiating/issuing any transaction.
		parameter integer C_M_START_COUNT	= 32
	)
	(
		// Users to add ports here

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

		// Global ports
		input wire  M_AXIS_ACLK,
		// 
		input wire  M_AXIS_ARESETN,
		// Master Stream Ports. TVALID indicates that the master is driving a valid transfer, A transfer takes place when both TVALID and TREADY are asserted. 
		output wire  M_AXIS_TVALID,
		// TDATA is the primary payload that is used to provide the data that is passing across the interface from the master.
		output wire [C_M_AXIS_TDATA_WIDTH-1 : 0] M_AXIS_TDATA,
		// TSTRB is the byte qualifier that indicates whether the content of the associated byte of TDATA is processed as a data byte or a position byte.
		output wire [(C_M_AXIS_TDATA_WIDTH/8)-1 : 0] M_AXIS_TSTRB,
		// TLAST indicates the boundary of a packet.
		output wire  M_AXIS_TLAST,
		// TREADY indicates that the slave can accept a transfer in the current cycle.
		input wire  M_AXIS_TREADY
	);
	// Total number of output data                                                 
	localparam NUMBER_OF_OUTPUT_WORDS = 8;                                               
	                                                                                     
	// function called clogb2 that returns an integer which has the                      
	// value of the ceiling of the log base 2.                                           
	function integer clogb2 (input integer bit_depth);                                   
	  begin                                                                              
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                                      
	      bit_depth = bit_depth >> 1;                                                    
	  end                                                                                
	endfunction                                                                          
	                                                                                     
	// WAIT_COUNT_BITS is the width of the wait counter.                                 
	localparam integer WAIT_COUNT_BITS = clogb2(C_M_START_COUNT-1);                      
	                                                                                     
	// bit_num gives the minimum number of bits needed to address 'depth' size of FIFO.  
	localparam bit_num  = clogb2(NUMBER_OF_OUTPUT_WORDS);                                
	                                                                                     
	// Define the states of state machine                                                
	// The control state machine oversees the writing of input streaming data to the FIFO,
	// and outputs the streaming data from the FIFO                                      
	parameter [1:0] IDLE = 2'b00,        // This is the initial/idle state               
	                                                                                     
	                INIT_COUNTER  = 2'b01, // This state initializes the counter, once   
	                                // the counter reaches C_M_START_COUNT count,        
	                                // the state machine changes state to SEND_STREAM     
	                SEND_STREAM   = 2'b10; // In this state the                          
	                                     // stream data is output through M_AXIS_TDATA   
	// State variable                                                                    
	reg [1:0] mst_exec_state;                                                            
	// Example design FIFO read pointer                                                  
	reg [bit_num-1:0] read_pointer;                                                      

	// AXI Stream internal signals
	//wait counter. The master waits for the user defined number of clock cycles before initiating a transfer.
	reg [WAIT_COUNT_BITS-1 : 0] 	count;
	//streaming data valid
	wire  	axis_tvalid;
	//streaming data valid delayed by one clock cycle
	reg  	axis_tvalid_delay;
	//Last of the streaming data 
	wire  	axis_tlast;
	//Last of the streaming data delayed by one clock cycle
	reg  	axis_tlast_delay;
	//FIFO implementation signals
	reg [C_M_AXIS_TDATA_WIDTH-1 : 0] 	stream_data_out;
	wire  	tx_en;
	//The master has issued all the streaming data stored in FIFO
	reg  	tx_done;


	// I/O Connections assignments

	assign M_AXIS_TVALID	= axis_tvalid_delay;
	assign M_AXIS_TDATA	= stream_data_out;
	assign M_AXIS_TLAST	= axis_tlast_delay;
	assign M_AXIS_TSTRB	= {(C_M_AXIS_TDATA_WIDTH/8){1'b1}};


	// Control state machine implementation                             
	always @(posedge M_AXIS_ACLK)                                             
	begin                                                                     
	  if (!M_AXIS_ARESETN)                                                    
	  // Synchronous reset (active low)                                       
	    begin                                                                 
	      mst_exec_state <= IDLE;                                             
	      count    <= 0;                                                      
	    end                                                                   
	  else                                                                    
	    case (mst_exec_state)                                                 
	      IDLE:                                                               
	        // The slave starts accepting tdata when                          
	        // there tvalid is asserted to mark the                           
	        // presence of valid streaming data                               
	        //if ( count == 0 )                                                 
	        //  begin                                                           
	            mst_exec_state  <= INIT_COUNTER;                              
	        //  end                                                             
	        //else                                                              
	        //  begin                                                           
	        //    mst_exec_state  <= IDLE;                                      
	        //  end                                                             
	                                                                          
	      INIT_COUNTER:                                                       
	        // The slave starts accepting tdata when                          
	        // there tvalid is asserted to mark the                           
	        // presence of valid streaming data                               
	        if ( count == C_M_START_COUNT - 1 )                               
	          begin                                                           
	            mst_exec_state  <= SEND_STREAM;                               
	          end                                                             
	        else                                                              
	          begin                                                           
	            count <= count + 1;                                           
	            mst_exec_state  <= INIT_COUNTER;                              
	          end                                                             
	                                                                          
	      SEND_STREAM:                                                        
	        // The example design streaming master functionality starts       
	        // when the master drives output tdata from the FIFO and the slave
	        // has finished storing the S_AXIS_TDATA                          
	        if (tx_done)                                                      
	          begin                                                           
	            mst_exec_state <= IDLE;                                       
	          end                                                             
	        else                                                              
	          begin                                                           
	            mst_exec_state <= SEND_STREAM;                                
	          end                                                             
	    endcase                                                               
	end                                                                       


	//tvalid generation
	//axis_tvalid is asserted when the control state machine's state is SEND_STREAM and
	//number of output streaming data is less than the NUMBER_OF_OUTPUT_WORDS.
	assign axis_tvalid = ((mst_exec_state == SEND_STREAM) && (read_pointer < NUMBER_OF_OUTPUT_WORDS));
	                                                                                               
	// AXI tlast generation                                                                        
	// axis_tlast is asserted number of output streaming data is NUMBER_OF_OUTPUT_WORDS-1          
	// (0 to NUMBER_OF_OUTPUT_WORDS-1)                                                             
	assign axis_tlast = (read_pointer == NUMBER_OF_OUTPUT_WORDS-1);                                
	                                                                                               
	                                                                                               
	// Delay the axis_tvalid and axis_tlast signal by one clock cycle                              
	// to match the latency of M_AXIS_TDATA                                                        
	always @(posedge M_AXIS_ACLK)                                                                  
	begin                                                                                          
	  if (!M_AXIS_ARESETN)                                                                         
	    begin                                                                                      
	      axis_tvalid_delay <= 1'b0;                                                               
	      axis_tlast_delay <= 1'b0;                                                                
	    end                                                                                        
	  else                                                                                         
	    begin                                                                                      
	      axis_tvalid_delay <= axis_tvalid;                                                        
	      axis_tlast_delay <= axis_tlast;                                                          
	    end                                                                                        
	end                                                                                            


	//read_pointer pointer

	always@(posedge M_AXIS_ACLK)                                               
	begin                                                                            
	  if(!M_AXIS_ARESETN)                                                            
	    begin                                                                        
	      read_pointer <= 0;                                                         
	      tx_done <= 1'b0;                                                           
	    end                                                                          
	  else                                                                           
	    if (read_pointer <= NUMBER_OF_OUTPUT_WORDS-1)                                
	      begin                                                                      
	        if (tx_en)                                                               
	          // read pointer is incremented after every read from the FIFO          
	          // when FIFO read signal is enabled.                                   
	          begin                                                                  
	            read_pointer <= read_pointer + 1;                                    
	            tx_done <= 1'b0;                                                     
	          end                                                                    
	      end                                                                        
	    else if (read_pointer == NUMBER_OF_OUTPUT_WORDS)                             
	      begin                                                                      
	        // tx_done is asserted when NUMBER_OF_OUTPUT_WORDS numbers of streaming data
	        // has been out.                                                         
	        tx_done <= 1'b1;                                                         
	      end                                                                        
	end                                                                              


	//FIFO read enable generation 

	assign tx_en = M_AXIS_TREADY && axis_tvalid;   
	                                                     
	    // Streaming output data is read from FIFO       
	    always @( posedge M_AXIS_ACLK )                  
	    begin                                            
	      if(!M_AXIS_ARESETN)                            
	        begin                                        
	          stream_data_out <= 1;                      
	        end                                          
	      else if (tx_en)// && M_AXIS_TSTRB[byte_index]  
	        begin                                        
	          stream_data_out <= read_pointer + 32'b1;   
	        end                                          
	    end                                              

	// Add user logic here

	// User logic ends

	endmodule

2、AXIS从机部分

1.1 中文注释


`timescale 1 ns / 1 ps

	module axis_s_v1_0_S00_AXIS #
	(
		/*
			用户可以自定义参数
		*/

		//AXIS数据位宽
		parameter integer C_S_AXIS_TDATA_WIDTH	= 32
	)
	(
		/*
			用户可以在此自定义端口
		*/

		input wire  S_AXIS_ACLK,                    			  //时钟信号
		input wire  S_AXIS_ARESETN,               			      //复位信号
		output wire  S_AXIS_TREADY,              			      //ready信号,代表从机准备好了
		input wire [C_S_AXIS_TDATA_WIDTH-1 : 0] S_AXIS_TDATA,     //数据信号
		input wire [(C_S_AXIS_TDATA_WIDTH/8)-1 : 0] S_AXIS_TSTRB, //数据修饰符,辨别字节类型	
		input wire  S_AXIS_TLAST,								  //last信号,拉高代表是传输中的最后一个字节
		input wire  S_AXIS_TVALID                                 //ready信号,代表从机准备好了
	);

	//函数:以2为低求对数,用于计算位宽
	function integer clogb2 (input integer bit_depth);
	  begin
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
	      bit_depth = bit_depth >> 1;
	  end
	endfunction

	localparam NUMBER_OF_INPUT_WORDS  = 8;                      //输入数据个数
	localparam bit_num  = clogb2(NUMBER_OF_INPUT_WORDS-1);      //输入数据的位宽

	//状态机定义
	parameter [1:0] IDLE = 1'b0,                                //初始状态
	                WRITE_FIFO  = 1'b1;                         //读状态

	wire  	axis_tready;                                        //ready信号

	reg mst_exec_state;                                         //状态寄存器

	genvar byte_index;                                          //字节索引

	wire fifo_wren;                                             //FIFO写使能
	reg fifo_full_flag;                                         //FIFO满标志
	reg [bit_num-1:0] write_pointer;                            //FIFO写指针
	reg writes_done;                                            //写满标志

	assign S_AXIS_TREADY	= axis_tready;                    

	//状态机
	always @(posedge S_AXIS_ACLK) 
	begin  
	  if (!S_AXIS_ARESETN) 
	    begin
	      mst_exec_state <= IDLE;
	    end  
	  else
	    case (mst_exec_state)
	      IDLE: 
	          if (S_AXIS_TVALID)
	            begin
	              mst_exec_state <= WRITE_FIFO;
	            end
	          else
	            begin
	              mst_exec_state <= IDLE;
	            end
	      WRITE_FIFO: 
	        if (writes_done)
	          begin
	            mst_exec_state <= IDLE;
	          end
	        else
	          begin
	            mst_exec_state <= WRITE_FIFO;
	          end

	    endcase
	end

	//ready信号赋值,写状态+读指针写于等于接收数据总个数
	assign axis_tready = ((mst_exec_state == WRITE_FIFO) && (write_pointer <= NUMBER_OF_INPUT_WORDS-1));

	//写指针,写完成信号
	always@(posedge S_AXIS_ACLK)
	begin
	  if(!S_AXIS_ARESETN)
	    begin
	      write_pointer <= 0;
	      writes_done <= 1'b0;
	    end  
	  else
	    if (write_pointer <= NUMBER_OF_INPUT_WORDS-1)
	      begin
	        if (fifo_wren)
	          begin
	            write_pointer <= write_pointer + 1;
	            writes_done <= 1'b0;
	          end
	          if ((write_pointer == NUMBER_OF_INPUT_WORDS-1)|| S_AXIS_TLAST)
	            begin
	              writes_done <= 1'b1;
	            end
	      end  
	end

	//FIFO写使能信号
	assign fifo_wren = S_AXIS_TVALID && axis_tready;

	//例化4个宽为8,深度为8的二维数组stream_data_fifo,用来充当FIFO,每个FIFO依次写入数据的0-7;8-15;16-23;24-31位
	generate 
	  for(byte_index=0; byte_index<= (C_S_AXIS_TDATA_WIDTH/8-1); byte_index=byte_index+1)
	  begin:FIFO_GEN

	    reg  [(C_S_AXIS_TDATA_WIDTH/4)-1:0] stream_data_fifo [0 : NUMBER_OF_INPUT_WORDS-1];

		//写入FIFO数据
	    always @( posedge S_AXIS_ACLK )
	    begin
	      if (fifo_wren)// && S_AXIS_TSTRB[byte_index])
	        begin
	          stream_data_fifo[write_pointer] <= S_AXIS_TDATA[(byte_index*8+7) -: 8];
	        end  
	    end  
	  end		
	endgenerate

	/* 
		实现用户逻辑
	*/

	endmodule

1.2 源码展示


`timescale 1 ns / 1 ps

	module axis_s_v1_0_S00_AXIS #
	(
		// Users to add parameters here

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

		// AXI4Stream sink: Data Width
		parameter integer C_S_AXIS_TDATA_WIDTH	= 32
	)
	(
		// Users to add ports here

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

		// AXI4Stream sink: Clock
		input wire  S_AXIS_ACLK,
		// AXI4Stream sink: Reset
		input wire  S_AXIS_ARESETN,
		// Ready to accept data in
		output wire  S_AXIS_TREADY,
		// Data in
		input wire [C_S_AXIS_TDATA_WIDTH-1 : 0] S_AXIS_TDATA,
		// Byte qualifier
		input wire [(C_S_AXIS_TDATA_WIDTH/8)-1 : 0] S_AXIS_TSTRB,
		// Indicates boundary of last packet
		input wire  S_AXIS_TLAST,
		// Data is in valid
		input wire  S_AXIS_TVALID
	);
	// function called clogb2 that returns an integer which has the 
	// value of the ceiling of the log base 2.
	function integer clogb2 (input integer bit_depth);
	  begin
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
	      bit_depth = bit_depth >> 1;
	  end
	endfunction

	// Total number of input data.
	localparam NUMBER_OF_INPUT_WORDS  = 8;
	// bit_num gives the minimum number of bits needed to address 'NUMBER_OF_INPUT_WORDS' size of FIFO.
	localparam bit_num  = clogb2(NUMBER_OF_INPUT_WORDS-1);
	// Define the states of state machine
	// The control state machine oversees the writing of input streaming data to the FIFO,
	// and outputs the streaming data from the FIFO
	parameter [1:0] IDLE = 1'b0,        // This is the initial/idle state 

	                WRITE_FIFO  = 1'b1; // In this state FIFO is written with the
	                                    // input stream data S_AXIS_TDATA 
	wire  	axis_tready;
	// State variable
	reg mst_exec_state;  
	// FIFO implementation signals
	genvar byte_index;     
	// FIFO write enable
	wire fifo_wren;
	// FIFO full flag
	reg fifo_full_flag;
	// FIFO write pointer
	reg [bit_num-1:0] write_pointer;
	// sink has accepted all the streaming data and stored in FIFO
	  reg writes_done;
	// I/O Connections assignments

	assign S_AXIS_TREADY	= axis_tready;
	// Control state machine implementation
	always @(posedge S_AXIS_ACLK) 
	begin  
	  if (!S_AXIS_ARESETN) 
	  // Synchronous reset (active low)
	    begin
	      mst_exec_state <= IDLE;
	    end  
	  else
	    case (mst_exec_state)
	      IDLE: 
	        // The sink starts accepting tdata when 
	        // there tvalid is asserted to mark the
	        // presence of valid streaming data 
	          if (S_AXIS_TVALID)
	            begin
	              mst_exec_state <= WRITE_FIFO;
	            end
	          else
	            begin
	              mst_exec_state <= IDLE;
	            end
	      WRITE_FIFO: 
	        // When the sink has accepted all the streaming input data,
	        // the interface swiches functionality to a streaming master
	        if (writes_done)
	          begin
	            mst_exec_state <= IDLE;
	          end
	        else
	          begin
	            // The sink accepts and stores tdata 
	            // into FIFO
	            mst_exec_state <= WRITE_FIFO;
	          end

	    endcase
	end
	// AXI Streaming Sink 
	// 
	// The example design sink is always ready to accept the S_AXIS_TDATA  until
	// the FIFO is not filled with NUMBER_OF_INPUT_WORDS number of input words.
	assign axis_tready = ((mst_exec_state == WRITE_FIFO) && (write_pointer <= NUMBER_OF_INPUT_WORDS-1));

	always@(posedge S_AXIS_ACLK)
	begin
	  if(!S_AXIS_ARESETN)
	    begin
	      write_pointer <= 0;
	      writes_done <= 1'b0;
	    end  
	  else
	    if (write_pointer <= NUMBER_OF_INPUT_WORDS-1)
	      begin
	        if (fifo_wren)
	          begin
	            // write pointer is incremented after every write to the FIFO
	            // when FIFO write signal is enabled.
	            write_pointer <= write_pointer + 1;
	            writes_done <= 1'b0;
	          end
	          if ((write_pointer == NUMBER_OF_INPUT_WORDS-1)|| S_AXIS_TLAST)
	            begin
	              // reads_done is asserted when NUMBER_OF_INPUT_WORDS numbers of streaming data 
	              // has been written to the FIFO which is also marked by S_AXIS_TLAST(kept for optional usage).
	              writes_done <= 1'b1;
	            end
	      end  
	end

	// FIFO write enable generation
	assign fifo_wren = S_AXIS_TVALID && axis_tready;

	// FIFO Implementation
	generate 
	  for(byte_index=0; byte_index<= (C_S_AXIS_TDATA_WIDTH/8-1); byte_index=byte_index+1)
	  begin:FIFO_GEN

	    reg  [(C_S_AXIS_TDATA_WIDTH/4)-1:0] stream_data_fifo [0 : NUMBER_OF_INPUT_WORDS-1];

	    // Streaming input data is stored in FIFO

	    always @( posedge S_AXIS_ACLK )
	    begin
	      if (fifo_wren)// && S_AXIS_TSTRB[byte_index])
	        begin
	          stream_data_fifo[write_pointer] <= S_AXIS_TDATA[(byte_index*8+7) -: 8];
	        end  
	    end  
	  end		
	endgenerate

	// Add user logic here

	// User logic ends

	endmodule

四、仿真测试

1、top

module top(
    input clk,
    input rst_n
    );

    wire [31:0] axis_tdata;
    wire [3:0]  axis_tstrb;
    wire        axis_tlast;
    wire        axis_tready;
    wire        axis_tvalid;

    axis_m_0 axis_m_u0
    (
     .m00_axis_tdata      (axis_tdata),
     .m00_axis_tstrb      (axis_tstrb), 
     .m00_axis_tlast      (axis_tlast),
     .m00_axis_tvalid     (axis_tvalid),
     .m00_axis_tready     (axis_tready),
     .m00_axis_aclk       (clk),
     .m00_axis_aresetn    (rst_n)
    );

    axis_s_0 axis_s_u0
    (
     .s00_axis_tdata      (axis_tdata),
     .s00_axis_tstrb      (axis_tstrb), 
     .s00_axis_tlast      (axis_tlast),
     .s00_axis_tvalid     (axis_tvalid),
     .s00_axis_tready     (axis_tready),
     .s00_axis_aclk       (clk),
     .s00_axis_aresetn    (rst_n)
    );

endmodule

2、tb

`timescale 1ns / 1ps

module tb_top();
    reg clk,rst_n;

initial begin
    clk = 0;
    rst_n = 1;
#10
    rst_n = 0;
#10
    rst_n = 1;
end

always #5 clk <= ~clk;

top top_u1(.clk(clk),
           .rst_n(rst_n));

endmodule

3、结果

AXI-Stream协议详解(3)—— AXI4-Stream IP核原理分析_第14张图片

你可能感兴趣的:(AMBA总线协议,fpga,AXI-S,zynq)