FPGA教程系列-Vivado AXI4-Lite master 测试
Master与Slave类似,对该功能进行一个简单的分析:
- 模块声明与参数 (Parameters)
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)
( 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 刚好相反。
- 内部状态机定义与变量声明
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)
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。
区别:写操作是地址和数据几乎同时发出的(为了快),而读操作是严格的“先发地址 -> 再等数据”的串行流程。
- 数据校验与期望值生成
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 endMaster 内部自己算一个“应该读到的数据”。因为写入时是Start_Value + 0,+1,+2… 所以读出时也按这个规律比对。
- 主控状态机 (Master Execution FSM)
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仿真效果:
精髓还是一句话,握手成功再进行数据通信。