2. AXI通道与握手:读地址通道、读数据通道、写地址通道、写数据通道、写响应通道,VALID/READY握手机制详解

AXI总线,说白了就是一套通信协议。它把一次传输拆成了五个独立的通道。这五个通道各干各的活,互不干扰。我刚开始接触AXI时,觉得通道好多,有点乱。后来调了几个项目,才明白这种设计有多巧妙。

今天咱们就把这五个通道掰开揉碎了讲。再配上VALID/READY握手机制,保证你听完就能上手。

2.1 五个独立通道,各司其职

AXI协议把一次读写操作,拆成了五个通道。你想想看,这就像一条流水线。每个工位只干一件事,效率自然高。

这五个通道分别是:

  • 读地址通道(AR通道):主机告诉从机,我要读哪个地址的数据。
  • 读数据通道(R通道):从机把读到的数据,返回给主机。
  • 写地址通道(AW通道):主机告诉从机,我要往哪个地址写数据。
  • 写数据通道(W通道):主机把要写的数据,发送给从机。
  • 写响应通道(B通道):从机告诉主机,写操作完成了,有没有出错。

这里有个关键点:读操作只用两个通道(AR和R),写操作用三个通道(AW、W和B)。为什么写操作多一个?因为写完后,从机需要给你一个回应,告诉你写成功了没有。读操作不需要,数据本身就已经包含了成功与否的信息。

核心要点:五个通道彼此独立,各自有各自的VALID/READY握手信号。这意味着读地址和写地址可以同时发送,互不阻塞。这也是AXI高性能的根源。

2.2 VALID/READY握手机制:双向控制

每个通道的传输,都靠一对信号来控制:VALID和READY。

VALID由发送方拉高,表示“我准备好了,数据/地址有效”。READY由接收方拉高,表示“我准备好了,可以接收”。

数据只有在VALID和READY同时为高的那个时钟沿,才真正传输成功。这就是握手的核心。

我见过不少新手,以为只要VALID拉高,数据就过去了。不是的。必须双方都同意,才算完成。这就像你递东西给别人,你伸手了(VALID),对方也得伸手接(READY),东西才算递过去。

2.2.1 三种握手场景

握手过程,其实就三种情况。我画个图你就明白了。

VALID/READY握手时序图 CLK T1 T2 T3 T4 场景1: VALID先于READY VALID READY 传输 场景2: READY先于VALID VALID READY 传输

三种场景分别是:

  1. VALID先拉高,READY后拉高:发送方先准备好,等接收方。这是最常见的场景。
  2. READY先拉高,VALID后拉高:接收方先准备好,等发送方。从机如果处理得快,可以提前把READY拉高。
  3. VALID和READY同时拉高:一拍就完成传输,效率最高。

我的经验:我个人习惯把READY默认拉高。除非从机忙不过来,才把READY拉低。这样可以减少握手延迟。但要注意,READY不能依赖VALID来拉高,否则会造成组合逻辑环路。

2.3 读地址通道(AR通道)详解

读地址通道,信号不多,但每个都很关键。主机通过这个通道告诉从机:我要读什么。

信号 位宽 说明
ARVALID 1 主机拉高,表示地址有效
ARREADY 1 从机拉高,表示可以接收地址
ARADDR 32/64 读起始地址
ARLEN 8 突发长度,一次读多少笔数据
ARSIZE 3 每笔数据的字节数
ARBURST 2 突发类型:FIXED/INCR/WRAP

ARLEN这个信号,我吃过亏。它表示的是突发长度减1。比如你想读4笔数据,ARLEN要设成3。刚开始我直接设成4,结果从机只返回了3笔,最后一笔死活等不到。查了半天才发现是这里的问题。

2.4 读数据通道(R通道)详解

从机通过R通道,把数据返回给主机。这个通道的信号也简单:

信号 位宽 说明
RVALID 1 从机拉高,表示数据有效
RREADY 1 主机拉高,表示可以接收数据
RDATA 32/64/128 读回来的数据
RRESP 2 读响应状态:OKAY/EXOKAY/SLVERR/DECERR
RLAST 1 最后一笔数据时拉高

RLAST这个信号很重要。它告诉主机,这是突发传输的最后一笔了。主机看到RLAST拉高,就知道这次读操作结束了。

