AXI4总线协议与接口设计:从理论到实战

说到Zynq开发,AXI总线是绕不开的核心。我刚开始接触Zynq时,面对AXI4-Full、AXI4-Lite、AXI4-Stream这三个变体,说实话有点懵。它们名字相似,但用法天差地别。今天咱们就把这三兄弟彻底讲透,再聊聊PS与PL之间的接口配置,最后手把手封装一个自定义AXI4-Lite IP核。

核心认知:AXI4是ARM AMBA协议家族的一员,Zynq的PS与PL之间通过AXI接口进行高速数据交换。理解AXI协议,就等于拿到了Zynq开发的钥匙。

AXI4三兄弟:Full、Lite、Stream

这三者的区别,说白了就是复杂度和适用场景的不同。我画了张图,帮你快速建立整体认知。

AXI4协议三兄弟对比 AXI4-Full 高性能内存映射 AXI4-Lite 轻量级寄存器访问 AXI4-Stream 高速数据流 特性 特性 特性 突发传输 单次传输 无地址 多数据拍 固定数据宽度 连续流 复杂握手 简单握手 最小握手 DDR、大块数据搬运 控制寄存器、状态寄存器 视频流、ADC数据、DMA 选择原则 需要地址映射 → Full 或 Lite 仅控制/状态 → Lite 纯数据流 → Stream

AXI4-Full:高性能内存映射

AXI4-Full是功能最完整的版本。它支持突发传输,一次发起地址后可以连续传输多个数据。我在做图像加速项目时,需要从DDR搬运一帧图像到PL处理,用的就是AXI4-Full。

它的核心特点:

  • 五个独立通道:读地址、读数据、写地址、写数据、写响应
  • 突发长度:1到256拍,每拍数据宽度可配置
  • 乱序传输:支持ID标签,事务可以乱序完成

我的经验:AXI4-Full的握手信号VALID/READY必须严格遵循依赖关系。我曾经因为READY信号拉高时机不对,导致仿真通过但上板死锁。排查了两天才发现是地址通道的READY在未收到VALID时就拉高,违反了协议。

AXI4-Lite:轻量级寄存器访问

AXI4-Lite是AXI4-Full的简化版。它不支持突发,每次传输只有一拍数据。但正因如此,它的控制逻辑简单得多。我习惯用AXI4-Lite来做PS与PL之间的控制通道——比如配置加速器的参数、读取状态寄存器。

AXI4-Lite与Full的关键差异:

特性 AXI4-Full AXI4-Lite
突发传输 支持(1-256拍) 不支持(仅1拍)
数据宽度 8/16/32/64/128/256/512/1024 32或64位固定
ID标签 支持乱序 不支持
写响应 每笔写都有响应 每笔写都有响应
典型应用 DDR、大块数据 控制寄存器、状态寄存器

AXI4-Stream:高速数据流

AXI4-Stream是三者中最特殊的——它没有地址通道。数据像流水一样从源头流向目的地。你想想看,视频数据、以太网包、ADC采样值,这些都不需要地址,只需要源源不断地传输。

它的握手信号只有两个:

  • TVALID:发送方表示数据有效
  • TREADY:接收方表示可以接收

再加上TLAST信号标记数据包的结束,TDATA传输数据,TKEEP和TSTRB用于字节对齐。嗯,这里要注意:TLAST在非包传输时可以不用,但大多数Stream接口都会用到。

避坑指南:我曾经在Stream接口上犯过一个低级错误——TVALID和TREADY同时拉高时,数据只在时钟上升沿传输一次。如果接收方在TREADY拉高后立刻拉低,发送方必须保持TVALID和数据稳定,直到握手完成。这个细节在高速传输中特别容易出问题。

Zynq PS与PL间AXI接口配置

Zynq的PS端提供了多个AXI接口,用于与PL通信。常用的有:

