AXI总线


参考:IHI0022D_amba_axi_protocol_spec、IHI0051A_amba4_axi4_stream_v1_0_protocol_spec、ug1037-vivado-axi-reference-guide

一、AXI接口特点

AXI-Full:高性能存储映射接口,用于需要指定地址的高速数据传输,支持最大256长度的突发传输;

AXI-Lite:轻量级存储映射接口,用于访问一些低速外设中的寄存器(如读写控制和状态寄存器等),不支持突发传输;

AXI-Stream:流接口,用于高速数据流通信,不需要地址,允许无限制长度的突发传输;

  • 存储映射:如果一个协议是存储映射的,那么主机所发出的会话(无论读或写)就会指定一个地址。这个地址对应于系统存储空间中的一个地址,表明是针对该存储空间的读写操作。
  • 流传输:向某一方向,以固定的速度,连续不断地输出。

突发传输:在一次事务中进行连续的数据传输而无需在每次传输之间重新设置地址和控制信号,AXI-Full的突发传输过程只需要首地址。

二、AXI接口的握手机制

2.1 握手原理

valid:由源端产生,表示当前地址或数据线上的信息是有效的;

ready:由目的端产生,表示已经准备好接收地址、数据以及控制信息;

握手:只有检测到valid和ready同时为高时,才能进行数据传输。这种机制使得主、从机都可以控制信息的传输。

valid和ready需要完全独立,不能相互依赖,避免二者相互等待而造成锁死

2.2 握手机制的三种情形


1.源端在T1之后将VALID拉高,表明INFORMATION信号线上传输的是有效信息;

2.目的端在T2之后将READY拉高,表明已准备好接收数据;

3.此时源端必须保持数据稳定,直到T3时刻进行数据传输;

  • 一旦VALID拉高,源端必须保持其有效状态,直到成功握手
  • 源端不允许等待READY拉高后才拉高VALID(简单来说就是VALID信号是由主机自主产生的);

在这里插入图片描述

  • 允许目的端等待VALID拉高后再拉高READY;
  • 如果拉高了READY,可以在拉高VALID之前取消拉高READY;

在这里插入图片描述

在这里插入图片描述

三、AXI接口的通道

3.1 AXI4-Stream

3.1.1 通道信号

信号含义
TVALIDM置1表示主机发出的数据有效
TREADYS置1表示从机准备好接收数据
TDATA[(8x-1):0]M流数据,宽度是1个整数字节
TLASTM置1表示此时传输的是数据流的最后一个数据,用于指示传输的结束
TSTRB[(x-1):0]M指示TDATA的相关字节是数据字节还是位置字节
TKEEP[(x-1):0]M指示TDATA的相关字节是否可以作为数据流的一部分处理(是否为空字节)
TID[(i-1):0]M区分同一接口上传输的多个数据流(源),用于多机通信
TDEST[(d-1):0]M指示数据流粗略路由信息(目的),用于多机通信
TUSER[(u-1):0]M用户自定义信号,用于多机通信
  • 具有相同TID和TDEST值的传输来自同一流。

3.1.2 数据字节类型

  1. 数据字节:包含在源和目的之间传输的有效信息的一个字节数据;
  2. 占位字节:指示流中数据字节相对位置的字节,是一个占位符,不包含传输的任何相关数据值;
  3. 空字节:不包含任何数据信息或相对位置信息的字节;
  • TKEEP置1表示传输的是数据字节或位置字节,必须传输到目的端;TKEEP置0表示传输的是可以从流中删除的空字节。
  • TKEEP置1后,用TSTRB指示相关字节具体是数据字节还是位置字节。TSTRB置1表示是数据字节,包含有效信息;TSTRB置0表示是位置字节,不包含有效信息。

在这里插入图片描述

3.1.3 流格式

  1. 字节流:传输一些数据字节和空字节,每次握手后可以传输任意数量的数据字节,空字节没有意义,可以从流中插入或删除,如下面两图传输的信息完全相同。
    在这里插入图片描述
  2. 连续对齐流:传输一些数据字节,每个数据包没有空字节或位置字节。
    在这里插入图片描述
  3. 连续不对齐流:数据包的开始和结束位置处有任意数量的位置字节,开始和结束中间全是数据字节。
    在这里插入图片描述
  4. 稀疏流:数据包包含数据字节和位置字节的任意组合,通常大多数字节是数据字节。
    在这里插入图片描述

