不枉初心,砥砺前行

皮皮祥的博客

欢迎留言,评论

导航

AXI4协议(一)AXI4_lite 上

先附上AXI协议 v2-0 版

AMBA-AXI-v2-0-protocol-spec.pdf
871.3K
·
百度网盘

AXI(advanced extensible interface)总线是AMBA总线家族中的一员,是由AHB发展而来,用于在SOC中的各个ip之间互联。AXI适用于高带宽,低延迟的应用,尤其是DDR4这样的高速路存储外设

在XILINX的所有自家ip中,几乎都支持AXI接口标准。所以AXI在FPGA设计中特别常用,尤其是ZYNQ这种需要和ARM核交互的设备。

AXI是并行主线,与串行总线不同,他的接口很多很复杂,总共分为五个通道,信号数量多,不利于集成与仿真调试,需要借助工具进行集成和仿真调试。而且协议相比串行总线要复杂的很多,需要一些学习成本和开发经验才能正确的应用它。

AXI的功能特点有如下几点:

  1. 单独的地址/控制和数据通道(五个传输通道
  2. 支持使用字节选通进行未对齐的数据传输(KEEP与STRB信号
  3. 突发的传输仅发出起始地址(FIXED、INCR、WRAP
  4. 单独的读写数据通道以实现低成本直接内存访问(全双工
  5. 能够发出多个未完成的地址(outstanding模式
  6. 无序传输模式(乱序模式
  7. 轻松添加寄存器级以提供定时关闭(配置灵活

有些功能点看不明白没关系,我们将分成几篇文章,挨个介绍并实现这些功能。

接下来我们从最简单的AXI_lite入手,一步步逐渐完善AXI的各种复杂的功能,包括AXI_stream,AXI_full(FIXED、INCR、WRAP),AXI_interconnect,outstanding模式以及乱序模式。

首先介绍AXI的基本概念,AXI协议支持单主单从通信,也支持多主多从通信,不过主从身份不可互换,始终是主机发起读命令或写命令。AXI作为一种并行总线,它的传输通道有五个,而且每个通道互相独立,分为写地址/命令通道,写数据通道,写回复通道,读地址/命令通道与读数据通道

写数据
读数据

为什么AXI的写通道要比读通道多一个响应通道呢?

要弄清楚这个问题首先要理解AXI的握手机制,AXI之所以可以在完全独立的读写通道中高速且安全地传输数据,靠的就是其内部的握手机制,开始时需要握手以确定双方是否准备好,结束后仍需要握手来确认是否成功传输数据。

开始阶段的握手:

VALID先到
READY先到

AXI的握手信号分为VALIDREADY,其中VALID信号是传输段发送的,READY是接收端发送的。只有在VALIDREADY同时有效的时候,信息才会被传递。VALID有效代表发送以准备好,READY有效代表接收已准备好,当其中一方有效,另一方无效时,有效的一方信号需要保持,等待另一方信号的有效。

之前说过AXI中的主从身份不可换,由于写通道的地址与数据都是由主机传输给从机,所以主机无法判断发送过去的数据是否可以被从机顺利接收,或是发起的写命令是否合法,因此需要一个写响应信号来让从机可以高速主机写地址是否合法,写数据是否被成功写入。

所以写响应通道要发送BVALIDBRESP。其中BVALID要与主机发过来的BREADY进行握手代表写数据结束。BRESP为两个bit的信号,作为写数据是否成功的回应。

BRESP含义 
00 OK 普通模式数据传输成功
01 EXOK 独占模式数据传输成功
10 SLVERR 从机收到命令/地址,但是不支持这个命令/地址,返回错误
11 DECERR 互联模式下,解码后的地址没有对应的从机。(interconnect模式下)

讲一下什么叫独占模式,独占模式指的是独占从机的某一地址空间,向其中写入数据并读出数据,如果这个独占地址在写入和读出过程中被其他主机访问,或者从机不支持独占模式,则报错。

AXI主要有三种模式,AXI_lite,AXI_stream和AXI_full。其中stream面向流的传输,不涉及到内存地址,适合摄像头这种无地址数据流。AXI_full则是完整的AXI协议,支持三种地址模式(FIXED、INCR、WRAP)。这些会在后续的文章中详细讲解与实现。

AXI_lite是轻量级的AXI协议,它每次传输的数据和地址的突发长度只有1,也就是burst=1。常用与较少数据量的存储映射通信,比如配置寄存器。

下面把AXI_lite的所有信号罗列出来:

写地址 AW_ADDR ADDR_WIDTH-1 :0  
  AW_VALID    
  AW_READY    
  AW_PORT 1 : 0 写通道保护信号
写数据 W_DATA DATA_WIDTH-1 : 0  
  W_STRB (DATA_WIDTH/8)-1 : 0 写字节有效位控制
  W_VALID    
  W_READY    
写回应 B_RESP 1:0  
  B_VALID    
  B_READY    
读地址 AR_ADDR ADDR_WIDTH-1 : 0  
  AR_VALID    
  AR_READY    
  AR_PORT 1:0 读通道保护信号
读数据 R_DATA    
  R_RESP 1:0  
  R_VALID    
  R_READY    

介绍一下AW_PORTAR_PORT,是写/读通道保护信号,[0]表示正常或特权,[1]表示安全或非安全,[2]表示指令或数据。这个信号需要用户在使用中根据需要自行配置,我们在本次实现的AXI_lite中不考虑这个信号。

W_STRB信号是写字节有效位控制,在高速通信协议中,都会有这个信号来代表传输的字节是否有效,PCIE、GTX等协议中都有,1代表有效,0代表无效。如果数据为32位,则STRB = 32/8 = 4位。

内部握手信号的控制

在普通模式下,内部的各种握手信号的产生并不是随便产生的,为了保证安全,AXI协议规定了各种信号的依赖关系:

写模式
读模式

注意!!!图中的单箭头,头部的信号可以取决与尾部信号来产生;双箭头,在判断尾部信号的时候必须先判断头部信号。

举例:在判断单箭头头部AWREADY信号时,根据单箭头尾部AWVALIDWVALID信号来判断

把WVALID和AWVALID作为AWREADY有效的条件

在判断双箭头尾部WREADY信号时,先判断双箭头头部信号BVALID

把BVALID控制的清零放在高顺位

在AXI4协议中的依赖关系要更加严格,在写模式中又添加了一些依赖关系。

slave端代码实现

端口和内部信号声明

module AXI_slave #
	(
		parameter integer ADDR_WIDTH	= 4,
		parameter integer DATA_WIDTH	= 32
	)
	(
		//全局变量
		input   CLK,
		input   RESETN,
		// 写地址 
		input  [ADDR_WIDTH-1 : 0] AW_ADDR,
		input   AW_VALID,
		output reg  AW_READY,
		// 写数据 
		input  [DATA_WIDTH-1 : 0] W_DATA,    
		input  [(DATA_WIDTH/8)-1 : 0] W_STRB,
		input   W_VALID,
		output reg  W_READY,
		// 写响应.
		output reg [1 : 0] B_RESP,
		output reg  B_VALID,
		input   B_READY,
		// 写地址 
		input  [ADDR_WIDTH-1 : 0] AR_ADDR,
		input   AR_VALID,
		output reg  AR_READY,
		// 写数据 
		output reg [DATA_WIDTH-1 : 0] R_DATA,
		output reg [1 : 0] R_RESP,
		output reg  R_VALID,
		input   R_READY
	);
integer i;
reg [ADDR_WIDTH-1 : 0] waddr;
reg [ADDR_WIDTH-1 : 0] raddr;	
reg [DATA_WIDTH-1 : 0] W_DATA_reg0;//内部寄存器
reg [DATA_WIDTH-1 : 0] W_DATA_reg1;//内部寄存器
reg [DATA_WIDTH-1 : 0] W_DATA_reg2;//内部寄存器
reg [DATA_WIDTH-1 : 0] W_DATA_reg3;//内部寄存器
reg [DATA_WIDTH-1 : 0] R_DATA_reg;


localparam LSB = DATA_WIDTH/32 + 1;//用于配合字节有效位,32为=2,64为=3

我们选择32位数据来做演示。

我们在slave配置四个内部寄存器,由于在各种存储系统以及地址系统中,地址的最小位都是按byte来设定的。所以32位数据有4个byte,及addr的后两位用来选择4个byte,前两位用于选择内部哪个寄存器,因此addr设置为4位。

//写地址通道	
//=====================================================================
//写地址AW_READY的产生
always@(posedge CLK)
  if(~RESETN)
    AW_READY <= 1'b0;
  else if(B_VALID && B_READY)//master 中AWVALID的清零信号应该也是它,同步清零
    AW_READY <= 1'b0;
  else if(~AW_READY && AW_VALID && W_VALID)	
    AW_READY <= 1'b1;
  else
    AW_READY <= 1'b0;  
  	
//写地址缓存
always@(posedge CLK)
  if(~RESETN)
    waddr <= 'd0;
  else if(~AW_READY && AW_VALID && W_VALID)//AWREADY拉高的同一拍内的地址被存下来
    waddr <= AW_ADDR;
//写数据通道
//======================================================================
always@(posedge CLK)
  if(~RESETN)
    W_READY <= 1'b0;
  else if(B_VALID && B_READY)//master 中WVALID的清零信号应该也是它,同步清零
    W_READY <= 1'b0;
  else if(~W_READY && AW_VALID && W_VALID)	//地址和数据要同步,所以都有效时才能ready
    W_READY <= 1'b1;
  else
    W_READY <= 1'b0; 
	
always@(posedge CLK)
  if(~RESETN) begin
    W_DATA_reg0 <= 'd0;
    W_DATA_reg1 <= 'd0;
    W_DATA_reg2 <= 'd0;
    W_DATA_reg3 <= 'd0;	end	
  else if(AW_VALID && W_VALID && AW_READY && W_READY) begin	
    case(waddr[ADDR_WIDTH - 1 : LSB])
	 2'b00:begin
	   for(i=0;i<DATA_WIDTH/8;i=i+1) begin 
	     if(W_STRB[i])   //字节有效位
          W_DATA_reg0[i*8 +: 8] <= W_DATA[i*8 +: 8]; end
      end
	 2'b01:begin
	   for(i=0;i<DATA_WIDTH/8;i=i+1) begin 
	     if(W_STRB[i])   //字节有效位
          W_DATA_reg1[i*8 +: 8] <= W_DATA[i*8 +: 8]; end
      end
	 2'b10:begin
	   for(i=0;i<DATA_WIDTH/8;i=i+1) begin 
	     if(W_STRB[i])   //字节有效位
          W_DATA_reg2[i*8 +: 8] <= W_DATA[i*8 +: 8]; end
      end
	 2'b11:begin
	   for(i=0;i<DATA_WIDTH/8;i=i+1) begin 
	     if(W_STRB[i])   //字节有效位
          W_DATA_reg3[i*8 +: 8] <= W_DATA[i*8 +: 8]; end
      end
    default:begin
      W_DATA_reg0 <= W_DATA_reg0;
      W_DATA_reg1 <= W_DATA_reg1;
      W_DATA_reg2 <= W_DATA_reg2;
      W_DATA_reg3 <= W_DATA_reg3; end
	 endcase
	end   

AXI协议规定可以使用异步复位,但是复位信号要在时钟上升沿来临前释放。所以我们这里直接使用同步复位。

在写数据处要注意, +:8 符号指的是从当前位向高数8位,-:8 为从当前位向低数8位

例:我们要表示[ 7:0 ],可以用 [ 0 +: 8 ] 或者 [ 7 -: 8 ] 。

我们这里把32位数据分成4个byte挨个写入,是为了配合字节有效位W_STRB,有效则写入,无效则不写入。

上述的case看起来比较清晰,也可以合并为

assign j = waddr[ADDR_WIDTH - 1 : LSB]	  
	  	for(i=0;i<DATA_WIDTH/8;i=i+1) begin 
	     if(W_STRB[i])   //字节有效位
          data_regj[i*8 +: 8] <= W_DATA[i*8 +: 8];
      end

其他

//写响应通道
//======================================================================	  	  	  	  
always@(posedge CLK)
  if(~RESETN) begin
    B_VALID <= 1'b0;
    B_RESP <= 2'b00; end//00:OK ;01:EXOK;10:SLVERR;11:DECERR
  else begin
    if(~B_VALID && AW_VALID && W_VALID && AW_READY && W_READY)begin
      B_VALID <= 1'b1;
      B_RESP <= 2'b00; end
    else if(B_READY && B_VALID)	//地址和数据要同步,所以都有效时才能ready
      B_VALID <= 1'b0;
  end  	
	
//读地址通道
//======================================================================		
always@(posedge CLK)
  if(~RESETN)   
    AR_READY <= 1'b0;
  else if(~AR_READY && AR_VALID && R_VALID)
    AR_READY <= 1'b1;  
  else
    AR_READY <= 1'b0; 

always@(posedge CLK)
  if(~RESETN)  
    raddr <= 'd0;
  else if(~AR_READY && AR_VALID)
    raddr <= AR_ADDR;
	
//读数据通道
//======================================================================    
always@(posedge CLK)
  if(~RESETN) begin  
    R_VALID <= 1'b0;
    R_RESP <= 2'b00; end
  else if(R_READY && R_VALID)begin
    R_VALID <= 1'b0;
    R_RESP <= 2'b00;	end 
  else if(~R_VALID && AR_VALID) begin
    R_VALID <= 1'b1; 
    R_RESP <= 2'b00; end	
 
always@(*)
  if(~R_READY && R_VALID && AR_VALID) begin //addr到了的时钟的数据立刻读取
  case(raddr[ADDR_WIDTH - 1 : LSB])	
    2'b00:R_DATA_reg = W_DATA_reg0;
    2'b01:R_DATA_reg = W_DATA_reg1;
    2'b10:R_DATA_reg = W_DATA_reg2;
    2'b11:R_DATA_reg = W_DATA_reg3;
    default R_DATA_reg = 'd0;
  endcase
  end
//简化写法
//assign j = raddr[ADDR_WIDTH - 1 : LSB]
//R_DATA_reg = W_DATA_regj
always@(posedge CLK)
  if(~RESETN) 
    R_DATA <= 'd0;
  else if(~R_READY && R_VALID && AR_VALID)
    R_DATA <= R_DATA_reg;
  else 	
    R_DATA <= 'd0;
endmodule	

注意在读数据时,当R_VALID和R_READY握手时数据同步出现,因此要用组合逻辑提前一拍读取地址。

在我们的设计中错误信号R_RESP始终为00,及代表数据一直有效。在AXI标准协议中规定R_RESP会在以下几种情况下报错:

  • FIFO/CACHE溢出或不足情况
  • 尝试了不支持的传输大小
  • 试图对只读位置进行写访问
  • 从设备中的条件超时
  • 试图访问不存在的寄存器地址
  • 试图访问禁用或断电的功能

至此,AXI_lite_slave就设计完成了,下一篇文章我们继续设计AXIAXI_lite_master。

h700:手写AXI4协议(二)AXI4_lite 下15 赞同 · 2 评论文章

posted on 2022-11-01 15:34  皮皮祥  阅读(626)  评论(0编辑  收藏  举报