FPGA教程系列-Vivado AXI4-Lite master 测试
Master与Slave类似,对该功能进行一个简单的分析:

- 模块声明与参数 (Parameters)
verilog
module myip_axi_lite_master_master_lite_v1_0_M00_AXI #
(
parameter C_M_START_DATA_VALUE = 32'hAA000000, // 测试数据的起始值
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000, // 目标从机的基地址
parameter integer C_M_AXI_ADDR_WIDTH = 32, // 地址位宽
parameter integer C_M_AXI_DATA_WIDTH = 32, // 数据位宽
parameter integer C_M_TRANSACTIONS_NUM = 4 // 测试读写的次数(默认做4次读写)
)
定义了模块的可配置参数。
关键点:
C_M_TARGET_SLAVE_BASE_ADDR:这个非常重要, Master 发出的所有地址都会加上这个基地址。如果你的 Slave 挂载在0x40000000,这里就必须填对,否则 Master 会读写到错误的地址空间。C_M_TRANSACTIONS_NUM:决定了这次测试会连续写几个字,再读几个字。
- 端口定义 (Ports)
verilog
(
input wire INIT_AXI_TXN, // 用户信号:拉高此信号,开始一次完整的 读写测试
output reg ERROR, // 用户信号:如果读回数据不对,输出1
output wire TXN_DONE, // 用户信号:测试完成信号
input wire M_AXI_ACLK, // AXI 时钟
input wire M_AXI_ARESETN, // AXI 复位(低电平有效)
// ... 下面是标准的 AXI4-Lite 五个通道的信号 ...
// 写地址通道 (AW): M_AXI_AWADDR, M_AXI_AWVALID, M_AXI_AWREADY...
// 写数据通道 (W): M_AXI_WDATA, M_AXI_WSTRB, M_AXI_WVALID...
// 写响应通道 (B): M_AXI_BRESP, M_AXI_BVALID...
// 读地址通道 (AR): M_AXI_ARADDR, M_AXI_ARVALID...
// 读数据通道 (R): M_AXI_RDATA, M_AXI_RVALID...
)
定义了物理接口。作为 Master,输出是以 VALID 结尾的信号(如 AWVALID),输入是以 READY 结尾的信号(如 AWREADY)。这与 Slave 刚好相反。
- 内部状态机定义与变量声明
verilog
localparam [1:0] IDLE = 2'b00,
INIT_WRITE = 2'b01, // 状态:正在写
INIT_READ = 2'b10, // 状态:正在读
INIT_COMPARE = 2'b11; // 状态:正在比较结果
// ...
reg [1:0] mst_exec_state; // 主控流程状态机
reg [1:0] state_write; // 写通道状态机
reg [1:0] state_read; // 读通道状态机
定义了整个模块的控制逻辑。这里有三个层级的状态机:
-
mst_exec_state (主控) :负责宏观流程(闲置 -> 写全套 -> 读全套 -> 比较 -> 结束)。 -
state_write (写底层) :负责 AXI 写通道的具体握手细节。 -
state_read (读底层) :负责 AXI 读通道的具体握手细节。 -
I/O 连接 (Assigns)
verilog
assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr; // 基地址 + 偏移量
assign M_AXI_WDATA = axi_wdata;
assign M_AXI_WSTRB = 4'b1111; // 这里的 1111 表示 32位数据全部有效(写全字)
assign init_txn_pulse = (!init_txn_ff2) && init_txn_ff; // 产生一个单周期的脉冲信号
将内部寄存器连接到输出端口。init_txn_pulse 是通过检测 INIT_AXI_TXN 输入信号的上升沿生成的。这意味着给一个高电平开关,内部只会触发一次启动脉冲。
- 写通道状态机 (Write State Machine)

IDLE 状态:
- 等待
init_txn_pulse且主状态机进入INIT_WRITE。 - 一旦触发,立即拉高
axi_awvalid(写地址有效) 和axi_wvalid(写数据有效)。这是一个简化的设计,为了效率,它同时发起了地址和数据。
WADDR 状态 (写地址) :
- 等待从机回复
M_AXI_AWREADY。 - 处理逻辑 :这个状态机比较激进。它会检查从机是否已经准备好接收数据 (
M_AXI_WREADY)。 - 如果地址和数据都握手成功,它会更新地址 (
+4) 和数据 (+1),并检查是否写够了次数 (write_index)。 - 如果还没写完,继续保持在
WADDR发送下一个。 - 如果只是地址握手了但数据没握手,它会跳到
WDATA状态去专门等数据握手。
WDATA 状态 (写数据) :
- 当进入这个状态,说明地址已经发过去了,现在只等从机接收数据。
- 一旦
M_AXI_WREADY有效,完成数据传输,跳转回WADDR或者结束。
- 读通道状态机 (Read State Machine)

IDLE 状态:
- 等待主控状态机发出
INIT_READ命令。 - 触发后,拉高
axi_arvalid(读地址有效),进入RADDR。
RADDR 状态 (发读地址) :
- 等待从机回复
M_AXI_ARREADY。 - 握手成功后,拉低
arvalid,拉高axi_rready(表示主机准备好收数据了),地址加 4,跳转到RDATA。
RDATA 状态 (收读数据) :
- 等待从机发送数据有效信号
M_AXI_RVALID。 - 握手成功后,读取数据,计数器
read_index加 1。 - 如果没读完,跳回
RADDR发起下一次读地址请求;如果读完了,回 IDLE。
区别:写操作是地址和数据几乎同时发出的(为了快),而读操作是严格的"先发地址 -> 再等数据"的串行流程。
- 数据校验与期望值生成
verilog
always @(posedge M_AXI_ACLK)
begin
// ...
else if (M_AXI_RVALID && axi_rready) // 每当成功读到一个数据
begin
expected_rdata <= C_M_START_DATA_VALUE + read_index + 1; // 生成期望值
end
end
Master 内部自己算一个"应该读到的数据"。因为写入时是 Start_Value + 0, +1, +2... 所以读出时也按这个规律比对。
- 主控状态机 (Master Execution FSM)
verilog
case (mst_exec_state)
IDLE:
if (init_txn_pulse) mst_exec_state <= INIT_WRITE; // 收到开始信号,去写
INIT_WRITE:
if (writes_done) mst_exec_state <= INIT_READ; // 写完了,去读
INIT_READ:
if (reads_done) mst_exec_state <= INIT_COMPARE; // 读完了,去比较
INIT_COMPARE:
begin
ERROR <= error_reg; // 输出错误标志
compare_done <= 1'b1; // 输出完成标志
mst_exec_state <= IDLE; // 回到空闲,等待下一次触发
end
endcase
- 功能:这就是整个 IP 的总指挥。它协调读写顺序。
- 错误检测逻辑
代码末尾有几个小的 always 块:
-
writes_done / reads_done :判断计数器是否达到预设的TRANSACTIONS_NUM。 -
read_mismatch :实时比较总线上的RDATA和内部的expected_rdata。如果不相等,置位。 -
error_reg :捕捉任何错误(读写响应错误BRESP/RRESP非 0,或者数据校验错误)。
仿真:
AXI_VIP_IP仿真效果:



精髓还是一句话,握手成功再进行数据通信。