3.2 AXI4-Lite和AXI4-Full

  • 突发传输不能被提前终止,写突发中可以通过禁用所有写选通来阻止进一步的写,读突发中主机可以丢弃读取的数据,但都必须完成突发中所有的传输。

3.1.1 读地址通道

信号含义AXI-lite是否支持该信号
ARADDR[x:0]M读突发传输中第一次传输的地址(AXI-lite中是每次传输的地址)
ARPROT[2:0]M保护类型,指示传输的安全级别以及传输是数据访问还是指令访问
ARVALIDM置1表明读地址和控制信息有效
ARREADYS置1表示从机可以接收一个读地址和相关控制信息
ARID[x:0]M读地址组信号的标识,指定某些特殊传输任务的顺序
ARLEN[7:0]M突发长度,表示一次突发中与该地址相关的传输数量。实际的突发长度是ARLEN+1,支持 2 n ( n = 0 , 1 , 2...8 ) 2^n(n=0,1,2...8) 2n(n=0,1,2...8)
ARSIZE[2:0]M突发大小,表示一次突发中每次传输的数据大小。SIZE值对应的数据大小为 2 S I Z E 2^{SIZE} 2SIZE字节,该字节数不能超过数据位宽对应的字节数。
ARBURST[1:0]M突发类型,和突发大小共同决定了如何计算突发内每次传输的地址
ARLOCKM锁类型,置0表示正常传输,置1表示独有传输
ARCACHE[3:0]M内存类型
ARQOS[3:0]M服务质量,一般直接置0

突发类型AxBURST:

  1. FIXED:突发中每次传输的地址相同,该类型用于重复访问同一位置;
  2. INCR:突发中每次传输的地址都是前一次传输地址的增量,增量为突发大小,该类型较常用;
  3. WRAP(回环突发):突发传输的最低地址与传输数据的总大小对齐,该最低地址被定义为回环边界。每次传输后,地址以与INCR相同方式增加,但是如果地址增加到(回环边界+传输数据总大小),就会退回到回环边界。该类型突发长度必须是2,4,8,16。

3.1.2 读数据通道

在这里插入图片描述

信号含义AXI-lite中是否支持该信号
RDATA[x:0]S读数据,AXI-Lite的数据宽度是32位或64位,AXI-Full的数据宽度可以是 2 n ( n = 3 , 4...10 ) 2^n(n=3,4...10) 2n(n=3,4...10)
RRESP[1:0]S读响应,指示读传输的状态 ,AXI-Lite不支持EXOKAY状态
RVALIDS置1表示要读的数据有效
RREADYM置1表示主机可以接收读数据
RID[x:0]S读数据传输标识,RID值必须与正在响应的读传输的ARID值匹配
RLASTS置1表示一次读突发中的最后一次传输

响应类型xRESP:

  1. OKAY:正常访问成功;
  2. EXOKAY:独有访问成功;
  3. SLVERR:从机错误,访问已到达从机,但从机希望向主机返回错误时使用 ;
  4. DECERR:解码错误,指示该传输地址上没有从机;

3.1.3 写地址通道

信号含义AXI-lite中是否支持该信号
AWADDR[x:0]M写突发传输中第一次传输的地址(AXI-lite中是每次传输的地址)
AWPROT[2:0]M保护类型,指示传输的安全级别以及传输是数据访问还是指令访问
AWVALIDM置1表示写地址和控制信息有效
AWREADYS置1表示从机可接收一个地址和相关控制信号
AWID[x:0]M写地址组信号的标识,指定某些特殊传输任务的顺序
AWLEN[7:0]M突发长度,表示一次突发中与该地址相关的传输数量。实际的突发长度是AWLEN+1,支持 2 n ( n = 0 , 1 , 2...8 ) 2^n(n=0,1,2...8) 2n(n=0,1,2...8)
AWSIZE[2:0]M突发大小,表示一次突发中每次传输的数据大小。SIZE值对应的数据大小为 2 S I Z E 2^{SIZE} 2SIZE字节,该字节数不能超过数据位宽对应的字节数。
AWBURST[1:0]M突发类型,和突发大小共同决定了如何计算突发内每次传输的地址
AWLOCKM锁类型,置0表示正常传输,置1表示独有传输;
AWCACHE[3:0]M内存类型
AWQOS[3:0]M服务质量,一般直接置0

