带你快速入门AXI4总线--AXI4-Lite篇(3)----XILINX AXI4-Lite接口IP源码仿真分析

本文转载自:孤独的单刀的CSDN博客

写在前面

        在带你快速入门AXI4总线--AXI4-Lite篇(2)----XILINX AXI4-Lite接口IP源码仿真分析(Slave接口)中我们已经对Slave接口的代码做了分析,并观察了其仿真波形,在本文我们将生成AXI4-Lite_Master接口的IP来对其解析。

1、调用IP

        具体步骤不讲,请参看Slave接口的文章,只需要将IP的接口类型改为Master即可,其他一致。

2、Master接口的源码分析

        打开生成的源码(注意:我删除了源码的注释,不然太长了。再优化了一下格式,主要是对齐。顺便再吐槽一下CSDN不能折叠代码):

        代码较长,我将其分成NO.1-13共13个部分来进行讲解。只讲大体思路,其他内容请看代码注释。

NO.1:

`timescale 1 ns / 1 ps
//NO.1--------------------------------输入输出端口-------------------------------------------
	module myip_axi_lite_master_v1_0_M00_AXI #
	(
		parameter 			C_M_START_DATA_VALUE		= 32'hAA000000,	//初始写入数据的值
		parameter  			C_M_TARGET_SLAVE_BASE_ADDR	= 32'h40000000,	//写入地址的基地址
		parameter integer 	C_M_AXI_ADDR_WIDTH			= 32,			//地址位宽
		parameter integer 	C_M_AXI_DATA_WIDTH			= 32,			//数据位宽
		parameter integer 	C_M_TRANSACTIONS_NUM		= 4				//传输的最大事务个数
	)
	(
//工具人型信号	
		input 	wire  								INIT_AXI_TXN,		//传输开始信号
		output 	reg  								ERROR,				//错误
		output 	wire  								TXN_DONE,			//传输完成
//全局信号		
		input 	wire  								M_AXI_ACLK,
		input 	wire  								M_AXI_ARESETN,
//写地址通道信号		
		output 	wire [C_M_AXI_ADDR_WIDTH-1 : 0] 	M_AXI_AWADDR,
		output 	wire [2 : 0] 						M_AXI_AWPROT,
		output 	wire  								M_AXI_AWVALID,
		input 	wire  								M_AXI_AWREADY,
//写数据通道信号				
		output 	wire [C_M_AXI_DATA_WIDTH-1 : 0]		M_AXI_WDATA,
		output 	wire [C_M_AXI_DATA_WIDTH/8-1 : 0] 	M_AXI_WSTRB,
		output 	wire  								M_AXI_WVALID,
		input 	wire  								M_AXI_WREADY,
//写响应通道信号		
		input 	wire [1 : 0]	 					M_AXI_BRESP,
		input 	wire  								M_AXI_BVALID,
		output 	wire  								M_AXI_BREADY,
//读地址通道信号		
		output 	wire [C_M_AXI_ADDR_WIDTH-1 : 0] 	M_AXI_ARADDR,
		output 	wire [2 : 0] 						M_AXI_ARPROT,
		output 	wire  								M_AXI_ARVALID,
		input 	wire  								M_AXI_ARREADY,
//读数据通道信号	
		input 	wire [C_M_AXI_DATA_WIDTH-1 : 0] 	M_AXI_RDATA,
		input 	wire [1 : 0] 						M_AXI_RRESP,
		input 	wire  								M_AXI_RVALID,
		output 	wire  								M_AXI_RREADY
	);

 这部分主要是模块端口及参数例化。               

参数例化:初始写入数据的值;写入地址的基地址;数据位宽32位;地址位宽4位;传输的最大事务个数

模块端口:

AXI4-Lite协议的端口。不记得可以看这里:带你快速入门AXI4总线--AXI4-Lite篇(1)----AXI4-Lite总线

其他工具人型信号

NO.2:

//NO.2--------------------------------以2为底取对数的function-------------------------------------------	
	//以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 TRANS_NUM_BITS = clogb2(C_M_TRANSACTIONS_NUM-1);		//计算最大传输事务计数的位宽

 这部分主要是写了一个求2为底的对数的FUNCTION,这个function一般用来计算变量的位宽;计算了传输事务最大个数的位宽。

NO.3:

//NO.3--------------------------------reg、wire、参数定义-------------------------------------------
	//状态机状态定义
	parameter [1:0] IDLE 			= 2'b00, 			//初始状态
					INIT_WRITE 		= 2'b01, 			//写事务状态
					INIT_READ 		= 2'b10, 			//读事务状态
					INIT_COMPARE 	= 2'b11; 			//比较读、写结果状态
	//axi总线信号
	reg [1:0] 						mst_exec_state;
	reg  							axi_awvalid;
	reg  							axi_wvalid;
	reg  							axi_arvalid;
	reg  							axi_rready;
	reg  							axi_bready;
	reg [C_M_AXI_ADDR_WIDTH-1 : 0] 	axi_awaddr;
	reg [C_M_AXI_DATA_WIDTH-1 : 0] 	axi_wdata;
	reg [C_M_AXI_ADDR_WIDTH-1 : 0] 	axi_araddr;
	
	wire  							write_resp_error;	//写响应错误
	wire  							read_resp_error;	//读响应错误
	reg  							start_single_write;	//单次写事务触发信号
	reg  							start_single_read;	//单次读事务触发信号
	reg  							write_issued;		//拉高表示此时正在发起进行写事务
	reg  							read_issued;		//拉高表示此时正在发起进行读事务
	reg  							writes_done;		//所有写事务完成
	reg  							reads_done;			//所有读事务完成
	reg  							error_reg;			//错误寄存器,拉高表示此模块读写有误
	reg [TRANS_NUM_BITS : 0] 		write_index;		//写事务计数
	reg [TRANS_NUM_BITS : 0] 		read_index;			//读事务计数
	reg [C_M_AXI_DATA_WIDTH-1 : 0] 	expected_rdata;		//理论上应该读取的数据(即被写入的数据)
	reg  							compare_done;		//比较读写结果完成
	reg  							read_mismatch;		//读、写结果不匹配
	reg  							last_write;			//最后一次写事务
	reg  							last_read;			//最后一次读事务
	reg  							init_txn_ff;		//传输开始信号打1拍
	reg  							init_txn_ff2;		//传输开始信号打2拍
	reg  							init_txn_edge;		//没用到的信号
	wire  							init_txn_pulse;		//传输开始信号的上升沿捕获信号

这部分定义了一系列的变量,具体作用请看注释。

NO.4:

//NO.4--------------------------------部分赋值模块-------------------------------------------
	assign M_AXI_AWADDR	  = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr;		//写地址 = 基地址 + 偏移地址
	assign M_AXI_WDATA	  = axi_wdata;
	assign M_AXI_AWPROT	  = 3'b000;											//保护类型
	assign M_AXI_AWVALID  = axi_awvalid;
	assign M_AXI_WVALID	  = axi_wvalid;
	assign M_AXI_WSTRB	  = 4'b1111;										//写入数据全部选中
	assign M_AXI_BREADY	  = axi_bready;
	assign M_AXI_ARADDR	  = C_M_TARGET_SLAVE_BASE_ADDR + axi_araddr;		//读地址 = 基地址 + 偏移地址
	assign M_AXI_ARVALID  = axi_arvalid;
	assign M_AXI_ARPROT	  = 3'b001;											//保护类型
	assign M_AXI_RREADY	  = axi_rready;
	assign TXN_DONE		  = compare_done;									//比较完成信号赋值给传输完成信号

 这部分对部分变量进行了赋值,避免了输出信号的直接操作。

NO.5:

//NO.5--------------------------------对传输开始信号打拍并求其上升沿(异步信号)-------------------------------------------
	assign init_txn_pulse = (!init_txn_ff2) && init_txn_ff;					//求上升沿
	
	always @(posedge M_AXI_ACLK)										      
	  begin                                                                        
	    // Initiates AXI transaction delay    
	    if (M_AXI_ARESETN == 0 )                                                   
	      begin                                                                    
	        init_txn_ff <= 1'b0;                                                   
	        init_txn_ff2 <= 1'b0;                                                   
	      end                                                                               
	    else                                                                       
	      begin  
	        init_txn_ff <= INIT_AXI_TXN;
	        init_txn_ff2 <= init_txn_ff;                                                                 
	      end                                                                      
	  end

 这部分对INIT_AXI_TXN这个信号进行了上升沿捕捉,这是控制主机模块开始写事务的使能信号,由外部传入,属于异步信号。

NO.6:

//NO.6--------------------------------状态机实现流程控制-------------------------------------------                       
	  always @ ( posedge M_AXI_ACLK)                                                    
	  begin                                                                             
	    if (M_AXI_ARESETN == 1'b0)                                                     
	      begin                                                                                  
	        mst_exec_state  <= IDLE;                                            
	        start_single_write <= 1'b0;                                                 
	        write_issued  <= 1'b0;                                                      
	        start_single_read  <= 1'b0;                                                 
	        read_issued   <= 1'b0;                                                      
	        compare_done  <= 1'b0;                                                      
	        ERROR <= 1'b0;
	      end                                                                           
	    else                                                                            
	      begin                                                                                                                                  
	        case (mst_exec_state)                                                       	                                                                                    
	          IDLE:               						//初始状态                                               
	            if ( init_txn_pulse == 1'b1 )    		//传输开始                                 
	              begin                             	                                    
	                mst_exec_state  <= INIT_WRITE;  	                                    
	                ERROR <= 1'b0;	
	                compare_done <= 1'b0;	
	              end                               	                                    
	            else                                	                                    
	              begin                             	                                    
	                mst_exec_state  <= IDLE;        	                            
	              end                               	                                    	                                                                                    
	          INIT_WRITE:                           	//写传输事务状态  	                                                                                        
	            if (writes_done)   						// 全部4次写事务完成                                                        
	              begin                             	                                    
	                mst_exec_state <= INIT_READ;		//跳转到读事务状态	                                  
	              end                                                                   
	            else                                                                    
	              begin                                                                 
	                mst_exec_state  <= INIT_WRITE;                                      
						//空闲状态下生成start_single_write、write_issued																						
	                  if (~axi_awvalid && ~axi_wvalid && ~M_AXI_BVALID && ~last_write && ~start_single_write && ~write_issued)
	                    begin                                                           
	                      start_single_write <= 1'b1;                                   
	                      write_issued  <= 1'b1;   		//拉高表示此时正在发起进行写事务                                     
	                    end                                                             
	                  else if (axi_bready)                                              
	                    begin                                                           
	                      write_issued  <= 1'b0;  		//写入响应后表示写事务可以结束(有1个周期的时延)                                      
	                    end                                                             
	                  else                                                              
	                    begin                                                           
	                      start_single_write <= 1'b0; 	//其他情况不进行写事务     
	                    end                                                             
	              end                                                                   	                                                                                    
	          INIT_READ:                                 //读传输事务状态                                                                                    
	             if (reads_done)                         // 全部4次读事务完成                                
	               begin                                                                                     
	                 mst_exec_state <= INIT_COMPARE;     //跳转到比较读写结果状态	                                
	               end                                                                  
	             else                                                                   
	               begin                                                                
	                 mst_exec_state  <= INIT_READ;  
					//start_single_read、read_issued	
	                 if (~axi_arvalid && ~M_AXI_RVALID && ~last_read && ~start_single_read && ~read_issued)
	                   begin                                                            
	                     start_single_read <= 1'b1;		                                   
	                     read_issued  <= 1'b1;      	//拉高表示此时正在发起进行读事务                                      
	                   end                                                              
	                 else if (axi_rready) 				//读取响应后表示读事务可以结束(有1个周期的时延)                                              
	                   begin                                                            
	                     read_issued  <= 1'b0;                                          
	                   end                                                              
	                 else                                                               
	                   begin                                                            
	                     start_single_read <= 1'b0; 	//其他情况不进行读事务        
	                   end                                                              
	               end                                                                  	                                                                                    
	           INIT_COMPARE: 							//比较读、写数据结果                                                           
	             begin
	                 mst_exec_state <= IDLE; 				 
	                 ERROR <= error_reg;                                   
	                 compare_done <= 1'b1;                                              
	             end                                                                  
	           default :                                                                
	             begin                                                                  
	               mst_exec_state  <= IDLE;                                     
	             end                                                                    
	        endcase                                                                     
	    end                                                                             
	  end

这个部分使用一个状态机实现了整个代码的流程控制,状态流程大致如下:

1.png

 整个流程想实现的功能是:首先进入初始状态,等外部发送可以开始进行传输信号后(init_txn_pulse),进入写事务状态。在写事务状态如果完成了所有的写事务(4个),则跳转到读事务状态。若没有完成则继续保持在该状态。在读事务状态如果完成了所有的读事务(4个),则跳转到比较状态。若没有完成则继续保持在该状态。在比较状态,对读、写数据进行比较,观察是否由错误。

NO.7:

//NO.7--------------------------------写地址通道-------------------------------------------
//写地址有效	  
	  always @(posedge M_AXI_ACLK)										      
	  begin                                                                            
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                   
	      begin                                                                    
	        axi_awvalid <= 1'b0;                                                   
	      end                                                                                
	    else                                                                       
	      begin                                                                    
	        if (start_single_write)                    //单次写入有效                                            
	          begin                                                                
	            axi_awvalid <= 1'b1;                                               
	          end                                                                  
	        else if (M_AXI_AWREADY && axi_awvalid)  	//写地址通道握手完成                               
	          begin                                                                
	            axi_awvalid <= 1'b0;                                               
	          end                                                                  
	      end                                                                      
	  end                                                                          
//写次数计数                                                                                                                                                                                                                      
	  always @(posedge M_AXI_ACLK)                                                 
	  begin                                                                        
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                   
	      begin                                                                    
	        write_index <= 0;                                                      
	      end                                                                                                                     
	    else if (start_single_write)                                               
	      begin                                                                    
	        write_index <= write_index + 1;                                        
	      end                                                                      
	  end
//写地址                                        
	  always @(posedge M_AXI_ACLK)                                  
	      begin                                                     
	        if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                
	          begin                                                 
	            axi_awaddr <= 0;                                    
	          end                                                                              
	        else if (M_AXI_AWREADY && axi_awvalid)                  
	          begin                                                 
	            axi_awaddr <= axi_awaddr + 32'h00000004;            
	                                                                
	          end                                                   
	      end

这个部分主要对三个信号赋值:

写地址有效信号axi_awvalid:当单次写事务使能信号有效时,拉高axi_awvalid。当完成握手后,拉低axi_awvalid。

写次数计数信号write_index:每当单次写事务使能信号有效时(表示进行了一次写事务),其值+1,用来统计写事务的次数。

写地址信号axi_awaddr:每当握手完成后,其值加4。即两次写事务的写入地址相差4

NO.8:

//NO.8--------------------------------写数据通道-------------------------------------------
//写数据有效
	   always @(posedge M_AXI_ACLK)                                        
	   begin                                                                         
	     if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                                    
	       begin                                                                     
	         axi_wvalid <= 1'b0;                                                     
	       end                                                                                    
	     else if (start_single_write)                                                
	       begin                                                                     
	         axi_wvalid <= 1'b1;                                                     
	       end                                                                            
	     else if (M_AXI_WREADY && axi_wvalid)                                        
	       begin                                                                     
	        axi_wvalid <= 1'b0;                                                      
	       end                                                                       
	   end
//写数据	   
	  always @(posedge M_AXI_ACLK)                                  
	      begin                                                     
	        if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                
	          begin                                                 
	            axi_wdata <= C_M_START_DATA_VALUE;                  
	          end                                                                                
	        else if (M_AXI_WREADY && axi_wvalid)                    
	          begin                                                 
	            axi_wdata <= C_M_START_DATA_VALUE + write_index;    
	          end                                                   
	        end 	

 这个部分主要对2个信号赋值:

写数据有效信号axi_wvalid:当单次写事务使能信号有效时,拉高axi_wvalid。当完成握手后,拉低axi_wvalid。

写数据信号axi_wdata:每当握手完成后,其值在首次写入值的基础上 + 当前写事务的次数。

NO.9:

//NO.9--------------------------------写响应通道-------------------------------------------
//准备接收写响应
	  always @(posedge M_AXI_ACLK)                                    
	  begin                                                                
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                           
	      begin                                                            
	        axi_bready <= 1'b0;                                            
	      end                                                                                       
	    else if (M_AXI_BVALID && ~axi_bready)                              
	      begin                                                            
	        axi_bready <= 1'b1;                                            
	      end                                                                                               
	    else if (axi_bready)                                               
	      begin                                                            
	        axi_bready <= 1'b0;                                            
	      end                                                                                                     
	    else                                                               
	      axi_bready <= axi_bready;                                        
	  end                                                                  
	  
	assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]);	//判断响应是否有效

 这个部分主要对2个信号赋值:

写响应准备信号axi_bready:当从机发送的响应有效信号有效时,拉高axi_bready。当完成握手后,拉低axi_bready。

写响应错误信号write_resp_error:根据响应值对响应结果进行正确性判断

NO.10:

//读次数计数   
	  always @(posedge M_AXI_ACLK)                                                     
	  begin                                                                            
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                       
	      begin                                                                        
	        read_index <= 0;                                                           
	      end                                                                                                                             
	    else if (start_single_read)                                                    
	      begin                                                                        
	        read_index <= read_index + 1;                                              
	      end                                                                          
	  end                                                                              
//读地址有效	                                                                                                                                                     
	  always @(posedge M_AXI_ACLK)                                                     
	  begin                                                                            
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                       
	      begin                                                                        
	        axi_arvalid <= 1'b0;                                                       
	      end                                                                          
	    else if (start_single_read)                                                    
	      begin                                                                        
	        axi_arvalid <= 1'b1;                                                       
	      end                                                                          
	    else if (M_AXI_ARREADY && axi_arvalid)                                         
	      begin                                                                        
	        axi_arvalid <= 1'b0;                                                       
	      end                                                                          
	  end
//读地址                                            
	  always @(posedge M_AXI_ACLK)                                  
	      begin                                                     
	        if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                
	          begin                                                 
	            axi_araddr <= 0;                                    
	          end                                                                              
	        else if (M_AXI_ARREADY && axi_arvalid)                  
	          begin                                                 
	            axi_araddr <= axi_araddr + 32'h00000004;            
	          end                                                   
	      end

这个部分主要对三个信号赋值:

读地址有效信号axi_arvalid:当单次读事务使能信号有效时,拉高axi_arvalid。当完成握手后,拉低axi_arvalid。

读次数计数信号read_index:每当单次读事务使能信号有效时(表示进行了一次读事务),其值+1,用来统计读事务的次数。

读地址信号axi_araddr:每当握手完成后,其值加4。即两次读事务的读地址相差4(这一点与写地址一致)

NO.11:

//NO.11--------------------------------读数据通道(含读响应)-------------------------------------------
//准备读数据
	  always @(posedge M_AXI_ACLK)                                    
	  begin                                                                 
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                            
	      begin                                                             
	        axi_rready <= 1'b0;                                             
	      end                                                                                        
	    else if (M_AXI_RVALID && ~axi_rready)                               
	      begin                                                             
	        axi_rready <= 1'b1;                                             
	      end                                                               
	    else if (axi_rready)                                                
	      begin                                                             
	        axi_rready <= 1'b0;                                             
	      end                                                               
	  end                                                                   
	                                                                        
	assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]);  //判断响应是否有效
//生成理论上应该读到的值,后续与实际读出的值做对比                                                               
	  always @(posedge M_AXI_ACLK)                                  
	      begin                                                     
	        if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                
	          begin                                                 
	            expected_rdata <= C_M_START_DATA_VALUE;             
	          end                                                                             
	        else if (M_AXI_RVALID && axi_rready)                    
	          begin                                                 
	            expected_rdata <= C_M_START_DATA_VALUE + read_index;
	          end                                                   
	      end

这个部分主要对3个信号赋值:

准备读数据信号axi_rready:当从机告知可以读数据时,拉高axi_rready。当完成握手后,拉低axi_rready。

读响应错误信号read_resp_error:根据响应值对响应结果进行正确性判断            

理论上应该被读出的信号expected_rdata:根据之前写入的信号判断当前理应被读出的信号是多少

NO.12:

//NO.12--------------------------------读、写事务状态判断-------------------------------------------		                                                      
//最后一次写事务																																																									
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      last_write <= 1'b0; 
	    else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY)	//达到最大写次数,且第四次写地址已完成               
	      last_write <= 1'b1;                                                           
	    else                                                                            
	      last_write <= last_write;                                                     
	  end                                                                               	                                                                                                                                   
//全部写事务完成																																																											
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      writes_done <= 1'b0;                                                          	                                                                                                   
	    else if (last_write && M_AXI_BVALID && axi_bready)                              
	      writes_done <= 1'b1;                                                          
	    else                                                                            
	      writes_done <= writes_done;                                                   
	  end                                                                               
//最后一次读事务	                                                                                                                                                 	                                                                                    
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      last_read <= 1'b0;                                                            	                                                                                            
	    else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) )              
	      last_read <= 1'b1;                                                            
	    else                                                                            
	      last_read <= last_read;                                                       
	  end                                                                               
//全部读事务完成	                                                                                                                                                                    
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      reads_done <= 1'b0;                                                           
	    else if (last_read && M_AXI_RVALID && axi_rready)                               
	      reads_done <= 1'b1;                                                           
	    else                                                                            
	      reads_done <= reads_done;                                                     
	    end

这个部分主要对4个信号赋值:

最后一次写事务信号last_write:当开始了最后一次写事务后,拉高此信号。

最后一次读事务信号last_read:当开始了最后一次读事务后,拉高此信号。

全部写事务完成信号writes_done:当完成了所有写事务后,拉高此信号。        

全部读事务完成信号reads_done:当完成了所有读事务后,拉高此信号。 

NO.13:

//NO.13--------------------------------错误判断-------------------------------------------	                                                                                    
//判断读出的数据是否与写入的数据匹配                                                   
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                                         
	    read_mismatch <= 1'b0;                                                          	                                                                                    
	    else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata)) 	//读、写数据不匹配        
	      read_mismatch <= 1'b1;                                                        
	    else                                                                            
	      read_mismatch <= read_mismatch;                                               
	  end                                                                               
//错误判断的三种类型:1、读写不匹配;2、写响应不正确;3、读响应不正确	                                                                                    
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                                         
	      error_reg <= 1'b0;                                                            
	    else if (read_mismatch || write_resp_error || read_resp_error)                  
	      error_reg <= 1'b1;                                                            
	    else                                                                            
	      error_reg <= error_reg;                                                       
	  end                                                                               
 
	endmodule

这个部分主要对2个信号赋值:

判断读、写是否匹配的信号read_mismatch:若读出的数据与写入的数据不一致则拉高此信号。

错误error_reg:若存在以下三种错误则拉高此信号:

1、读写不匹配;

2、写响应不正确;

3、读响应不正确    

3、仿真波形

      代码分析完了,接下来使用Vivado自带的仿真器来进行仿真,观看仿真结果,加深对设计方法的理解:

3.1、AXI4-Lite总线的仿真波形

       我们先把自动生成的仿真信号删除,添加如下的波形信号:

2.png

仿真结果如下:

3.png

  可以看到仿真结果是用这个彩条+字符的形式表示的,非常清晰。这就是添加了AXI VIP IP的效果。

在AXI4-Lite总线上共发生了8个事务:先是连续的4个写事务,接着4个读事务。下面的五个通道分别示意了此时通道内执行的握手操作,将鼠标放在其中任意一处上,会出现如下信息(顺序1、地址40000000等):

4.png

左键点击,会显示具体的事务流程如下:

5.png

   从上图的箭头我们可以直到一次写事务的流程:写地址----写数据----写响应。再看看读事务的流程:

6.png

可以看到读事务的流程:读地址----读数据。

3.2、主机IP的master接口仿真波形

看完了AXI4-Lite总线的仿真波形,我们再看下上面具体解析代码(可以理解为底层驱动)的仿真波形。按如下方法添加:

7.png

将信号按通道或用途做好分类(我还删除了一些不要紧的信号),状态机部分的仿真结果如下:

8.png

不讲解了,直接看图吧。

 AXI4-Lite总线部分的仿真结果如下:

9.png

在上图中,主机先发起了4次写事务,分别往地址h0000_0000、h0000_0004、h0000_0000和h0000_000c中写入了数据aa00_0000、 aa00_0001、aa00_0002和aa00_0003。

接着又发起了4次读事务,分别从之前写入的地址中读取到了数据aa00_0000、 aa00_0001、aa00_0002和aa00_0003(这里图没截好),与写入的一致,证明验证成功。

4、其他

可以看到其实AXI4-Lite总线的使用还是相对比较简单的,只要设计好各个通道的握手时序,以及读写的时序关系就好了。

最新文章

最新文章