Files
IPA/tb/data_cache/tb_data_cache.v
Core_kingdom 79dee10db1 cache module
2025-08-26 16:53:22 +08:00

451 lines
15 KiB
Verilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

`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);
// 测试2RGB模式单帧测试
$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