2. AXI4-Lite接口原理与寄存器映射:信号定义、握手协议与地址映射机制
AXI4-Lite,说白了就是AXI总线家族里的“轻量级选手”。它不像AXI4-Full那样支持突发传输,也不像AXI4-Stream那样只管数据流。它专为控制类寄存器访问而生。我在Zynq项目里,十次有八次都用它来搭PS与PL之间的控制通道——配置参数、读状态、发启动信号,这些活儿它干得最顺手。
2.1 AXI4-Lite的信号定义
AXI4-Lite把读写通道分得清清楚楚。一共5个独立通道,每个通道都有自己的握手信号。我刚开始学的时候觉得信号太多,后来发现其实就三组:写地址、写数据、写响应,再加上读地址、读数据。嗯,就这么简单。
| 通道 | 信号名 | 方向 | 说明 |
|---|---|---|---|
| 写地址通道 | AWADDR | Master→Slave | 写地址,32位 |
| AWVALID | Master→Slave | 地址有效标志 | |
| AWREADY | Slave→Master | 从机准备好接收地址 | |
| AWPROT | Master→Slave | 保护类型(一般置0) | |
| 写数据通道 | WDATA | Master→Slave | 写数据,32位 |
| WVALID | Master→Slave | 数据有效标志 | |
| WREADY | Slave→Master | 从机准备好接收数据 | |
| 写响应通道 | BRESP | Slave→Master | 写响应状态(OKAY/EXOKAY/SLVERR/DECERR) |
| BVALID | Slave→Master | 响应有效标志 | |
| BREADY | Master→Slave | 主机准备好接收响应 | |
| 读地址通道 | ARADDR | Master→Slave | 读地址,32位 |
| ARVALID | Master→Slave | 地址有效标志 | |
| ARREADY | Slave→Master | 从机准备好接收地址 | |
| 读数据通道 | RDATA | Slave→Master | 读数据,32位 |
| RRESP | Slave→Master | 读响应状态 | |
| RVALID | Slave→Master | 数据有效标志 | |
| RREADY | Master→Slave | 主机准备好接收数据 |
2.2 握手协议:VALID与READY的舞蹈
AXI4-Lite的握手,核心就是VALID和READY这一对信号。你想想看,主机说“我有数据要发”(VALID拉高),从机说“我准备好了”(READY拉高),两边一拍即合,数据就传过去了。
握手有三种情况:
- VALID先到,READY后到:主机先拉高VALID,等从机准备好后拉高READY,在时钟上升沿完成传输。这是最常见的场景。
- READY先到,VALID后到:从机提前准备好,主机随后拉高VALID,同样在时钟上升沿完成。
- 同时到达:VALID和READY在同一时钟周期拉高,一拍完成,效率最高。
这里有个坑——VALID一旦拉高,就不能等READY为低时又拉低。协议规定,VALID必须保持到握手完成。我曾经在一个项目里犯过这个错:主机看到从机没准备好,就把VALID撤了,结果从机后来READY拉高时,数据已经没了。嗯,从那以后我再也不敢乱动VALID了。
2.3 寄存器地址映射机制
地址映射,说白了就是把PL里的寄存器“挂”到PS的地址空间里。PS通过AXI总线发一个地址,PL根据这个地址找到对应的寄存器,然后读写数据。
在Zynq里,PS的M_AXI_GP0/GP1接口默认分配了0x4000_0000 ~ 0x7FFF_FFFF这段地址空间给PL。你可以在Vivado里通过Address Editor来分配具体的偏移地址。
地址映射的规则其实很简单:
- 每个寄存器占用4字节(32位)
- 地址按4字节对齐(地址最低2位为0)
- 读操作返回指定地址的32位数据
- 写操作只写指定地址的32位数据
举个例子,假设我在PL里设计了4个寄存器:
| 偏移地址 | 寄存器名 | 方向 | 功能描述 |
|---|---|---|---|
| 0x00 | CTRL_REG | 读写 | 控制寄存器,bit0=启动,bit1=复位 |
| 0x04 | STATUS_REG | 只读 | 状态寄存器,bit0=忙标志,bit1=完成标志 |
| 0x08 | DATA_IN_REG | 只写 | 输入数据寄存器 |
| 0x0C | DATA_OUT_REG | 只读 | 输出数据寄存器 |
PS端访问时,基地址是0x4000_0000,那么CTRL_REG的物理地址就是0x4000_0000,STATUS_REG是0x4000_0004,以此类推。
2.4 完整的读写时序与状态机
写操作的状态机,我一般这么设计:
- IDLE:等待AWVALID和WVALID同时有效
- WRITE:将WDATA写入地址对应的寄存器
- RESP:拉高BVALID,返回OKAY响应
- 回到IDLE
读操作更简单:
- IDLE:等待ARVALID有效
- READ:根据ARADDR读出寄存器数据
- DATA:拉高RVALID,输出RDATA
- 回到IDLE
下面是我画的一个AXI4-Lite从机状态机结构图,帮你理清整体脉络:
2.5 实战中的地址映射陷阱
地址映射看着简单,实际项目里坑不少。我挑几个常见的说说:
- 地址未对齐:PS发了个0x4000_0001,PL译码时发现地址最低2位不是0,直接返回DECERR。我遇到过有人把寄存器地址配成0x4000_0002,结果死活读写不了。
- 地址重叠:两个寄存器映射到同一个偏移地址,写一个另一个也跟着变。这种bug最难查,因为波形上看地址确实是对的。
- 未使用的地址空间:PS访问了0x4000_0010,但PL只实现了0x00~0x0C的寄存器。这时候PL应该返回SLVERR,而不是默默忽略。
2.6 小结
AXI4-Lite的核心就三件事:信号定义、握手协议、地址映射。信号定义告诉你每个引脚干什么用;握手协议保证数据传输的可靠性;地址映射把PL里的寄存器变成PS可以访问的内存空间。这三件事搞清楚了,写一个AXI4-Lite从机IP就是水到渠成的事。