FPGA 进阶实战 · 02 AXI4-Lite 寄存器与反压

独立接收 AW/W、落实 WSTRB 与错误响应,并保持 B/R 响应直到握手。

先修知识完成 FPGA FIFO 六章基础实战;理解同步时序、非阻塞赋值与 ready/valid。

适用环境Basys 3 / xc7a35tcpg236-1;板上 100/50 MHz 同源时钟;Icarus Verilog、Python 3;Vivado 2024.1 命令参考。

验证状态三组时钟 RTL 测试、两类负向测试及板上自检器的软件模型已通过;Vivado、物理 CDC 和 ILA 上板尚需实测。

本文目录将五个通道分开观察定义软件可依赖的寄存器语义用错开到达与反压验证接口典型故障:错误等待同拍 AW/W

将五个通道分开观察

一次软件写操作在总线上拆成地址、数据和响应过程,地址与数据到达次序不能由从设备猜测。本工程分别锁存地址和数据,凑齐后才提交寄存器操作;读通道按地址握手建立一份响应快照。由此可以解释为什么地址已经握手仍没有写响应,也能在波形中区分尚未收齐输入与下游执行变慢。

不要把 AWVALID 和 WVALID 同拍作为正确工作的前提。AW 缓冲地址独立接收W 缓冲数据 + WSTRB提交寄存器两者齐备再操作B / R 响应VALID 保持到 READYAR/R 独立于 AW/W/B;本实现每类响应最多一笔在途
不要把 AWVALID 和 WVALID 同拍作为正确工作的前提。(原创示意,非器件引脚图;窄屏可横向滑动)

定义软件可依赖的寄存器语义

DATA 支持按字节写入,START 只在最低字节有效且最低数据位为一时触发。忙时再次启动返回错误而不排队,但允许更新 DATA 暂存下一笔,已经锁存的跨域数据保持不变。STATUS 的 done 由新命令清除、结果返回置位,读取本身不清除;RESULT 只在当前命令完成后有效,避免隐藏的读副作用干扰排障。

工程接口与验收依据
地址 / 方向含义与边界
0x00 RW DATAWSTRB 字节合并;全零 WSTRB 不修改
0x04 WO STARTbit0 启动;忙时 SLVERR,零写无副作用
0x08 RO STATUSbit0 busy,bit1 done;读取不清除
0x0c RO RESULT最后一次完成的 32 位结果
其他地址 / 错误方向SLVERR;非对齐访问也返回错误
AWPROT / ARPROT接收但忽略,本外设不形成安全隔离边界

完整 AXI 外设实现,与下载包一致;只支持本表规定的寄存器行为。

systemverilog
`timescale 1ns/1ps
module axi_mailbox #(parameter INJECT_ERROR=0)(
 input wire aclk,arst,dclk,drst,
 input wire [7:0] awaddr,input wire [2:0] awprot,input wire awvalid,output wire awready,
 input wire [31:0] wdata,input wire [3:0] wstrb,input wire wvalid,output wire wready,
 output reg [1:0] bresp,output reg bvalid,input wire bready,
 input wire [7:0] araddr,input wire [2:0] arprot,input wire arvalid,output wire arready,
 output reg [31:0] rdata,output reg [1:0] rresp,output reg rvalid,input wire rready,
 output wire [79:0] debug_s,debug_d);
 reg aw_pending,w_pending;reg [7:0] addr_q;reg [31:0] data_q;reg [3:0] strb_q;
 reg [31:0] data_reg,result_reg;reg busy,done,start;
 wire mb_ready,mb_valid;wire [31:0] mb_result;
 // PROT is accepted but not used: no security boundary in this teaching peripheral.
 assign awready=!arst && !aw_pending && !bvalid;
 assign wready=!arst && !w_pending && !bvalid;
 assign arready=!arst && !rvalid;
 cdc_mailbox #(.INJECT_ERROR(INJECT_ERROR)) u_cdc(aclk,arst,dclk,drst,start,data_reg,mb_ready,mb_valid,mb_result,debug_s,debug_d);
 integer k;
 always @(posedge aclk or posedge arst) begin
  if(arst) begin
   aw_pending<=0;w_pending<=0;addr_q<=0;data_q<=0;strb_q<=0;
   bresp<=0;bvalid<=0;rresp<=0;rvalid<=0;rdata<=0;
   data_reg<=0;result_reg<=0;busy<=0;done<=0;start<=0;
  end else begin
   start<=0;
   if(bvalid && bready)bvalid<=0;
   if(rvalid && rready)rvalid<=0;
`ifdef BUG_AW_W
   // Intentional fault: wrongly requires simultaneous AWVALID and WVALID.
   if(awvalid && awready && wvalid)begin addr_q<=awaddr;aw_pending<=1;end
   if(wvalid && wready && awvalid)begin data_q<=wdata;strb_q<=wstrb;w_pending<=1;end