AXI-Lite所有传输的宽度必须为32位或64位,并且不可修改、不可缓冲,不支持独有传输。

3.1.4 写数据通道

在这里插入图片描述

信号含义AXI-lite中是否支持该信号
WID[x:0]M写数据传输标识,WID值必须与AWID值匹配
WDATA[x:0]M写数据,AXI-Lite的数据宽度是32位或64位,AXI-Full的数据宽度可以是 2 n ( n = 3 , 4...10 ) 2^n(n=3,4...10) 2n(n=3,4...10)
WSTRB[x:0]M写选通,指示内存中要更新的字节的位置,写数据总线每8位有一个选通位,即WSTRB[x]对应于WDATA[(8x+7):8x]
WVALIDM置1表示写数据有效且WSTRB可用
WREADYS置1表示从机可以接收写数据
WLASTM置1表示一次写突发中的最后一次传输
  • WSTRB:主机需确保写选通只对包含有效数据的字节通道为高,当WVALID为低时,写选通可以取任何值;

3.1.5 写响应通道

信号含义AXI-lite中是否支持该信号
BRESP[1:0]S写响应,指示写传输的状态,AXI-Lite不支持EXOKAY状态
BVALIDS置1表示写响应有效
BREADYM置1表示主机可以接收写响应信息。
BID[x:0]S写响应标识,BID值必须与正在响应的写传输的AWID值匹配

3.1.6 通道信号的编码

  • AxSIZE:
    在这里插入图片描述
  • AxBURST:
    在这里插入图片描述
  • AxPROT:
    在这里插入图片描述
  • AxCACHE:
    在这里插入图片描述
  • xRESP:
    在这里插入图片描述

四、时序

突发读:
在这里插入图片描述
重叠突发读:
在这里插入图片描述
突发写:
在这里插入图片描述

五、源码分析

AXI4-Stream

流接口的源码是在AXI-Stream时序的基础上以读写FIFO的场景来描述传输过程。

主机模块

  1. 初始化计数状态:主机在开始传输前会等待一定的时钟周期,等待过后进入发送数据状态;
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
  1. 发送数据状态:将数据有效信号置1,表示将要发送有效的数据。主从机握手后,将发送使能信号置1,开始连续发送自增数据。发送NUMBER_OF_OUTPUT_WORDS个数据后,将tlast置1;
assign axis_tlast = (read_pointer == NUMBER_OF_OUTPUT_WORDS-1);
assign tx_en = M_AXIS_TREADY && axis_tvalid;   
assign axis_tvalid = ((mst_exec_state == SEND_STREAM) && (read_pointer < NUMBER_OF_OUTPUT_WORDS));
	    // 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
  1. 数据开始发送滞后于tvalid拉高1周期,tlast拉高提前于发送最后一个数据1周期。last信号要与最后一个数据对齐,将tvalid和tlast信号延迟一拍;
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
  1. clogb2函数是用移位的方式来计算数据位宽,输入参数是原数据减1
function integer clogb2 (input integer bit_depth);                                   
	  begin                                                                              
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                                      
	      bit_depth = bit_depth >> 1;                                                    
	  end                                                                                
	endfunction 

从机模块

  1. 检测到数据有效后,进入到写FIFO状态;
if (S_AXIS_TVALID)
	            begin
	              mst_exec_state <= WRITE_FIFO;
	            end
	          else
	            begin
	              mst_exec_state <= IDLE;
	            end
  1. 写FIFO状态:将tready信号置1,表示准备好接收数据,主从机握手后,将FIFO写使能置1,然后写指针自增;写入NUMBER_OF_INPUT_WORDS个数据后,将done置1。
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
  1. AXIS数据总线宽度为4字节,使用generate-for语句生成4FIFO块,每个块写入AXIS发送数据的1个字节;
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

