451 lines
15 KiB
Verilog
451 lines
15 KiB
Verilog
`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
|