3、AXI总线基础:AXI4/4-Lite/4-Stream协议详解、握手信号、突发传输、地址映射
好,咱们今天来啃一块硬骨头——AXI总线。说实话,我刚接触FPGA那会儿,看到AXI协议那几百页的文档,头都大了。但后来在实际项目中摸爬滚打,发现这东西其实没那么玄乎。说白了,它就是ARM和FPGA之间沟通的“普通话”。你学会了它,Zynq、MPSoC这些异构芯片就能玩得转。
3.1 为什么是AXI?
先问个问题:为什么ARM非要推AXI?
我个人的理解是,传统的总线(比如AHB、APB)在高速、高带宽的场景下已经不够用了。你想想看,一个CPU要读DDR里的数据,如果一次只能读一个字,那效率得多低?AXI最大的特点就是支持突发传输,一次握手就能传一堆数据,带宽直接拉满。
我在一个图像处理项目里就吃过亏。当时用APB去读帧缓存,结果CPU跑飞了——因为总线带宽根本跟不上。后来换成AXI4,问题迎刃而解。嗯,这里要注意:选对总线协议,比优化代码更重要。
3.2 AXI家族三兄弟
AXI协议有三个变种,别搞混了。我列个表,你一看就明白:
| 协议 | 特点 | 典型应用 |
|---|---|---|
| AXI4 | 完整版,支持突发传输、乱序传输 | DDR控制器、高速DMA、视频流 |
| AXI4-Lite | 轻量版,不支持突发,一次只传一个数据 | 寄存器配置、状态读取、控制接口 |
| AXI4-Stream | 流式版,没有地址,只有数据流 | 视频像素流、音频流、数据包处理 |
我个人习惯:控制通路用Lite,数据通路用Full,流式数据用Stream。这个原则帮我避了不少坑。
3.3 握手信号——握不上手怎么办?
AXI的通信基础是握手。每个通道都有两个关键信号:VALID和READY。发送方拉高VALID表示“我有数据了”,接收方拉高READY表示“我准备好了”。当两者同时为高时,传输发生。
我曾经在一个项目中,发现AXI总线莫名其妙卡死。查了三天,最后发现是接收方的READY信号在复位后一直为低。说白了,就是接收方根本没准备好,但发送方已经把VALID拉高了。结果双方就这么干等着,死锁了。
VALID拉高。这会导致总线死锁。正确的做法是:复位时所有VALID必须为低,等复位释放后再开始握手。
握手有三种模式,我画个简图帮你理解:
- 模式一:VALID先高,READY后高——最常见,发送方先准备好,接收方随后响应。
- 模式二:READY先高,VALID后高——接收方提前告知“我随时可以收”,发送方再发数据。
- 模式三:同时拉高——一拍完成传输,效率最高。
你想想看,如果你的设计里VALID和READY总是同时拉高,那说明你的流水线设计得很完美。但现实中,往往会有等待周期。
3.4 突发传输——一次握手,传一堆数据
这是AXI4最核心的特性。突发传输允许你只发送一次地址,然后连续传输多个数据。比如你要从地址0x1000开始读16个32位数据,传统总线要发16次地址,而AXI4只需要发一次。
突发传输有三个关键参数:
- AxLEN:突发长度,表示传输多少个数据。注意:实际传输数量是AxLEN+1。比如AxLEN=15,就是传16个数据。
- AxSIZE:每个数据的字节数。比如32位数据就是4字节,AxSIZE=2(因为2^2=4)。
- AxBURST:突发类型。有FIXED(固定地址)、INCR(递增地址)、WRAP(回环地址)三种。
我在一个DMA控制器项目中,需要从摄像头连续采集图像数据。当时用了INCR模式,地址自动递增,效率非常高。但要注意:INCR模式不能跨4KB边界。为什么?因为ARM的页表是4KB对齐的。如果你跨了边界,总线会报错。
3.5 地址映射——FPGA和ARM怎么“看到”对方?
在Zynq或MPSoC中,ARM和FPGA共享一个地址空间。ARM通过地址总线访问FPGA里的寄存器或BRAM,FPGA也可以通过AXI接口访问DDR。
地址映射说白了就是:ARM把FPGA里的某个模块映射到自己的内存地址上。比如你给AXI-Lite从机分配一个基地址0x4000_0000,那么ARM读0x4000_0000就是读FPGA里的第一个寄存器,读0x4000_0004就是第二个寄存器。
我建议你在设计时,把地址映射表写在代码注释里。比如:
// AXI-Lite 地址映射
// 0x4000_0000 - 控制寄存器 (32位)
// 0x4000_0004 - 状态寄存器 (32位)
// 0x4000_0008 - 数据寄存器 (32位)
// 0x4000_000C - 中断使能寄存器 (32位)
这样做的好处是,以后调试时不用翻文档,直接看代码就知道地址对应什么功能。
axi_addr_map.h,里面用宏定义好所有地址。这样ARM端和FPGA端都引用同一个文件,避免地址不一致的bug。
3.6 AXI4-Stream——没有地址的“水管”
AXI4-Stream和前面两个完全不同。它没有地址通道,只有数据通道。数据像水流一样从发送端流到接收端。你想想看,视频像素流、音频采样流,这些数据根本不需要地址,只需要按顺序传输就行。
Stream接口的关键信号:
- TVALID:发送方有数据
- TREADY:接收方可以收
- TDATA:数据总线
- TLAST:最后一个数据(用于包传输)
- TKEEP:哪些字节有效(用于非对齐数据)
我在一个视频处理项目中,用AXI4-Stream连接了摄像头接口和图像处理IP。数据流从摄像头进来,经过缩放、滤波,最后输出到显示器。整个过程没有地址参与,全是流式传输,延迟极低。
但要注意:Stream接口的握手和Full/Lite是一样的。如果接收方来不及处理,必须拉低TREADY进行反压。我曾经见过一个设计,接收方没有做反压处理,结果数据溢出,画面出现撕裂。
3.7 实战建议:从零搭建一个AXI-Lite从机
说了这么多,不如动手试试。我建议你从最简单的AXI-Lite从机开始。在Vivado里,你可以用IP Integrator直接生成一个AXI-Lite从机模板。然后修改里面的寄存器逻辑。
核心代码就这几步:
- 定义寄存器地址偏移(比如0x00、0x04、0x08)
- 在写通道中,当
AWVALID和AWREADY同时为高时,锁存地址 - 当
WVALID和WREADY同时为高时,把数据写入对应寄存器 - 在读通道中,当
ARVALID和ARREADY同时为高时,把对应寄存器的值放到RDATA上
嗯,这里要注意:读操作和写操作是独立的。你可以同时读写不同的地址,这就是AXI的“多通道”特性。
好了,这一章的内容就到这里。AXI总线是FPGA和ARM协同开发的基石,你花时间把它搞透,后面的章节就会轻松很多。下一章我们讲如何用AXI总线实现一个实际的DMA控制器,到时候你会看到这些握手信号和突发传输是怎么配合工作的。