S_AXIS_TDATA[7 -: 8]表示S_AXIS_TDATA[7:0]。

AXI4-Lite

主机模块

  1. 在读写状态,链路上无有效信号时,产生start_single脉冲,开始一次读写传输;issued信号指示当前正在进行一次传输,只有issued为0即没有正在进行的传输时,才会产生start_single脉冲;
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;                                        
	                    end                                                             
	                  else                                                              
	                    begin                                                           
	                      start_single_write <= 1'b0; //Negate to generate a pulse      
	                    end 
  1. 每次传输开始后读写索引变量加1,最后一次传输握手后产生last信号,与最后一次传输的数据对齐。最后一次传输响应后,产生读/写完成信号,表示传输过程结束,随后进入下个过程;
always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      last_write <= 1'b0;                                                           
	                                                                                    
	    //The last write should be associated with a write address ready response       
	    else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY)                
	      last_write <= 1'b1;                                                           
	    else                                                                            
	      last_write <= last_write;                                                     
	  end                                                                               
	                                                                                    
	  //Check for last write completion.                                                
	                                                                                    
	  //This logic is to qualify the last write count with the final write              
	  //response. This demonstrates how to confirm that a write has been                
	  //committed.                                                                      
	                                                                                    
	  always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      writes_done <= 1'b0;                                                          
	                                                                                    
	      //The writes_done should be associated with a bready response                 
	    else if (last_write && M_AXI_BVALID && axi_bready)                              
	      writes_done <= 1'b1;                                                          
	    else                                                                            
	      writes_done <= writes_done;                                                   
	  end 
  1. 检测到start_single脉冲后,将主机发出的valid或ready拉高,握手后就拉低,由于lite接口每次传输1个数据,握手信号持续1周期即可; 主机ready信号是等待从机valid信号拉高后才置1;
always @(posedge M_AXI_ACLK)                                                     
	  begin                                                                            
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                       
	      begin                                                                        
	        axi_arvalid <= 1'b0;                                                       
	      end                                                                          
	    //Signal a new read address command is available by user logic                 
	    else if (start_single_read)                                                    
	      begin                                                                        
	        axi_arvalid <= 1'b1;                                                       
	      end                                                                          
	    //RAddress accepted by interconnect/slave (issue of M_AXI_ARREADY by slave)    
	    else if (M_AXI_ARREADY && axi_arvalid)                                         
	      begin                                                                        
	        axi_arvalid <= 1'b0;                                                       
	      end                                                                          
	    // retain the previous value                                                   
	  end 
  1. 每个valid都和数据对应,从机检测到valid就会接收数据,握手后生成下一次的写数据和读写地址偏移量,最后传输的地址实际是基地址+地址偏移量;内存地址以字节为单位,数据位宽为4字节,所以每次传输的地址偏移量加4;
always @(posedge M_AXI_ACLK)                                  
	      begin                                                     
	        if (M_AXI_ARESETN == 0  || init_txn_pulse == 1'b1)                                
	          begin                                                 
	            axi_awaddr <= 0;                                    
	          end                                                   
	          // Signals a new write address/ write data is         
	          // available by user logic                            
	        else if (M_AXI_AWREADY && axi_awvalid)                  
	          begin                                                 
	            axi_awaddr <= axi_awaddr + 32'h00000004;            
	                                                                
	          end                                                   
	      end
  1. 读写响应xRESP[1]置1说明读写传输错误,产生错误标志resp_error;读写数据不一致时,产生错误标志read_mismatch;

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)                                                         
	    read_mismatch <= 1'b0;                                                          
	                                                                                    
	    //The read data when available (on axi_rready) is compared with the expected data
	    else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata))         
	      read_mismatch <= 1'b1;                                                        
	    else                                                                            
	      read_mismatch <= read_mismatch;                                               
	  end 

