AXI协议基础(一):AXI通道概述与握手协议
各位同学,今天我们来聊聊AXI协议最核心的部分——五个独立通道和那个无处不在的握手协议。说实话,我刚接触AXI时也被这五个通道搞得有点晕,但后来发现,搞懂了它们,整个协议就通了一半。
一、AXI的五个独立通道
AXI协议把一次传输拆成了五个独立的通道。为什么要这么设计?说白了就是为了并行。你想想看,读地址和写地址可以同时发,读数据和写数据也能并行跑,这效率不就上来了吗?
这五个通道分别是:
- 读地址通道(AR):发送读请求的地址和控制信息
- 读数据通道(R):返回读到的数据,还带响应信号
- 写地址通道(AW):发送写请求的地址和控制信息
- 写数据通道(W):发送要写入的数据
- 写响应通道(B):返回写操作的完成状态
关键点:每个通道都是单向的,而且读和写是完全独立的。这意味着你可以同时进行读和写操作,互不干扰。
二、读操作的数据流
读操作其实很简单。主机先通过AR通道发个地址过去,从机收到后,通过R通道把数据返回来。我刚开始做验证时,总以为读数据必须等读地址完全处理完才能开始,其实不是这样的。
从机可以在处理读地址的同时,就开始准备数据。只要地址没问题,数据就能很快返回。这里有个细节要注意——读数据通道里除了数据,还有一个RRESP信号,用来告诉主机这次读操作是否成功。
我的经验:在项目中,我习惯把读地址和读数据的时序分开约束。读地址通道的时序通常比较宽松,但读数据通道因为要传大量数据,时序往往更紧张。我曾经在一个DDR控制器项目中,就因为读数据通道的时序没处理好,导致高频下数据采样出错。
三、写操作的数据流
写操作比读操作多了一个通道。主机先通过AW通道发地址,然后通过W通道发数据,最后从机通过B通道返回写响应。
这里有个有意思的地方——写地址和写数据是独立的,所以主机可以先发地址再发数据,也可以先发数据再发地址,甚至可以同时发。我个人习惯是先发地址再发数据,这样从机可以提前准备。
| 通道 | 方向 | 主要信号 | 说明 |
|---|---|---|---|
| AW | 主机→从机 | AWADDR, AWVALID, AWREADY | 写地址和控制信息 |
| W | 主机→从机 | WDATA, WSTRB, WVALID, WREADY | 写数据和字节使能 |
| B | 从机→主机 | BRESP, BVALID, BREADY | 写响应状态 |
| AR | 主机→从机 | ARADDR, ARVALID, ARREADY | 读地址和控制信息 |
| R | 从机→主机 | RDATA, RRESP, RVALID, RREADY | 读数据和响应 |
四、握手协议:VALID/READY
这是AXI协议的精髓所在。每个通道都使用VALID和READY信号来完成握手。说白了就是:主机说"我有数据要发"(VALID有效),从机说"我准备好了"(READY有效),然后一拍即合,数据就传过去了。
握手有三种情况:
- VALID先有效,READY后有效:主机先准备好数据,等从机准备好再传
- READY先有效,VALID后有效:从机先准备好,等主机发数据
- 同时有效:一拍就完成传输,效率最高
避坑指南:我曾经在一个项目中,从机的READY信号在复位后默认拉高,结果主机一上来就发数据,从机还没初始化完成,数据全丢了。所以我的建议是——READY信号默认拉低,等从机真正准备好了再拉高。
五、握手时序与依赖关系
AXI协议对握手时序有一些基本规则,我总结了几条必须记住的:
- VALID信号一旦拉高,必须等到握手完成才能拉低
- READY信号可以随时拉高或拉低
- 数据必须在VALID和READY同时有效的时钟上升沿被采样
- 读数据通道的RLAST信号标记最后一笔数据
- 写数据通道的WLAST信号标记最后一笔数据
嗯,这里要注意——读数据通道的RVALID和RREADY握手,必须在读地址通道的ARVALID和ARREADY握手之后才能发生。这是协议规定的依赖关系,不能乱来。
核心要点:五个通道之间虽然有依赖关系,但每个通道内部的握手是完全独立的。这意味着你可以让写地址通道在等待写数据通道的同时,读地址通道已经在处理新的读请求了。
六、AXI通道结构图
下面这张图展示了AXI五个通道之间的数据流和握手关系。我特意把读和写分开画,这样看起来更清楚。
七、握手协议代码示例
下面是一个简单的握手逻辑,用Verilog写的。我习惯把VALID和READY的握手逻辑封装成一个模块,这样复用起来方便。
// 读地址通道握手示例
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ar_valid <= 1'b0;
ar_addr <= 32'b0;
end else begin
// 当主机有读请求且握手未完成时
if (read_req && !ar_valid) begin
ar_valid <= 1'b1;
ar_addr <= read_addr;
end
// 握手完成,拉低VALID
if (ar_valid && ar_ready) begin
ar_valid <= 1'b0;
end
end
end
// 读数据通道握手示例
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
r_ready <= 1'b0;
end else begin
// 从机准备好接收读数据
if (r_valid && !r_ready) begin
r_ready <= 1'b1;
end
// 数据被采样后拉低
if (r_valid && r_ready) begin
r_ready <= 1'b0;
end
end
end
小技巧:写握手逻辑时,我建议把VALID和READY分开控制。VALID由数据源控制,READY由数据目的地控制。这样逻辑清晰,不容易出错。我曾经见过一个项目把两者混在一起写,调试起来简直要命。
八、实际项目中的注意事项
最后分享几个我在项目中踩过的坑:
- 死锁问题:如果主机和从机都在等对方先发信号,就会死锁。比如主机等从机的READY,从机等主机的VALID。解决办法是确保至少有一方可以先行动。
- 数据对齐:AXI协议要求地址必须对齐到传输大小。比如32位传输,地址必须是4的倍数。这个在验证时很容易漏掉。
- 字节使能:WSTRB信号控制哪些字节有效。我见过有人忘了处理WSTRB,结果写进去的数据全是乱的。
曾经踩过的坑:有一次我在做AXI到APB的桥接,从机的READY信号在复位后有一个时钟周期的毛刺。结果主机检测到READY有效就开始发数据,从机还没准备好,数据全丢了。后来我在READY路径上加了一级寄存器,问题就解决了。
好了,AXI的五个通道和握手协议就讲到这里。这些是AXI协议的基础,后面的内容都会基于这些概念展开。记住一句话——搞懂了握手,就搞懂了AXI的一半。