`timescale 1ns/1ps module tb_axi_write_ctrl(); // -------------------------- 1. 参数定义 -------------------------- parameter AXI_ID_W = 8; parameter AXI_ADDR_W = 32; parameter AXI_DATA_W = 256; parameter AXI_STRB_W = AXI_DATA_W / 8; parameter CLK_PERIOD = 10; parameter FIFO_DEPTH = 4; // -------------------------- 2. 信号定义 -------------------------- // 系统时钟/复位 reg clk; reg rst_n; // 控制与数据输入 reg start_en; reg [AXI_ADDR_W-1:0] sram_base_addr; wire [AXI_DATA_W-1:0] fifo_rd_data; wire fifo_empty; wire fifo_rd_en_dut; // DUT输出的读使能(仅由DUT驱动) // Testbench控制的FIFO读使能(用于清空FIFO) reg tb_fifo_rd_en; // 合并后的FIFO读使能(仅驱动FIFO,避免直接驱动DUT输出) wire fifo_rd_en = tb_fifo_rd_en | fifo_rd_en_dut; // AXI AW通道 wire [AXI_ID_W-1:0] axi_m_awid; wire [AXI_ADDR_W-1:0] axi_m_awaddr; wire [3:0] axi_m_awlen; wire [2:0] axi_m_awsize; wire [1:0] axi_m_awburst; wire axi_m_awlock; wire [4:0] axi_m_awcache; wire [2:0] axi_m_awprot; wire [4:0] axi_m_awqos; wire axi_m_awvalid; reg axi_m_awready; // AXI W通道 wire [AXI_ID_W-1:0] axi_m_wid; wire [AXI_DATA_W-1:0] axi_m_wdata; wire [AXI_STRB_W-1:0] axi_m_wstrb; wire axi_m_wlast; wire axi_m_wvalid; reg axi_m_wready; // AXI B通道 reg [AXI_ID_W-1:0] axi_m_bid; reg [1:0] axi_m_bresp; reg axi_m_bvalid; wire axi_m_bready; // DUT状态输出 wire axi_busy; wire axi_done; // FIFO相关控制信号 reg fifo_wr_en; reg [AXI_DATA_W-1:0] fifo_wr_data; wire fifo_full; reg [$clog2(FIFO_DEPTH)-1:0] fifo_wr_ptr; // -------------------------- 3. 生成系统时钟 -------------------------- initial begin clk = 1'b0; forever #(CLK_PERIOD/2) clk = ~clk; end // -------------------------- 4. 实例化DUT(AXI写控制器) -------------------------- axi_write_ctrl #( .AXI_ID_W (AXI_ID_W), .AXI_ADDR_W (AXI_ADDR_W), .AXI_DATA_W (AXI_DATA_W), .AXI_STRB_W (AXI_STRB_W) ) u_axi_write_ctrl ( .clk (clk), .rst_n (rst_n), .start_en (start_en), .sram_base_addr (sram_base_addr), .fifo_rd_data (fifo_rd_data), .fifo_empty (fifo_empty), .fifo_rd_en (fifo_rd_en_dut), // DUT输出单独命名,避免冲突 .axi_m_awid (axi_m_awid), .axi_m_awaddr (axi_m_awaddr), .axi_m_awlen (axi_m_awlen), .axi_m_awsize (axi_m_awsize), .axi_m_awburst (axi_m_awburst), .axi_m_awlock (axi_m_awlock), .axi_m_awcache (axi_m_awcache), .axi_m_awprot (axi_m_awprot), .axi_m_awqos (axi_m_awqos), .axi_m_awvalid (axi_m_awvalid), .axi_m_awready (axi_m_awready), .axi_m_wid (axi_m_wid), .axi_m_wdata (axi_m_wdata), .axi_m_wstrb (axi_m_wstrb), .axi_m_wlast (axi_m_wlast), .axi_m_wvalid (axi_m_wvalid), .axi_m_wready (axi_m_wready), .axi_m_bid (axi_m_bid), .axi_m_bresp (axi_m_bresp), .axi_m_bvalid (axi_m_bvalid), .axi_m_bready (axi_m_bready), .axi_busy (axi_busy), .axi_done (axi_done) ); // -------------------------- 5. 实例化sync_fifo模块 -------------------------- sync_fifo #( .DATA_WIDTH (AXI_DATA_W), .FIFO_DEPTH (FIFO_DEPTH) ) u_sync_fifo ( .clk (clk), .rst_n (rst_n), .wr_en (fifo_wr_en), .wr_data (fifo_wr_data), .full (fifo_full), .rd_en (fifo_rd_en), // 使用合并后的读使能 .rd_data (fifo_rd_data), .empty (fifo_empty) ); // -------------------------- 6. 生成FSDB波形文件 -------------------------- initial begin $fsdbDumpfile("tb.fsdb"); $fsdbDumpvars(0, tb_axi_write_ctrl); $fsdbDumpMDA(0, tb_axi_write_ctrl); end // -------------------------- 7. FIFO初始化与数据写入 -------------------------- reg [AXI_DATA_W-1:0] fifo_init_data[FIFO_DEPTH-1:0]; initial begin fifo_init_data[0] = 256'h00010203_04050607_08090a0b_0c0d0e0f_10111213_14151617_18191a1b_1c1d1e1f; fifo_init_data[1] = 256'h20212223_24252627_28292a2b_2c2d2e2f_30313233_34353637_38393a3b_3c3d3e3f; fifo_init_data[2] = 256'h40414243_44454647_48494a4b_4c4d4e4f_50515253_54555657_58595a5b_5c5d5e5f; fifo_init_data[3] = 256'h60616263_64656667_68696a6b_6c6d6e6f_70717273_74757677_78797a7b_7c7d7e7f; end task fill_fifo; input integer count; begin fifo_wr_en = 1'b1; repeat(count) begin @(posedge clk); if (!fifo_full) begin fifo_wr_data = fifo_init_data[fifo_wr_ptr]; fifo_wr_ptr = fifo_wr_ptr + 1'b1; if (fifo_wr_ptr >= FIFO_DEPTH) fifo_wr_ptr = 0; end end fifo_wr_en = 1'b0; @(posedge clk); end endtask // -------------------------- 8. 模拟AXI从机 -------------------------- always @(posedge clk or negedge rst_n) begin if (!rst_n) begin axi_m_awready <= 1'b0; end else if (axi_m_awvalid) begin axi_m_awready <= ($random % 2) ? 1'b1 : 1'b0; if (!axi_m_awready && axi_m_awvalid) begin axi_m_awready <= #(CLK_PERIOD) 1'b1; end end else begin axi_m_awready <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if (!rst_n) begin axi_m_wready <= 1'b0; end else if (axi_m_wvalid) begin axi_m_wready <= ($random % 2) ? 1'b1 : 1'b0; if (!axi_m_wready && axi_m_wvalid) begin axi_m_wready <= #(CLK_PERIOD) 1'b1; end end else begin axi_m_wready <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if (!rst_n) begin axi_m_bvalid <= 1'b0; axi_m_bid <= 8'd0; axi_m_bresp <= 2'd0; end else if (axi_m_wvalid && axi_m_wready) begin axi_m_bvalid <= #(CLK_PERIOD) 1'b1; axi_m_bid <= axi_m_wid; end else if (axi_m_bvalid && axi_m_bready) begin axi_m_bvalid <= 1'b0; end end // -------------------------- 9. 核心测试场景 -------------------------- initial begin rst_n = 1'b0; start_en = 1'b0; sram_base_addr = 32'h1000_0000; fifo_wr_en = 1'b0; fifo_wr_data = {AXI_DATA_W{1'b0}}; fifo_wr_ptr = 0; tb_fifo_rd_en = 1'b0; axi_m_bid = 8'd0; axi_m_bresp = 2'd0; axi_m_bvalid = 1'b0; #(CLK_PERIOD * 5); rst_n = 1'b1; #(CLK_PERIOD * 2); // 测试1:单事务写 $display("[%0t] Test 1: Single AXI Write Transaction", $time); fill_fifo(FIFO_DEPTH); start_en = 1'b1; #(CLK_PERIOD); start_en = 1'b0; wait(axi_done == 1'b1); #(CLK_PERIOD); $display("[%0t] Test 1 Result: AWAddr=0x%08h (Expected:0x10000000), WData Match=%b", $time, axi_m_awaddr, (axi_m_wdata == fifo_init_data[0])); #(CLK_PERIOD * 2); // 测试2:连续事务写 $display("[%0t] Test 2: Continuous AXI Write Transactions", $time); fill_fifo(FIFO_DEPTH); #(CLK_PERIOD); start_en = 1'b1; #(CLK_PERIOD); start_en = 1'b0; wait(axi_done == 1'b1); #(CLK_PERIOD); $display("[%0t] Test 2 Trans1: AWAddr=0x%08h (Expected:0x10000020), WData Match=%b", $time, axi_m_awaddr, (axi_m_wdata == fifo_init_data[1])); fill_fifo(FIFO_DEPTH); #(CLK_PERIOD); start_en = 1'b1; #(CLK_PERIOD); start_en = 1'b0; wait(axi_done == 1'b1); #(CLK_PERIOD); $display("[%0t] Test 2 Trans2: AWAddr=0x%08h (Expected:0x10000040), WData Match=%b", $time, axi_m_awaddr, (axi_m_wdata == fifo_init_data[2])); #(CLK_PERIOD * 2); // 测试3:FIFO空阻塞 $display("[%0t] Test 3: FIFO Empty Block Test", $time); tb_fifo_rd_en = 1'b1; // 使用Testbench读使能清空FIFO while (!fifo_empty) begin @(posedge clk); end tb_fifo_rd_en = 1'b0; #(CLK_PERIOD); start_en = 1'b1; #(CLK_PERIOD * 3); start_en = 1'b0; $display("[%0t] Test 3 Result: AXI Busy=%b (Expected:0), AWValid=%b (Expected:0)", $time, axi_busy, axi_m_awvalid); #(CLK_PERIOD * 2); // 测试4:AXI握手延迟 $display("[%0t] Test 4: AXI Handshake Delay Test", $time); fill_fifo(FIFO_DEPTH); #(CLK_PERIOD); axi_m_awready <= 1'b0; axi_m_wready <= 1'b0; start_en = 1'b1; #(CLK_PERIOD); start_en = 1'b0; #(CLK_PERIOD * 2); axi_m_awready <= 1'b1; #(CLK_PERIOD); axi_m_wready <= 1'b1; wait(axi_done == 1'b1); #(CLK_PERIOD); $display("[%0t] Test 4 Result: WData Match=%b (Expected:1), Done=%b (Expected:1)", $time, (axi_m_wdata == fifo_init_data[3]), axi_done); #(CLK_PERIOD * 2); $display("[%0t] All Tests Completed!", $time); $finish; end // -------------------------- 10. 状态监控 -------------------------- reg [1:0] axi_state; always @(posedge clk) begin axi_state = u_axi_write_ctrl.axi_state; end always @(posedge clk) begin case (axi_state) 2'd0: $display("[%0t] AXI State: IDLE", $time); 2'd1: $display("[%0t] AXI State: AW (Address Channel)", $time); 2'd2: $display("[%0t] AXI State: W (Data Channel)", $time); 2'd3: $display("[%0t] AXI State: B (Response Channel)", $time); endcase end endmodule