从机模块

  1. 复位后写地址使能为1,这表示从机可以接收写地址。检测到写地址和写数据通道有效信号后,axi_awready置1,同时写地址使能置0,此时正在进行一次有效传输,不再接收写地址。读写地址通道握手后,发出写响应有效信号,写响应通道握手后,写地址使能置1,axi_awready置0,此时已完成一次传输,又可以重新接收写地址;
always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_awready <= 1'b0;
	      aw_en <= 1'b1;
	    end 
	  else
	    begin    
	      if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
	        begin
	          // slave is ready to accept write address when 
	          // there is a valid write address and write data
	          // on the write address and data bus. This design 
	          // expects no outstanding transactions. 
	          axi_awready <= 1'b1;
	          aw_en <= 1'b0;
	        end
	        else if (S_AXI_BREADY && axi_bvalid)
	            begin
	              aw_en <= 1'b1;
	              axi_awready <= 1'b0;
	            end
	      else           
	        begin
	          axi_awready <= 1'b0;
	        end
	    end 
	end
  1. 写地址通道和写数据通道握手后会向AXI-lite从机写数据,模块会将数据接收到存储映射寄存器slv_reg;ar/awaddr的高位是选择位,用于选择将数据接收到哪个slv_reg。

ADDR_LSB表示选择位的最低位,对于32位数据总线,最低位是2,对于64位数据总线,最低位是3。
OPT_MEM_ADDR_BITS表示有几个选择位,当添加多个存储映射寄存器时,写地址宽度和选择位都会增加

在向AXI-lite模块某地址中写时会指定地址偏移量,偏移量之间相差1个数据字节宽度,偏移量的值和选择位的值大小相等。当指定偏移量为0时,写地址的选择位就是0,数据将被接收到slv_reg0;在接收数据时是按字节接收,只有写选通为1的字节才能被接收到slv_reg

slv_reg0[(byte_index * 8) + : 8] <= S_AXI_WDATA[(byte_index * 8) + : 8] 等价于slv_reg[7:0]<=S_AXI_WDATA[7:0],slv_reg[15:8]<=S_AXI_WDATA[15:8]…;

assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
	          2'h0:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 0
	                slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          2'h1:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 1
	                slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end 
  1. 按选择位将slv_reg赋值给reg_data_out,读地址通道握手后再赋值给读数据;
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
	always @(*)
	begin
	      // Address decoding for reading registers
	      case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
	        2'h0   : reg_data_out <= slv_reg0;
	        2'h1   : reg_data_out <= slv_reg1;
	        2'h2   : reg_data_out <= slv_reg2;
	        2'h3   : reg_data_out <= slv_reg3;
	        default : reg_data_out <= 0;
	      endcase
	end

若要读取PS写到AXI-lite的数据,只需读取存储映射寄存器slv_reg;
若要将数据写到PS端,只需将reg_data_out <=右端替换为要写的数据

AXI-Full

主机模块

  1. 整体过程由状态机控制,当没有有效的读/写地址并且当前没有正在进行的突发传输时,产生开始一次读/写突发传输脉冲。
if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)
  	begin                                                              
   	 	start_single_burst_write <= 1'b1;                                
  	end                                                                
else                                                                 
  	begin                                                              
    	start_single_burst_write <= 1'b0; //Negate to generate a pulse   
  	end                                                                
  1. 检测到读/写脉冲后,拉高读/写地址有效信号,握手后就拉低,每次突发传输只需要传输一个地址,握手信号只需持续1个周期。
always @(posedge M_AXI_ACLK)                                   
	  begin                                                                
	                                                                       
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                           
	      begin                                                            
	        axi_awvalid <= 1'b0;                                           
	      end                                                              
	    // If previously not valid , start next transaction                
	    else if (~axi_awvalid && start_single_burst_write)                 
	      begin                                                            
	        axi_awvalid <= 1'b1;                                           
	      end                                                              
	    /* Once asserted, VALIDs cannot be deasserted, so axi_awvalid      
	    must wait until transaction is accepted */                         
	    else if (M_AXI_AWREADY && axi_awvalid)                             
	      begin                                                            
	        axi_awvalid <= 1'b0;                                           
	      end                                                              
	    else                                                               
	      axi_awvalid <= axi_awvalid;                                      
	    end
  1. 读/写地址通道握手后,产生下一次突发的地址。读/写数据通道握手后,产生下一次传输的数据。

