2. AXI通道详解:读地址通道、读数据通道、写地址通道、写数据通道、写响应通道的信号与时序
AXI协议的核心,说白了就是五条独立通道。我刚开始接触时也觉得通道多、信号杂,但摸清规律后会发现——这五条通道就像五条并行的流水线,各干各的,互不干扰。
为什么这么设计?因为ARM要解决一个根本问题:高带宽、低延迟、乱序传输。你想想看,如果读写共用一条通道,那读请求就得等写请求完成,效率就下来了。AXI把地址、数据、响应全部分开,每个方向都有自己的专用通道。
五通道总览
- 读地址通道(AR):Master发读请求,告诉Slave要读哪里、读多少
- 读数据通道(R):Slave返回读数据,带响应状态
- 写地址通道(AW):Master发写请求,告诉Slave写哪里、写多少
- 写数据通道(W):Master发送要写的数据
- 写响应通道(B):Slave返回写完成状态
2.1 读地址通道(AR Channel)
读地址通道是Master发起读操作的起点。Master通过这个通道告诉Slave:我要读哪个地址,读多少数据。
我个人习惯把AR通道的信号分成两类:地址类和属性类。地址类决定"读哪里",属性类决定"怎么读"。
| 信号 | 位宽 | 描述 |
|---|---|---|
| ARID | 取决于系统 | 读事务ID,用于乱序标识 |
| ARADDR | 32/64 | 读起始地址 |
| ARLEN | 8 | 突发长度(实际传输次数 = ARLEN + 1) |
| ARSIZE | 3 | 每拍数据字节数(2^ARSIZE) |
| ARBURST | 2 | 突发类型:00=FIXED, 01=INCR, 10=WRAP |
| ARVALID | 1 | Master指示地址有效 |
| ARREADY | 1 | Slave准备好接收地址 |
避坑提醒:我曾经在一个项目中,ARLEN设成了0,结果Slave只返回了一拍数据。后来查协议才发现,ARLEN=0表示只传输1次,ARLEN=7才表示传输8次。这个"减1"的坑,新手很容易踩。
握手时序很简单:Master拉高ARVALID,Slave拉高ARREADY,两者同时为高时,地址在时钟上升沿被采样。嗯,这里要注意——VALID不能依赖READY,但READY可以等VALID。
2.2 读数据通道(R Channel)
读数据通道是Slave返回数据的通道。和地址通道不同,数据通道是从Slave到Master的方向。
R通道的信号比AR通道多一个RLAST和RRESP。RLAST告诉Master"这是最后一拍",RRESP表示读操作的状态。
| 信号 | 位宽 | 描述 |
|---|---|---|
| RID | 同ARID | 读事务ID,必须与ARID匹配 |
| RDATA | 8/16/32/64/128 | 读数据 |
| RRESP | 2 | 读响应:00=OKAY, 01=EXOKAY, 10=SLVERR, 11=DECERR |
| RLAST | 1 | 最后一拍指示 |
| RVALID | 1 | Slave指示数据有效 |
| RREADY | 1 | Master准备好接收数据 |
读数据通道支持乱序返回。什么意思?就是Master发了两个读请求,Slave可以先返回第二个请求的数据。怎么区分?靠RID。RID必须和ARID一致,Master通过ID来匹配。
重要:RLAST信号只在突发传输的最后一拍拉高。如果ARLEN=3(4次传输),那么第4个R数据必须带RLAST=1。我曾经见过一个设计,RLAST在倒数第二拍就拉高了,结果Master只收了3拍数据就认为传输结束——数据丢了。
2.3 写地址通道(AW Channel)
写地址通道和读地址通道几乎一样,只是前缀从AR变成了AW。信号定义、位宽、握手方式完全一致。
为什么ARM要重复设计?说白了,就是为了让读写可以完全并行。你可以在同一个时钟周期内,通过AW通道发写请求,通过AR通道发读请求,Slave可以同时处理。
| 信号 | 位宽 | 描述 |
|---|---|---|
| AWID | 取决于系统 | 写事务ID |
| AWADDR | 32/64 | 写起始地址 |
| AWLEN | 8 | 突发长度(实际传输次数 = AWLEN + 1) |
| AWSIZE | 3 | 每拍数据字节数 |
| AWBURST | 2 | 突发类型 |
| AWVALID | 1 | Master指示地址有效 |
| AWREADY | 1 | Slave准备好接收地址 |
2.4 写数据通道(W Channel)
写数据通道是Master发送数据的通道。和读数据通道不同,W通道多了一个WSTRB信号——字节选通信号。
WSTRB是干嘛用的?举个例子:你想写一个32位数据,但只想修改低8位。WSTRB[3:0]中,WSTRB[0]=1表示第0字节有效,其他字节不变。这个功能在寄存器配置时特别有用。
| 信号 | 位宽 | 描述 |
|---|---|---|
| WDATA | 8/16/32/64/128 | 写数据 |
| WSTRB | WDATA/8 | 字节选通,每bit对应一个字节 |
| WLAST | 1 | 最后一拍指示 |
| WVALID | 1 | Master指示数据有效 |
| WREADY | 1 | Slave准备好接收数据 |
写数据通道的关键规则:
- WLAST必须在最后一拍拉高,和RLAST类似
- WSTRB为全0时,数据不会被写入——但传输仍然进行
- 写数据可以和写地址同时发送,也可以先发地址再发数据
2.5 写响应通道(B Channel)
写响应通道是Slave告诉Master"写完了,结果如何"的通道。它只有三个信号:BID、BRESP、BVALID和BREADY。
| 信号 | 位宽 | 描述 |
|---|---|---|
| BID | 同AWID | 写事务ID,必须与AWID匹配 |
| BRESP | 2 | 写响应:00=OKAY, 01=EXOKAY, 10=SLVERR, 11=DECERR |
| BVALID | 1 | Slave指示响应有效 |
| BREADY | 1 | Master准备好接收响应 |
写响应通道只有一个响应,对应整个突发传输。也就是说,你发了8拍写数据,Slave只在最后返回一个B响应。这和读数据通道不同——读数据每拍都有RRESP。
个人经验:我调试过一个DMA控制器,写响应总是SLVERR。查了半天发现是地址越界了——DMA配置的地址超出了Slave的地址范围。BRESP=10(SLVERR)就是告诉你"地址不对,写失败了"。所以调试时,先检查BRESP,能省很多时间。
2.6 通道间的依赖关系
五条通道虽然独立,但存在逻辑依赖。我画个简单的依赖关系:
- 读操作:AR通道 → R通道(必须先有地址,才有数据)
- 写操作:AW通道 + W通道 → B通道(地址和数据都到了,才能返回响应)
注意,AW和W通道之间没有顺序要求。你可以先发地址再发数据,也可以先发数据再发地址,甚至可以同时发。Slave必须能处理这两种情况。
容易踩的坑:有些Slave设计假设地址先到,数据后到。如果Master先发了数据,这个Slave可能把数据丢掉。所以设计Slave时,一定要支持地址和数据乱序到达。我见过一个IP,因为没处理好这个顺序,导致系统跑着跑着就死机了。
2.7 握手协议:VALID/READY机制
所有通道都使用相同的握手协议:VALID/READY。这个机制保证了Master和Slave可以以各自的速度工作。
握手有三种情况:
- VALID先到,READY后到:Master先拉高VALID,Slave准备好后拉高READY,下一个时钟上升沿传输完成
- READY先到,VALID后到:Slave提前准备好,Master准备好后拉高VALID,立即传输
- 同时到达:VALID和READY在同一时钟周期拉高,立即传输
我个人习惯用第一种——Master先发请求,Slave慢慢处理。这样逻辑简单,不容易出错。
握手规则总结:
- VALID拉高后,必须保持到握手完成(READY拉高)
- READY可以提前拉高,也可以等VALID来了再拉高
- 握手完成后,VALID和READY都可以拉低
- 不允许VALID依赖READY——这是死锁的根源
好了,五条通道的信号和时序就讲到这里。记住一个核心思想:地址通道负责"说清楚要干什么",数据通道负责"把数据传过去",响应通道负责"告诉结果"。三条写通道配合完成写操作,两条读通道配合完成读操作,互不干扰。