`else
   if(awvalid && awready)begin addr_q<=awaddr;aw_pending<=1;end
   if(wvalid && wready)begin data_q<=wdata;strb_q<=wstrb;w_pending<=1;end
`endif
   if(mb_valid)begin result_reg<=mb_result;busy<=0;done<=1;end
   if(aw_pending && w_pending && !bvalid)begin
    aw_pending<=0;w_pending<=0;bvalid<=1;bresp<=0;
    case(addr_q)
     8'h00:for(k=0;k<4;k=k+1)if(strb_q[k])data_reg[k*8+:8]<=data_q[k*8+:8];
     8'h04:if(strb_q[0] && data_q[0])begin
      if(busy || !mb_ready)bresp<=2; // SLVERR, no command accepted
      else begin busy<=1;done<=0;start<=1;end
     end
     default:bresp<=2;
    endcase
   end
   if(arvalid && arready)begin
    rvalid<=1;rresp<=0;
    case(araddr)
     8'h00:rdata<=data_reg;
     8'h08:rdata<={30'b0,done,busy};
     8'h0c:rdata<=result_reg;
     default:begin rdata<=0;rresp<=2;end
    endcase
   end
  end
 end
endmodule

用错开到达与反压验证接口

测试既要检查最终读写值,也要检查等待期间接口是否稳定。测试任务让 AW 先到、W 先到或同时到达,随后故意推迟 BREADY 和 RREADY,确认响应不会消失或改变。字节合并用有辨识度的数据验证,非法访问则检查响应码,避免把工具没有报错误认为总线协议实现正确。

  1. 分离地址与数据

    预期与判定:确认 AW 先到与 W 先到都能形成一次写响应。

  2. 推迟响应接收

    预期与判定:B 等待四拍、R 等待三拍期间 VALID 与载荷保持。

  3. 检查边界访问

    预期与判定:部分字节、全零写使能、非法和非对齐地址结果正确。

  4. 验证负向测试

    预期与判定:BUG_AW_W 版本必须被超时检查检出。

在解压后的 fpga-advanced 目录执行。软件仿真不需要 Vivado;硬件步骤需目标工具与板卡。

bash
python3 flow.py sim
# 测试中的字节写:
# DATA <- 0x11223344, WSTRB=0xf
# DATA <- 0xaabbccdd, WSTRB=0x5
# 读回应为 0x11bb33dd;WSTRB=0 不修改
python3 flow.py sim --axi-fault
# 预期:AXI_TIMEOUT,随后 ADV_NEGATIVE_PASS

典型故障:错误等待同拍 AW/W

故障版本对地址和数据接收都增加了另一通道有效的条件,却仍对外声称能够握手。于是主设备认为地址已被接受,随后撤销地址有效,而从设备实际上没有保存地址。这个例子说明不能靠延长软件等待或要求上游固定发送顺序修复协议问题,必须修复接收缓存和握手事件的一致性。

完成检查

  • AW/W 任意先后均能独立接收。
  • WSTRB、SLVERR 与响应保持得到验证。
  • 故障版被 AXI_TIMEOUT 明确检出。

延伸阅读与来源

本文为黄河电子原创教学说明。产品命令与支持范围请以实际安装版本的官方文档为准。

继续阅读

FPGA

从 RTL 到板上运行的完整流程

建立约束、实现、下载与观测的最小工程闭环。

10 分钟
FPGA

第一个工程的引脚与时钟约束

区分电气连接与时间要求,避免“能生成位流就算正确”。

11 分钟
FPGA

如何读懂时序报告

从起点、终点、时钟和裕量找出失败路径的原因。

10 分钟