assign burst_size_bytes = C_M_AXI_BURST_LEN * C_M_AXI_DATA_WIDTH/8;//一次突发传输64字节

always @(posedge M_AXI_ACLK)                                         
	  begin                                                                
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                            
	      begin                                                            
	        axi_awaddr <= 'b0;                                             
	      end                                                              
	    else if (M_AXI_AWREADY && axi_awvalid)                             
	      begin                                                            
	        axi_awaddr <= axi_awaddr + burst_size_bytes;                   
	      end                                                              
	    else                                                               
	      axi_awaddr <= axi_awaddr;                                        
	    end
always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                         
	      axi_wdata <= 'b1;                                                             
	    //else if (wnext && axi_wlast)                                                  
	    //  axi_wdata <= 'b0;                                                           
	    else if (wnext)                                                                 
	      axi_wdata <= axi_wdata + 1;                                                   
	    else                                                                            
	      axi_wdata <= axi_wdata;                                                       
	    end 
  1. 每进行突发中的1次读/写数据传输,index计1次数,直到完成C_M_AXI_BURST_LEN次数据传输。
    assign wnext = M_AXI_WREADY & axi_wvalid; //握手信号
always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_write == 1'b1)    
	      begin                                                                         
	        write_index <= 0;                                                           
	      end                                                                           
	    else if (wnext && (write_index != C_M_AXI_BURST_LEN-1))                         
	      begin                                                                         
	        write_index <= write_index + 1;                                             
	      end                                                                           
	    else                                                                            
	      write_index <= write_index;                                                   
	  end
  1. 如果只有1次突发传输,直接拉高last信号,持续1个时钟周期;如果突发传输超过2次,直到完成(C_M_AXI_BURST_LEN-1)次突发传输后拉高tlast信号,持续1个时钟周期。这里计数到C_M_AXI_BURST_LEN-2是因为last信号要和最后一个数据对齐。
always @(posedge M_AXI_ACLK)                                                      
	  begin                                                                             
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                                        
	      begin                                                                         
	        axi_wlast <= 1'b0;                                                          
	      end                                                                           
	    // axi_wlast is asserted when the write index                                   
	    // count reaches the penultimate count to synchronize                           
	    // with the last write data when write_index is b1111                           
	    // else if (&(write_index[C_TRANSACTIONS_NUM-1:1])&& ~write_index[0] && wnext)  
	    else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))
	      begin                                                                         
	        axi_wlast <= 1'b1;                                                          
	      end                                                                           
	    // Deassrt axi_wlast when the last write data has been                          
	    // accepted by the slave with a valid response                                  
	    else if (wnext)                                                                 
	      axi_wlast <= 1'b0;                                                            
	    else if (axi_wlast && C_M_AXI_BURST_LEN == 1)                                   
	      axi_wlast <= 1'b0;                                                            
	    else                                                                            
	      axi_wlast <= axi_wlast;                                                       
	  end
  1. 每进行1次突发传输,burst_counter计1次数,当burst_counter最高位是1时,就计到了最大值64,停止计数。
    源码中给出的是在4K地址范围进行突发传输,每次突发传输64字节,故最大计到4K/64=64,位宽为12-6+1=7。
     localparam integer C_MASTER_LENGTH	= 12;//主机传输地址范围的宽度
	 localparam integer C_NO_BURSTS_REQ = C_MASTER_LENGTH-clogb2((C_M_AXI_BURST_LEN*C_M_AXI_DATA_WIDTH/8)-1);//突发计数器位宽-1
