`timescale 1ns/1ps module tb_data_cache(); // -------------------------- 参数定义 -------------------------- parameter ASYNC_FIFO_DEPTH = 1024; parameter ASYNC_FIFO_DATA_W = 27; parameter SYNC_FIFO_DEPTH = 2048; parameter SYNC_FIFO_DATA_W = 256; parameter HIST_RAM_DEPTH = 256; parameter HIST_RAM_DATA_W = 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 IMAGE_WIDTH = 32; // 图像宽度(32像素,便于拼接256bit) parameter IMAGE_HEIGHT = 16; // 图像高度 parameter HIST_LOW_NUM = 1; // 直方图低位数量 parameter HIST_HIGH_NUM = 1; // 直方图高位数量 // 时钟周期定义 parameter SYS_CLK_PERIOD = 10; // 系统时钟周期(10ns=100MHz) parameter IR_CLK_PERIOD = 5; // IR时钟周期(8ns=125MHz) // -------------------------- 信号定义 -------------------------- // 系统时钟与复位 reg sys_clk; reg ir_clk; reg rst_n; // 配置信号 reg ipa_en; reg update_src_trig; reg input_pixel_type; reg [15:0] src_pixel_height; reg [15:0] src_pixel_width; reg [15:0] histogram_low_num; reg [15:0] histogram_high_num; wire src_image_cache_done; // 与Windowed模块接口 wire [7:0] dwidth_conv_min_ch0; wire [7:0] dwidth_conv_max_ch0; wire [7:0] dwidth_conv_min_ch1; wire [7:0] dwidth_conv_max_ch1; wire [7:0] dwidth_conv_min_ch2; wire [7:0] dwidth_conv_max_ch2; // IR图像输入 reg ir_valid; reg ir_vs; reg ir_hs; reg [7:0] ir_ch0; reg [7:0] ir_ch1; reg [7:0] ir_ch2; // AXI写总线 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 [3:0] axi_m_awcache; wire [2:0] axi_m_awprot; wire [3:0] axi_m_awqos; wire axi_m_awvalid; reg axi_m_awready; 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; reg [AXI_ID_W-1:0] axi_m_bid; reg [1:0] axi_m_bresp; reg axi_m_bvalid; wire axi_m_bready; // 内部测试信号 reg [15:0] frame_cnt; // 帧计数器 reg [15:0] ir_row_cnt; // IR行计数器 reg [15:0] ir_col_cnt; // IR列计数器 reg [7:0] golden_min_ch0; // 预期CH0最小值 reg [7:0] golden_max_ch0; // 预期CH0最大值 reg [7:0] golden_min_ch1; // 预期CH1最小值 reg [7:0] golden_max_ch1; // 预期CH1最大值 reg [7:0] golden_min_ch2; // 预期CH2最小值 reg [7:0] golden_max_ch2; // 预期CH2最大值 // -------------------------- 时钟生成 -------------------------- initial begin sys_clk = 1'b0; forever #(SYS_CLK_PERIOD/2) sys_clk = ~sys_clk; end initial begin ir_clk = 1'b0; forever #(IR_CLK_PERIOD/2) ir_clk = ~ir_clk; end // -------------------------- 实例化DUT -------------------------- data_cache #( .ASYNC_FIFO_DEPTH(ASYNC_FIFO_DEPTH), .ASYNC_FIFO_DATA_W(ASYNC_FIFO_DATA_W), .SYNC_FIFO_DEPTH(SYNC_FIFO_DEPTH), .SYNC_FIFO_DATA_W(SYNC_FIFO_DATA_W), .HIST_RAM_DEPTH(HIST_RAM_DEPTH), .HIST_RAM_DATA_W(HIST_RAM_DATA_W), .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_data_cache ( .clk(sys_clk), .rst_n(rst_n), .ipa_en(ipa_en), .update_src_trig(update_src_trig), .input_pixel_type(input_pixel_type), .src_pixel_height(src_pixel_height), .src_pixel_width(src_pixel_width), .histogram_low_num(histogram_low_num), .histogram_high_num(histogram_high_num), .src_image_cache_done(src_image_cache_done), .dwidth_conv_min_ch0(dwidth_conv_min_ch0), .dwidth_conv_max_ch0(dwidth_conv_max_ch0), .dwidth_conv_min_ch1(dwidth_conv_min_ch1), .dwidth_conv_max_ch1(dwidth_conv_max_ch1), .dwidth_conv_min_ch2(dwidth_conv_min_ch2), .dwidth_conv_max_ch2(dwidth_conv_max_ch2), .ir_clk(ir_clk), .ir_valid(ir_valid), .ir_vs(ir_vs), .ir_hs(ir_hs), .ir_ch0(ir_ch0), .ir_ch1(ir_ch1), .ir_ch2(ir_ch2), .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) ); // -------------------------- 生成FSDB波形 -------------------------- initial begin $fsdbDumpfile("tb.fsdb"); $fsdbDumpvars(0, tb_data_cache); $fsdbDumpMDA(0, tb_data_cache); // 记录内存和内部信号 end // -------------------------- AXI从机模拟 -------------------------- // AW通道响应 always @(posedge sys_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) begin axi_m_awready <= #(SYS_CLK_PERIOD) 1'b1; end end else begin axi_m_awready <= 1'b0; end end // W通道响应 always @(posedge sys_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) begin axi_m_wready <= #(SYS_CLK_PERIOD) 1'b1; end end else begin axi_m_wready <= 1'b0; end end // B通道响应 always @(posedge sys_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; // 0=OKAY end else if (axi_m_wvalid && axi_m_wready) begin // W通道传输完成后,延迟1周期发送响应 axi_m_bvalid <= #(SYS_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 // -------------------------- 生成测试图像数据 -------------------------- task generate_ir_data; input integer frame_num; input integer is_rgb; begin // 初始化计数器 ir_row_cnt = 0; ir_col_cnt = 0; ir_vs = 1'b0; ir_hs = 1'b0; ir_valid = 1'b0; ir_ch0 = 8'd0; ir_ch1 = 8'd0; ir_ch2 = 8'd0; // 发送帧起始信号(ir_vs至少持续2个ir_clk周期,确保被FIFO捕获) @(posedge ir_clk); ir_vs = 1'b1; ir_valid = 1'b1; // 强制置1,配合DUT的async_fifo_wr_en逻辑 @(posedge ir_clk); @(posedge ir_clk); // 持续2个周期,避免单周期漏采 ir_vs = 1'b0; ir_valid = 1'b0; // 计算预期的min/max值 if (is_rgb) begin golden_min_ch0 = 8'd32 + frame_num * 16; golden_max_ch0 = 8'd96 + frame_num * 16; golden_min_ch1 = 8'd64 + frame_num * 16; golden_max_ch1 = 8'd128 + frame_num * 16; golden_min_ch2 = 8'd96 + frame_num * 16; golden_max_ch2 = 8'd160 + frame_num * 16; end else begin golden_min_ch0 = 8'd32 + frame_num * 16; golden_max_ch0 = 8'd192 + frame_num * 16; golden_min_ch1 = 8'd0; golden_max_ch1 = 8'd0; golden_min_ch2 = 8'd0; golden_max_ch2 = 8'd0; end // 逐行发送数据 while (ir_row_cnt < IMAGE_HEIGHT) begin // 行起始信号 ir_hs = 1'b1; @(posedge ir_clk); ir_hs = 1'b0; // 逐像素发送数据 ir_col_cnt = 0; while (ir_col_cnt < IMAGE_WIDTH) begin ir_valid = 1'b1; // 生成有规律的数据便于验证 if (is_rgb) begin // RGB模式:三个通道不同范围 ir_ch0 = golden_min_ch0 + (ir_row_cnt % (golden_max_ch0 - golden_min_ch0 + 1)); ir_ch1 = golden_min_ch1 + (ir_col_cnt % (golden_max_ch1 - golden_min_ch1 + 1)); ir_ch2 = golden_min_ch2 + ((ir_row_cnt + ir_col_cnt) % (golden_max_ch2 - golden_min_ch2 + 1)); end else begin // 灰度模式:仅CH0有效 ir_ch0 = golden_min_ch0 + ((ir_row_cnt * IMAGE_WIDTH + ir_col_cnt) % (golden_max_ch0 - golden_min_ch0 + 1)); ir_ch1 = 8'd0; ir_ch2 = 8'd0; end @(posedge ir_clk); ir_col_cnt = ir_col_cnt + 1; end // 行结束 ir_valid = 1'b0; ir_row_cnt = ir_row_cnt + 1; // 行间隙 repeat(2) @(posedge ir_clk); end // 帧结束 ir_valid = 1'b0; // 帧间隙 repeat(5) @(posedge ir_clk); end endtask // -------------------------- 核心测试流程 -------------------------- initial begin // 初始化 rst_n = 1'b0; ipa_en = 1'b0; update_src_trig = 1'b0; input_pixel_type = 1'b0; // 默认灰度模式 src_pixel_height = IMAGE_HEIGHT; src_pixel_width = IMAGE_WIDTH; histogram_low_num = HIST_LOW_NUM; histogram_high_num = HIST_HIGH_NUM; frame_cnt = 16'd0; axi_m_bid = 8'd0; axi_m_bresp = 2'd0; axi_m_bvalid = 1'b0; // 复位 #(SYS_CLK_PERIOD * 10); rst_n = 1'b1; #(SYS_CLK_PERIOD * 5); // 测试1:灰度模式单帧测试 $display("[%0t] Test 1: Gray scale single frame test", $time); input_pixel_type = 1'b0; // 灰度模式 ipa_en = 1'b1; #(SYS_CLK_PERIOD * 2); // 发送一帧灰度图像 generate_ir_data(frame_cnt, 0); frame_cnt = frame_cnt + 1; // 等待缓存完成 wait(src_image_cache_done == 1'b1); #(SYS_CLK_PERIOD * 10); // 验证直方图结果 $display("[%0t] Test 1 Histogram Check: CH0 min=%h (exp=%h), max=%h (exp=%h)", $time, dwidth_conv_min_ch0, golden_min_ch0, dwidth_conv_max_ch0, golden_max_ch0); if (dwidth_conv_min_ch0 != golden_min_ch0 || dwidth_conv_max_ch0 != golden_max_ch0) begin $display("[%0t] Test 1 Histogram Check FAILED!", $time); end else begin $display("[%0t] Test 1 Histogram Check PASSED!", $time); end #(SYS_CLK_PERIOD * 5); // 测试2:RGB模式单帧测试 $display("[%0t] Test 2: RGB single frame test", $time); update_src_trig = 1'b1; // 触发更新 #(SYS_CLK_PERIOD * 2); update_src_trig = 1'b0; input_pixel_type = 1'b1; // RGB模式 #(SYS_CLK_PERIOD * 2); // 发送一帧RGB图像 generate_ir_data(frame_cnt, 1); frame_cnt = frame_cnt + 1; // 等待缓存完成 wait(src_image_cache_done == 1'b1); #(SYS_CLK_PERIOD * 10); // 验证直方图结果 $display("[%0t] Test 2 Histogram Check: CH0 min=%h (exp=%h), max=%h (exp=%h)", $time, dwidth_conv_min_ch0, golden_min_ch0, dwidth_conv_max_ch0, golden_max_ch0); $display("[%0t] Test 2 Histogram Check: CH1 min=%h (exp=%h), max=%h (exp=%h)", $time, dwidth_conv_min_ch1, golden_min_ch1, dwidth_conv_max_ch1, golden_max_ch1); $display("[%0t] Test 2 Histogram Check: CH2 min=%h (exp=%h), max=%h (exp=%h)", $time, dwidth_conv_min_ch2, golden_min_ch2, dwidth_conv_max_ch2, golden_max_ch2); if (dwidth_conv_min_ch0 != golden_min_ch0 || dwidth_conv_max_ch0 != golden_max_ch0 || dwidth_conv_min_ch1 != golden_min_ch1 || dwidth_conv_max_ch1 != golden_max_ch1 || dwidth_conv_min_ch2 != golden_min_ch2 || dwidth_conv_max_ch2 != golden_max_ch2) begin $display("[%0t] Test 2 Histogram Check FAILED!", $time); end else begin $display("[%0t] Test 2 Histogram Check PASSED!", $time); end #(SYS_CLK_PERIOD * 5); // 测试3:连续两帧测试(验证FIFO和AXI写连续性) $display("[%0t] Test 3: Continuous frame test", $time); update_src_trig = 1'b1; #(SYS_CLK_PERIOD * 2); update_src_trig = 1'b0; #(SYS_CLK_PERIOD * 2); // 发送第3帧(灰度) input_pixel_type = 1'b0; generate_ir_data(frame_cnt, 0); frame_cnt = frame_cnt + 1; wait(src_image_cache_done == 1'b1); #(SYS_CLK_PERIOD * 5); // 发送第4帧(RGB) input_pixel_type = 1'b1; generate_ir_data(frame_cnt, 1); frame_cnt = frame_cnt + 1; wait(src_image_cache_done == 1'b1); #(SYS_CLK_PERIOD * 10); // 测试4:更新触发测试(验证中途更新配置) $display("[%0t] Test 4: Update trigger test", $time); input_pixel_type = 1'b0; generate_ir_data(frame_cnt, 0); frame_cnt = frame_cnt + 1; // 中途发送更新触发 #(SYS_CLK_PERIOD * 20); update_src_trig = 1'b1; #(SYS_CLK_PERIOD * 5); update_src_trig = 1'b0; #(SYS_CLK_PERIOD * 10); // 所有测试完成 $display("[%0t] All tests completed!", $time); $finish; end // -------------------------- 监控AXI写事务 -------------------------- always @(posedge sys_clk) begin if (axi_m_awvalid && axi_m_awready) begin $display("[%0t] AXI Write Transaction: Address=0x%08h, Length=%d", $time, axi_m_awaddr, axi_m_awlen); end if (axi_m_wvalid && axi_m_wready) begin $display("[%0t] AXI Write Data: ID=%d, Data[31:0]=0x%08h, Last=%b", $time, axi_m_wid, axi_m_wdata[31:0], axi_m_wlast); end end // -------------------------- 监控状态转换 -------------------------- reg [2:0] prev_state; always @(posedge sys_clk) begin prev_state <= u_data_cache.curr_state; if (prev_state != u_data_cache.curr_state) begin case (u_data_cache.curr_state) 3'b000: $display("[%0t] Data Cache State: IDLE", $time); 3'b001: $display("[%0t] Data Cache State: WAIT_VS", $time); 3'b010: $display("[%0t] Data Cache State: RECEIVE_DATA", $time); 3'b011: $display("[%0t] Data Cache State: WRITE_FIFO", $time); 3'b100: $display("[%0t] Data Cache State: WAIT_AXI", $time); 3'b101: $display("[%0t] Data Cache State: FRAME_DONE", $time); endcase end end endmodule