接口名称 类型 位宽 典型用途
M_AXI_GP0/1 AXI4-Full 32位 通用数据访问
M_AXI_HP0/1/2/3 AXI4-Full 64位 高速数据通路(DDR)
M_AXI_ACP AXI4-Full 64位 加速器一致性端口
S_AXI_GP0 AXI4-Lite 32位 PL访问PS寄存器

在Vivado中配置这些接口很简单:打开Block Design,添加Zynq Processing System IP,双击后在PS-PL Configuration页面勾选需要的接口。我个人习惯把HP接口留给大数据通路,GP接口用于控制。

自定义AXI4-Lite IP核封装

接下来是实战环节。咱们封装一个自定义AXI4-Lite IP核,包含4个32位寄存器:2个可读写(控制寄存器),2个只读(状态寄存器)。

步骤1:使用Vivado IP Packager

  1. Tools → Create and Package New IP
  2. 选择Create AXI4 Peripheral
  3. 设置IP名称、版本、接口类型(AXI4-Lite)
  4. 指定寄存器数量:4个
  5. 完成向导,生成模板

步骤2:修改RTL代码

Vivado生成的模板中,核心逻辑在slv_reg0到slv_reg3的读写控制。我通常这样修改:

// 写操作逻辑
always @(posedge S_AXI_ACLK)
begin
  if (S_AXI_ARESETN == 1'b0)
    begin
      slv_reg0 <= 32'h0;
      slv_reg1 <= 32'h0;
    end
  else
    begin
      if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_AWVALID && S_AXI_AWREADY)
        begin
          case (S_AXI_AWADDR[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB])
            2'h0 : slv_reg0 <= S_AXI_WDATA;
            2'h1 : slv_reg1 <= S_AXI_WDATA;
            default : ;
          endcase
        end
    end
end

// 读操作逻辑
always @(*)
begin
  case (S_AXI_ARADDR[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB])
    2'h0 : reg_data_out <= slv_reg0;
    2'h1 : reg_data_out <= slv_reg1;
    2'h2 : reg_data_out <= slv_reg2;  // 只读
    2'h3 : reg_data_out <= slv_reg3;  // 只读
    default : reg_data_out <= 32'h0;
  endcase
end

步骤3:编写Linux驱动

在PS端访问这些寄存器,我推荐用UIO(Userspace I/O)驱动,简单高效:

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define REG_BASE 0x43C00000  // AXI-Lite基地址
#define REG_RANGE 0x10000    // 地址范围

int main() {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    void *map_base = mmap(NULL, REG_RANGE, 
                          PROT_READ | PROT_WRITE, 
                          MAP_SHARED, fd, REG_BASE);
    
    volatile uint32_t *reg0 = (uint32_t *)(map_base + 0x00);
    volatile uint32_t *reg1 = (uint32_t *)(map_base + 0x04);
    volatile uint32_t *reg2 = (uint32_t *)(map_base + 0x08);
    volatile uint32_t *reg3 = (uint32_t *)(map_base + 0x0C);
    
    // 写控制寄存器
    *reg0 = 0x12345678;
    *reg1 = 0xAABBCCDD;
    
    // 读状态寄存器
    printf("Status0: 0x%08X\n", *reg2);
    printf("Status1: 0x%08X\n", *reg3);
    
    munmap(map_base, REG_RANGE);
    close(fd);
    return 0;
}

调试技巧:在封装IP核时,我习惯先在Vivado中做行为仿真,用AXI Verification IP作为主设备发送读写事务。这样可以提前发现握手协议问题,避免上板后抓头皮。

总结一下

AXI4协议是Zynq开发的基石。Full负责大块数据搬运,Lite负责控制通路,Stream负责连续数据流。配置PS-PL接口时,根据带宽需求选择GP、HP或ACP。自定义IP核封装时,注意寄存器地址对齐和握手时序。

嗯,这一章的内容就到这里。记住:协议规范是死的,但应用是活的。多动手、多仿真、多上板验证,你很快就能掌握AXI的精髓。