注意:读数据通道的RRESP,是每笔数据都带一个响应。不像写响应通道,整个突发只返回一次响应。我曾经以为读响应也是整个突发一次,结果把RRESP只采样了最后一笔,前面的错误全漏了。

2.5 写地址通道(AW通道)

写地址通道和读地址通道几乎一样。信号名就是把AR换成AW:

  • AWVALID / AWREADY:握手信号
  • AWADDR:写起始地址
  • AWLEN:突发长度(也是减1)
  • AWSIZE:每笔数据字节数
  • AWBURST:突发类型

这里有个细节:写地址通道和写数据通道是独立握手的。也就是说,地址可以先传过去,数据后传。或者数据先传,地址后传。甚至地址和数据可以在同一拍传。从机需要自己把地址和数据对应起来。

2.6 写数据通道(W通道)

写数据通道比读数据通道多了一个信号——WSTRB。这个信号用来控制写哪些字节。

信号 位宽 说明
WVALID 1 主机拉高,表示写数据有效
WREADY 1 从机拉高,表示可以接收写数据
WDATA 32/64/128 要写的数据
WSTRB 数据位宽/8 字节使能,哪一位为1就写哪个字节
WLAST 1 最后一笔数据时拉高

WSTRB这个信号,很多人容易忽略。比如你只想写一个32位数据的低8位,WSTRB就要设成4'b0001。如果设成全1,就会把整个32位都写了。我在调试DDR控制器时,就因为这个信号没配好,导致数据写乱了。

2.7 写响应通道(B通道)

写响应通道是AXI协议里最轻量的一个通道。它只有一个目的:告诉主机,这次写突发操作完成了没有。

信号 位宽 说明
BVALID 1 从机拉高,表示响应有效
BREADY 1 主机拉高,表示可以接收响应
BRESP 2 写响应状态

注意,写响应是整个突发只返回一次。不像读数据,每笔都有响应。从机必须等到WLAST之后,才能给出B通道的响应。

关键时序要求:BVALID必须在WVALID和WLAST都握手成功之后,才能拉高。这是协议明确规定的。有些从机设计不严谨,W数据还没传完就拉BVALID,这是错误的。

2.8 握手依赖关系与死锁预防

五个通道之间,存在一些依赖关系。搞不好就会死锁。

最常见的依赖:

  • R通道依赖AR通道:没有读地址,就没有读数据。
  • B通道依赖W通道:没有写完所有数据,就没有写响应。
  • W通道和AW通道相互独立:地址和数据可以乱序到达。

死锁是怎么发生的?举个例子:

主机在等从机的RVALID,但RVALID需要主机先拉高RREADY才能产生。而主机又在等别的条件才拉RREADY。这就成了死循环。

避坑指南:我曾经在一个项目中,把RREADY设计成依赖某个内部状态机。结果那个状态机又在等RVALID。两边互相等,整个总线卡死了。后来我改成RREADY默认拉高,除非FIFO满了才拉低。问题就解决了。

预防死锁的原则很简单:不要让你的READY信号,依赖对方的VALID信号。READY应该由内部状态(比如FIFO空满)来决定,而不是由外部输入决定。

2.9 实战中的小技巧

最后分享几个我在项目中积累的经验:

  • 仿真时多打印握手信息:我习惯在仿真脚本里,把每个通道的VALID/READY握手时刻都打印出来。一眼就能看出传输有没有卡住。
  • 注意ARLEN和AWLEN的编码:再次强调,值是长度减1。写代码时最好用宏定义,比如`define BURST_LEN_4 3'd3,避免手算出错。
  • WLAST别忘了拉高:写突发的最后一笔数据,WLAST必须拉高。否则从机永远等不到写响应。
  • RRESP和BRESP都要检查:不要只看数据对不对,响应码也要看。SLVERR和DECERR都表示出错了。

嗯,五个通道和握手机制,大概就这些。你想想看,其实每个通道的握手逻辑都一样。只要把VALID/READY搞明白了,五个通道一通百通。下一节咱们就聊聊,怎么用Verilog把这些通道实现出来。

我的建议:初学者可以先拿一个简单的AXI Slave练手。只实现读通道,不实现写通道。等读通道调通了,再加写通道。一步一步来,不容易出错。

可可精品资料库