3. AXI4总线协议详解:AXI4-Full/AXI4-Lite/AXI4-Stream区别、握手信号与时序、实战:自定义AXI4-Lite IP
大家好,我是你们的FPGA讲师。今天我们来聊聊Zynq开发中绕不开的一个话题——AXI4总线协议。
说实话,我刚接触Zynq那会儿,看到AXI这三个字母就头大。什么Full、Lite、Stream,还有那一堆握手信号,简直像天书。但后来做项目多了,慢慢就摸清了门道。今天我就把这些经验掰开揉碎了讲给你听。
3.1 为什么是AXI4?
AXI4是ARM公司推出的AMBA总线家族中的一员。Zynq之所以选它,说白了就是因为它够快、够灵活。
你想想看,Zynq内部有ARM处理器(PS)和FPGA逻辑(PL)两部分。这两部分要通信,总得有个标准接口吧?AXI4就是这个桥梁。
我个人习惯把AXI4比作高速公路:
- AXI4-Full:就像八车道高速,能跑大卡车(突发传输)
- AXI4-Lite:像乡间小路,只能跑小轿车(单次传输)
- AXI4-Stream:像传送带,只管往前送数据,不问地址
嗯,这个比喻虽然糙了点,但道理是通的。
3.2 三种协议的区别
我们先看一张对比表,这样更直观:
| 特性 | AXI4-Full | AXI4-Lite | AXI4-Stream |
|---|---|---|---|
| 地址/数据通道 | 分离的地址和数据通道 | 分离的地址和数据通道 | 只有数据通道 |
| 突发传输 | 支持(最多256拍) | 不支持(单次传输) | 支持(无限拍) |
| 数据总线宽度 | 8/16/32/64/128/256/512/1024位 | 32位固定 | 任意宽度 |
| 典型应用 | DDR控制器、DMA传输 | 寄存器配置、状态读取 | 视频流、音频流、高速数据 |
| 握手信号 | VALID/READY + 额外控制 | VALID/READY(简化版) | VALID/READY + TLAST |
我在项目中遇到过这样一个坑:一开始用AXI4-Full去配置一个简单的控制寄存器,结果发现资源占用特别大。后来换成AXI4-Lite,问题就解决了。所以选对协议很重要。
3.3 握手信号与时序
三种协议都基于同一个握手机制——VALID/READY。这个机制其实很简单:
- VALID:发送方说「我数据准备好了,你拿走吧」
- READY:接收方说「我准备好了,你发过来吧」
数据只有在VALID和READY同时为高的时候才传输。这就是所谓的「握手成功」。
我画了一张时序图,帮你理解这个过程:
你看,在T2时刻,VALID和READY同时为高,数据就传过去了。T3时刻READY变低了,虽然VALID还高着,但数据传不过去,只能等。这就是握手的基本规则。
3.4 AXI4-Lite详解
AXI4-Lite是三种协议里最简单的。它不支持突发传输,每次只能读写一个地址。但正因为简单,它特别适合用来配置寄存器。
AXI4-Lite有5个独立通道:
- 写地址通道(AW)—— 告诉从设备我要写哪个地址
- 写数据通道(W)—— 把数据送过去
- 写响应通道(B)—— 从设备告诉你写完了,成功还是失败
- 读地址通道(AR)—— 告诉从设备我要读哪个地址
- 读数据通道(R)—— 从设备把数据返回给你
每个通道都有自己的VALID/READY握手信号。写操作需要三个通道配合,读操作需要两个通道配合。
3.5 实战:自定义AXI4-Lite IP
光说不练假把式。我们来做一个实际的AXI4-Lite IP,实现一个简单的PWM控制器。
这个IP有4个寄存器:
| 地址偏移 | 寄存器名称 | 功能 |
|---|---|---|
| 0x00 | CTRL | 控制寄存器(使能、极性等) |
| 0x04 | PERIOD | 周期寄存器(PWM周期) |
| 0x08 | DUTY | 占空比寄存器 |
| 0x0C | STATUS | 状态寄存器(只读) |
下面是我写的Verilog代码核心部分:
// AXI4-Lite 写操作处理
always @(posedge S_AXI_ACLK) begin
if (~S_AXI_ARESETN) begin
ctrl_reg <= 32'h0;
period_reg <= 32'd1000;
duty_reg <= 32'd500;
end else begin
// 写地址通道握手成功
if (aw_valid && aw_ready) begin
aw_addr_reg <= S_AXI_AWADDR;
end
// 写数据通道握手成功
if (w_valid && w_ready) begin
case (aw_addr_reg[3:0])
4'h0: ctrl_reg <= S_AXI_WDATA;
4'h4: period_reg <= S_AXI_WDATA;
4'h8: duty_reg <= S_AXI_WDATA;
default: ; // 其他地址忽略
endcase
end
end
end
// 写响应通道
assign b_valid = w_valid && w_ready;
assign b_resp = 2'b00; // OKAY响应
assign b_ready = S_AXI_BREADY;
// AXI4-Lite 读操作处理
always @(*) begin
case (ar_addr_reg[3:0])
4'h0: rdata = ctrl_reg;
4'h4: rdata = period_reg;
4'h8: rdata = duty_reg;
4'hC: rdata = status_reg;
default: rdata = 32'h0;
endcase
end
这个IP在Vivado中封装成AXI4-Lite外设后,就可以在PS端用C代码来控制了:
// PS端控制代码
#define PWM_BASEADDR 0x43C00000
// 设置PWM周期为1000
Xil_Out32(PWM_BASEADDR + 0x04, 1000);
// 设置占空比为300(30%)
Xil_Out32(PWM_BASEADDR + 0x08, 300);
// 使能PWM输出
Xil_Out32(PWM_BASEADDR + 0x00, 0x01);
// 读取状态
u32 status = Xil_In32(PWM_BASEADDR + 0x0C);
你看,通过AXI4-Lite总线,PS和PL之间的通信就这么简单。PS写寄存器,PL就能产生PWM波形。这就是Zynq的魅力所在。
3.6 小结
AXI4协议其实没那么可怕。你只要记住三点:
- 三种协议各有用途,选对协议比写好代码更重要
- 握手信号的核心就是VALID/READY,两者同时为高才传输
- AXI4-Lite最适合做寄存器配置,简单可靠
嗯,今天就讲到这里。记住,多动手写代码,多跑仿真,慢慢就熟练了。