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后拉高:发送方先准备好,等接收方。这是最常见的场景。
- READY先拉高,VALID后拉高:接收方先准备好,等发送方。从机如果处理得快,可以提前把READY拉高。
- 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练手。只实现读通道,不实现写通道。等读通道调通了,再加写通道。一步一步来,不容易出错。
可可精品资料库