在数字系统设计中,AXI总线协议作为AMBA标准的重要组成部分,已成为现代FPGA和SoC设计的核心互联架构。然而,对于许多工程师来说,单纯阅读数百页的协议文档往往令人望而生畏。本文将采用一种代码驱动的逆向学习法,通过解剖Xilinx Vivado生成的AXI Master IP核源码,带您直观理解AXI协议中最关键的握手机制与通道控制逻辑。
在开始分析之前,我们需要准备可操作的实验环境。不同于传统学习路径——先通读协议再实践,我们将直接从工程实例切入:
创建AXI Master IP核:
bash复制vivado -mode gui
在Vivado中依次选择:
关键文件定位:
生成的IP核目录中,重点关注以下文件:
axi_master_v1_0_M_AXI.v:主控制逻辑axi_master_v1_0.v:顶层封装axi_master_v1_0_S00_AXI.v:从接口(可选)提示:建议在Vivado中启用"Auto Derive"功能,让工具自动生成标准的AXI接口代码结构,这能确保我们分析的代码符合协议规范。
AXI协议的精髓在于其多通道并行与双向握手机制。下面我们通过代码片段逐层拆解:
这是AXI协议中最典型的VALID/READY握手场景。观察Master代码中的关键逻辑:
verilog复制always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
axi_awvalid <= 1'b0;
else if (~axi_awvalid && start_single_burst_write)
axi_awvalid <= 1'b1;
else if (M_AXI_AWREADY && axi_awvalid)
axi_awvalid <= 1'b0;
else
axi_awvalid <= axi_awvalid;
end
这段代码揭示了AXI协议的三个黄金规则:
start_single_burst_write脉冲启动新事务对应的地址生成逻辑则展示了突发传输的地址计算方式:
verilog复制always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
axi_awaddr <= 'b0;
else if (M_AXI_AWREADY && axi_awvalid)
axi_awaddr <= axi_awaddr + burst_size_bytes;
else
axi_awaddr <= axi_awaddr;
end
其中burst_size_bytes的计算公式为:
code复制burst_size_bytes = C_M_AXI_BURST_LEN * (C_M_AXI_DATA_WIDTH/8)
数据通道的独特之处在于WLAST信号的生成机制。以下是关键代码段:
verilog复制// WLAST生成逻辑
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
axi_wlast <= 1'b0;
else if (((write_index == C_M_AXI_BURST_LEN-2) && (C_M_AXI_BURST_LEN >= 2)) ||
(C_M_AXI_BURST_LEN == 1))
axi_wlast <= 1'b1;
else if (M_AXI_WREADY && axi_wvalid)
axi_wlast <= 1'b0;
else
axi_wlast <= axi_wlast;
end
这里有几个值得注意的实现细节:
对应的数据生成器采用简单的递增模式:
verilog复制always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
axi_wdata <= 'b1;
else if (wnext)
axi_wdata <= axi_wdata + 1;
else
axi_wdata <= axi_wdata;
end
响应通道展示了角色反转的握手机制——此时Slave控制VALID,Master控制READY:
verilog复制always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
axi_bready <= 1'b0;
else if (M_AXI_BVALID && ~axi_bready)
axi_bready <= 1'b1;
else if (axi_bready)
axi_bready <= 1'b0;
else
axi_bready <= axi_bready;
end
这段代码实现了一个单周期应答脉冲,确保每个响应只被接收一次。这种设计避免了因持续保持READY而可能导致的死锁情况。
突发传输(Burst)是AXI协议提升效率的关键机制,其参数配置直接影响总线性能。
在IP核定制时设定的参数最终体现为这些信号:
verilog复制assign M_AXI_ARLEN = C_M_AXI_BURST_LEN - 1;
assign M_AXI_ARSIZE = clogb2((C_M_AXI_DATA_WIDTH/8)-1);
其中clogb2是计算数据位宽的辅助函数:
verilog复制function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
协议规定的突发长度与实际参数关系为:
| 信号值 | 实际传输长度 |
|---|---|
| 0 | 1 |
| 1 | 2 |
| ... | ... |
| N | N+1 |
AXI支持多种突发类型,代码中通过ARBURST/AWBURST信号配置:
verilog复制output wire [1:0] M_AXI_ARBURST;
assign M_AXI_ARBURST = 2'b01; // INCR模式
常见的突发类型包括:
地址生成器会根据突发类型自动计算下一个传输地址,这是AXI协议最精妙的设计之一。在INCR模式下,地址计算如下:
verilog复制next_addr = current_addr + (1 << AWSIZE);
在实际调试AXI接口时,以下几个技巧可能帮您快速定位问题:
添加监控逻辑检测长期未完成的握手:
verilog复制// AW通道握手超时计数器
always @(posedge M_AXI_ACLK)
begin
if (axi_awvalid && !M_AXI_AWREADY)
aw_timeout <= aw_timeout + 1;
else
aw_timeout <= 0;
end
assign aw_timeout_error = (aw_timeout > TIMEOUT_THRESHOLD);
利用SystemVerilog断言验证协议合规性:
verilog复制// VALID不得依赖READY
assert property (
@(posedge ACLK)
$rose(AWVALID) |-> !$past(AWREADY)
);
// VALID置位后必须保持
assert property (
@(posedge ACLK)
AWVALID && !AWREADY |=> AWVALID
);
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 传输卡死在第一个数据 | WLAST信号未正确生成 | 检查burst_length配置 |
| 随机数据错误 | 时钟域交叉未同步 | 添加CDC同步寄存器 |
| 性能低于预期 | 频繁小包传输 | 增大burst_length优化吞吐量 |
| 仿真中出现X态 | READY在复位后未初始化 | 确保所有信号有明确复位值 |
在Xilinx Vivado环境中,可以充分利用ILA(集成逻辑分析仪)来捕获AXI总线信号。建议配置触发条件为:
tcl复制create_ila -name axi_monitor -probe_spec { \
M_AXI_AWVALID M_AXI_AWREADY \
M_AXI_WVALID M_AXI_WREADY M_AXI_WLAST \
M_AXI_BVALID M_AXI_BREADY \
M_AXI_ARVALID M_AXI_ARREADY \
M_AXI_RVALID M_AXI_RREADY M_AXI_RLAST}