always @(posedge M_AXI_ACLK)                                                                              
	  begin                                                                                                     
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )                                                                                 
	      begin                                                                                                 
	        write_burst_counter <= 'b0;                                                                         
	      end                                                                                                   
	    else if (M_AXI_AWREADY && axi_awvalid)                                                                  
	      begin                                                                                                 
	        if (write_burst_counter[C_NO_BURSTS_REQ] == 1'b0)                                                   
	          begin                                                                                             
	            write_burst_counter <= write_burst_counter + 1'b1;                                              
	            //write_burst_counter[C_NO_BURSTS_REQ] <= 1'b1;                                                 
	          end                                                                                               
	      end                                                                                                   
	    else                                                                                                    
	      write_burst_counter <= write_burst_counter;                                                           
	  end  
  1. burst_active信号指示当前正在进行1次突发读/写传输,检测到写响应握手信号后burst_active置0,每开启1次传输置1。
always @(posedge M_AXI_ACLK)                                                                              
	  begin                                                                                                     
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                                                 
	      burst_write_active <= 1'b0;                                                                           
	                                                                                                            
	    //The burst_write_active is asserted when a write burst transaction is initiated                        
	    else if (start_single_burst_write)                                                                      
	      burst_write_active <= 1'b1;                                                                           
	    else if (M_AXI_BVALID && axi_bready)                                                                    
	      burst_write_active <= 0;                                                                              
	  end
  1. done信号用于指示所有突发传输都已经完成,当burst_counter计到最大且收到最后一次数据响应时done置1.
always @(posedge M_AXI_ACLK)                                                                              
	  begin                                                                                                     
	    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)                                                                                 
	      writes_done <= 1'b0;                                                                                  
	                                                                                                            
	    //The writes_done should be associated with a bready response                                           
	    //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)
	    else if (M_AXI_BVALID && (write_burst_counter[C_NO_BURSTS_REQ]) && axi_bready)                          
	      writes_done <= 1'b1;                                                                                  
	    else                                                                                                    
	      writes_done <= writes_done;                                                                           
	    end

从机模块

  1. axi_awv_awr_flag是当前正在进行1次写传输的状态标志,检测到写地址有效后axi_awv_awr_flag置1,检测到写数据的last后置0.读写地址通道握手后,W/RREADY信号才拉高,
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
  1. 检测到读/写地址有效后将读/写地址锁存,axi_awlen_cntr用于计数1次突发的每次传输,根据突发类型产生相应的读/写地址。

突发传输中起始地址要和AxSIZE对应的传输数据大小对齐,即起始地址的[ADDR_LSB-1:0]应该为0,而ADDR_LSB由AxSIZE决定。如源码中AxSIZE值是2,对应的突发大小是4(3’b100)个字节,4的[1:0]位是0,ADDR_LSB就是2,即起始地址的[1:0]位是0。

aw_wrap_en :这里是利用进位判断到达回环边界,如起始axi_awaddr =6’b011001,到达回环边界时axi_awaddr=6‘b011001+7’b1000000=7’b1011001,7’b1011001&7’b1000000=7’b1000000,也就是说当地址增加了64字节后最高位变成了1.

assign  aw_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_awlen));//回环突发数据总大小64字节
assign  aw_wrap_en = ((axi_awaddr & aw_wrap_size) == aw_wrap_size)? 1'b1: 1'b0;//回环使能,表示到达回环边界
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
  1. 这里和AXI-lite从机接口将数据接收到寄存器中类似,只不过由于传输多个数据,变为根据WSTRB将一次突发传输的全部数据按字节分别存储到4个RAM中。写通道握手后,RAM写使能,然后根据写选通将数据字节存储到byte_ram中。进入到读状态后,RAM读使能,将byte_ram的数据读出。
byte_ram:用数组定义的存储数据字节的RAM
OPT_MEM_ADDR_BITS:RAM地址的位宽-1,和burst_length有关
USER_NUM_MEM :用户需要多少组这样的RAM
mem_address :按读写状态标志获取RAM地址,范围是0~burst_length

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    

六、功能仿真

AXI4-Stream

直接使用官方的主从、机IP核进行连接,并在testbench中给出时钟和复位信号。
在这里插入图片描述
在这里插入图片描述

AXI4-Lite

AXI-lite主从机对地址的处理不同,不能直接相连,需要添加互联模块。
在这里插入图片描述
在这里插入图片描述

AXI-Full

在这里插入图片描述

写通道:
在这里插入图片描述
读通道:
在这里插入图片描述

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hi小瑞同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值