cache module

This commit is contained in:
Core_kingdom
2025-08-26 16:53:22 +08:00
commit 79dee10db1
124 changed files with 13283 additions and 0 deletions

View File

@@ -0,0 +1,314 @@
`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. 实例化DUTAXI写控制器 --------------------------
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);
// 测试3FIFO空阻塞
$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);
// 测试4AXI握手延迟
$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

View File

@@ -0,0 +1,138 @@
`timescale 1ns/1ps
module tb_data_assemble();
// 参数定义
parameter PIXEL_WIDTH = 8;
parameter GRAY_PIXEL_CNT = 32;
parameter RGB_PIXEL_CNT = 8;
// 信号定义
reg clk;
reg rst_n;
reg en;
reg input_pixel_type;
reg [PIXEL_WIDTH-1:0] ir_ch0;
reg [PIXEL_WIDTH-1:0] ir_ch1;
reg [PIXEL_WIDTH-1:0] ir_ch2;
reg pixel_valid;
wire done;
wire [255:0] assembled_data;
// 生成时钟100MHz
initial begin
clk = 1'b0;
forever #5 clk = ~clk;
end
// 实例化DUT
data_assemble #(
.PIXEL_WIDTH (PIXEL_WIDTH),
.GRAY_PIXEL_CNT (GRAY_PIXEL_CNT),
.RGB_PIXEL_CNT (RGB_PIXEL_CNT)
) u_data_assemble (
.clk (clk),
.rst_n (rst_n),
.en (en),
.input_pixel_type(input_pixel_type),
.ir_ch0 (ir_ch0),
.ir_ch1 (ir_ch1),
.ir_ch2 (ir_ch2),
.pixel_valid (pixel_valid),
.done (done),
.assembled_data (assembled_data)
);
// 生成FSDB波形
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0, tb_data_assemble);
$fsdbDumpMDA(0, tb_data_assemble);
end
// 测试场景
initial begin
// 初始化
rst_n = 1'b0;
en = 1'b0;
input_pixel_type = 1'b0;
ir_ch0 = 8'd0;
ir_ch1 = 8'd0;
ir_ch2 = 8'd0;
pixel_valid = 1'b0;
// 释放复位
#20;
rst_n = 1'b1;
#20;
// -------------------------- 测试1Gray模式拼接 --------------------------
$display("[%0t] Test 1: Gray mode assembly", $time);
input_pixel_type = 1'b0;
en = 1'b1;
#10;
// 输入32个数据0x01~0x20
repeat (GRAY_PIXEL_CNT) begin
@(posedge clk);
pixel_valid = 1'b1;
ir_ch0 = ir_ch0 + 8'd1; // 最后一个数据为0x20
end
@(posedge clk);
pixel_valid = 1'b0;
wait(done == 1'b1);
#10;
// 验证首8bit0x01和尾8bit0x20
$display("[%0t] Gray first 8bit: 0x%0h (Expected: 0x1)",
$time, assembled_data[255:248]);
$display("[%0t] Gray last 8bit: 0x%0h (Expected: 0x20)",
$time, assembled_data[7:0]);
#20;
// -------------------------- 测试2RGB模式拼接 --------------------------
$display("[%0t] Test 2: RGB mode assembly", $time);
input_pixel_type = 1'b1;
ir_ch0 = 8'd0;
ir_ch1 = 8'd0;
ir_ch2 = 8'd0;
#10;
// 输入8组数据CH0=10*N, CH1=20*N, CH2=30*NN=1~8
repeat (RGB_PIXEL_CNT) begin
@(posedge clk);
pixel_valid = 1'b1;
ir_ch0 = ir_ch0 + 8'd10; // 10,20,...,800x0a,0x14,...,0x50
ir_ch1 = ir_ch1 + 8'd20; // 20,40,...,1600x14,0x28,...,0xa0
ir_ch2 = ir_ch2 + 8'd30; // 30,60,...,2400x1e,0x3c,...,0xf0
end
@(posedge clk);
pixel_valid = 1'b0;
wait(done == 1'b1);
#10;
// 验证首32bit{8'd0,30,20,10}=0x00_1e_14_0a和尾32bit{8'd0,240,160,80}=0x00_f0_a0_50
$display("[%0t] RGB first 32bit: 0x%08h (Expected: 0x001e140a)",
$time, assembled_data[255:224]);
$display("[%0t] RGB last 32bit: 0x%08h (Expected: 0x00f0a050)",
$time, assembled_data[31:0]);
#20;
// -------------------------- 测试3关闭使能 --------------------------
$display("[%0t] Test 3: Disable assembly", $time);
en = 1'b0;
input_pixel_type = 1'b0;
@(posedge clk);
pixel_valid = 1'b1;
ir_ch0 = 8'd1;
@(posedge clk);
pixel_valid = 1'b0;
#10;
$display("[%0t] Disable check: done=%b (Expected:0)", $time, done);
#20;
$display("[%0t] All tests completed!", $time);
$finish;
end
endmodule

View File

@@ -0,0 +1,450 @@
`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

View File

@@ -0,0 +1,208 @@
`timescale 1ns/1ps
// 直方图控制模块Testbench修复标识符未声明问题
module tb_histogram_ctrl();
// -------------------------- 1. 参数定义与待测试模块匹配 --------------------------
parameter HIST_RAM_DEPTH = 256; // 直方图RAM深度像素值0~255
parameter HIST_RAM_DATA_W = 1; // 直方图RAM数据位宽1bit标记像素存在
parameter CLK_PERIOD = 10; // 时钟周期10ns = 100MHz
parameter RAM_CLEAR_CYCLES = 256; // 直方图复位需遍历256个地址256个时钟周期
// -------------------------- 2. 信号定义仅含模块接口信号无内部信号引用 --------------------------
reg clk; // 时钟
reg rst_n; // 全局复位低有效
reg hist_rst; // 直方图复位
reg input_pixel_type; // 像素类型0=Gray1=RGB
// CH0 写信号Gray模式有效/RGB模式R通道
reg hist_wr_en_ch0; // CH0写使能
reg [7:0] hist_wr_addr_ch0; // CH0写地址像素值0~255
// CH1 写信号仅RGB模式G通道
reg hist_wr_en_ch1; // CH1写使能
reg [7:0] hist_wr_addr_ch1; // CH1写地址
// CH2 写信号仅RGB模式B通道
reg hist_wr_en_ch2; // CH2写使能
reg [7:0] hist_wr_addr_ch2; // CH2写地址
// min/max计算配置
reg [15:0] histogram_low_num; // 低位数第N个有效像素作为min
reg [15:0] histogram_high_num; // 高位数第N个有效像素作为max
reg calc_en; // 计算使能
// 输出信号
wire calc_done; // 计算完成
wire [7:0] dwidth_conv_min_ch0;// CH0 min结果
wire [7:0] dwidth_conv_max_ch0;// CH0 max结果
wire [7:0] dwidth_conv_min_ch1;// CH1 min结果
wire [7:0] dwidth_conv_max_ch1;// CH1 max结果
wire [7:0] dwidth_conv_min_ch2;// CH2 min结果
wire [7:0] dwidth_conv_max_ch2;// CH2 max结果
// -------------------------- 3. 生成时钟 --------------------------
initial begin
clk = 1'b0;
forever #(CLK_PERIOD/2) clk = ~clk; // 5ns翻转100MHz时钟
end
// -------------------------- 4. 实例化待测试模块 --------------------------
histogram_ctrl #(
.HIST_RAM_DEPTH (HIST_RAM_DEPTH),
.HIST_RAM_DATA_W (HIST_RAM_DATA_W)
) u_histogram_ctrl (
.clk (clk),
.rst_n (rst_n),
.hist_rst (hist_rst),
.input_pixel_type (input_pixel_type),
.hist_wr_en_ch0 (hist_wr_en_ch0),
.hist_wr_addr_ch0 (hist_wr_addr_ch0),
.hist_wr_en_ch1 (hist_wr_en_ch1),
.hist_wr_addr_ch1 (hist_wr_addr_ch1),
.hist_wr_en_ch2 (hist_wr_en_ch2),
.hist_wr_addr_ch2 (hist_wr_addr_ch2),
.histogram_low_num (histogram_low_num),
.histogram_high_num (histogram_high_num),
.calc_en (calc_en),
.calc_done (calc_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)
);
// -------------------------- 5. 生成FSDB波形文件Verdi可查看 --------------------------
initial begin
$fsdbDumpfile("tb.fsdb"); // 波形文件命名为tb.fsdb
$fsdbDumpvars(0, tb_histogram_ctrl); // Dump顶层及所有子模块信号
$fsdbDumpMDA(0, tb_histogram_ctrl); // Dump内部RAMhist_ram_ch0/1/2内容
end
// -------------------------- 6. 核心测试场景无内部信号引用 --------------------------
initial begin
// --------------- 步骤1初始复位清除不定态 ---------------
rst_n = 1'b0;
hist_rst = 1'b0;
input_pixel_type = 1'b0;
hist_wr_en_ch0 = 1'b0;
hist_wr_addr_ch0 = 8'd0;
hist_wr_en_ch1 = 1'b0;
hist_wr_addr_ch1 = 8'd0;
hist_wr_en_ch2 = 1'b0;
hist_wr_addr_ch2 = 8'd0;
histogram_low_num = 16'd2; // 找第2个有效像素作为min跳过1个噪声点
histogram_high_num = 16'd2; // 找第2个有效像素作为max跳过1个噪声点
calc_en = 1'b0;
#(CLK_PERIOD * 5); // 复位保持5个时钟周期
rst_n = 1'b1; // 释放全局复位
#(CLK_PERIOD * 2); // 等待稳定
// --------------- 步骤2测试直方图复位替代原状态判断 ---------------
$display("[%0t] Test 1: Histogram Reset", $time);
hist_rst = 1'b1; // 触发直方图复位
#(CLK_PERIOD); // 保持1个时钟周期确保状态切换
hist_rst = 1'b0; // 释放复位
#(CLK_PERIOD * RAM_CLEAR_CYCLES); // 等待复位完成遍历256个地址
#(CLK_PERIOD * 2); // 额外等待2个周期稳定
$display("[%0t] Test 1 Done: Histogram RAM Cleared", $time);
// --------------- 步骤3灰度模式Gray写入与min/max计算 ---------------
$display("[%0t] Test 2: Gray Mode (Write + Calc min/max)", $time);
input_pixel_type = 1'b0; // 切换为Gray模式仅CH0有效
// 3.1 写入Gray数据有效像素值1020304050
hist_wr_en_ch0 = 1'b1;#(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd10; #(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd20; #(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd30; #(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd40; #(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd50; #(CLK_PERIOD);
hist_wr_en_ch0 = 1'b0; // 关闭写使能
#(CLK_PERIOD * 2);
// 3.2 触发min/max计算找第2个min=20第2个max=40
calc_en = 1'b1;
#(CLK_PERIOD);
calc_en = 1'b0; // 释放计算使能
wait(calc_done == 1'b1); // 用输出信号判断计算完成无需内部状态
#(CLK_PERIOD);
// 打印结果预期min_ch0=20max_ch0=40
$display("[%0t] Gray Mode Result: min_ch0=%0d, max_ch0=%0d (Expected: 20, 40)",
$time, dwidth_conv_min_ch0, dwidth_conv_max_ch0);
#(CLK_PERIOD * 2);
// --------------- 步骤4RGB模式写入与min/max计算 ---------------
$display("[%0t] Test 3: RGB Mode (Write + Calc min/max)", $time);
// 直方图复位用时间等待替代状态判断
hist_rst = 1'b1;
#(CLK_PERIOD);
hist_rst = 1'b0;
#(CLK_PERIOD * RAM_CLEAR_CYCLES); // 等待复位完成
input_pixel_type = 1'b1; // 切换为RGB模式CH0=RCH1=GCH2=B
#(CLK_PERIOD * 2);
// 4.1 写入RGB数据R:152535G:455565B:758595
// 写R通道CH0
hist_wr_en_ch0 = 1'b1;#(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd15; #(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd25; #(CLK_PERIOD);
hist_wr_addr_ch0 = 8'd35; #(CLK_PERIOD);
hist_wr_en_ch0 = 1'b0;
// 写G通道CH1
hist_wr_en_ch1 = 1'b1;#(CLK_PERIOD);
hist_wr_addr_ch1 = 8'd45; #(CLK_PERIOD);
hist_wr_addr_ch1 = 8'd55; #(CLK_PERIOD);
hist_wr_addr_ch1 = 8'd65; #(CLK_PERIOD);
hist_wr_en_ch1 = 1'b0;
// 写B通道CH2
hist_wr_en_ch2 = 1'b1;#(CLK_PERIOD);
hist_wr_addr_ch2 = 8'd75; #(CLK_PERIOD);
hist_wr_addr_ch2 = 8'd85; #(CLK_PERIOD);
hist_wr_addr_ch2 = 8'd95; #(CLK_PERIOD);
hist_wr_en_ch2 = 1'b0;
#(CLK_PERIOD * 2);
// 4.2 触发RGB模式计算预期R=25/25G=55/55B=85/85
calc_en = 1'b1;
#(CLK_PERIOD);
calc_en = 1'b0;
wait(calc_done == 1'b1); // 用输出信号判断完成
#(CLK_PERIOD);
// 打印RGB结果
$display("[%0t] RGB Mode Result: R(min=%0d, max=%0d), G(min=%0d, max=%0d), B(min=%0d, max=%0d) (Expected: R=25/25, G=55/55, B=85/85)",
$time, dwidth_conv_min_ch0, dwidth_conv_max_ch0,
dwidth_conv_min_ch1, dwidth_conv_max_ch1,
dwidth_conv_min_ch2, dwidth_conv_max_ch2);
#(CLK_PERIOD * 2);
// --------------- 步骤5测试+计算并行触发 ---------------
$display("[%0t] Test 4: Trigger Write and Calc Simultaneously", $time);
// 直方图复位
hist_rst = 1'b1;
#(CLK_PERIOD);
hist_rst = 1'b0;
#(CLK_PERIOD * RAM_CLEAR_CYCLES);
input_pixel_type = 1'b0; // 回到Gray模式
#(CLK_PERIOD * 2);
// 先写1个数据再并行触发+计算
hist_wr_en_ch0 = 1'b1;
hist_wr_addr_ch0 = 8'd5; #(CLK_PERIOD);
calc_en = 1'b1; // 同时触发计算
hist_wr_addr_ch0 = 8'd15; #(CLK_PERIOD);
hist_wr_en_ch0 = 1'b0;
calc_en = 1'b0;
wait(calc_done == 1'b1); // 用输出信号判断完成
#(CLK_PERIOD);
$display("[%0t] Test 4 Done: Simultaneous Trigger OK", $time);
// --------------- 步骤6测试完成结束仿真 ---------------
#(CLK_PERIOD * 5);
$display("[%0t] All Tests Completed!", $time);
$finish; // 结束仿真
end
endmodule

View File

@@ -0,0 +1,156 @@
`timescale 1ns/1ps
module tb_sync_fifo();
// 参数定义与被测试模块保持一致
parameter DATA_WIDTH = 256;
parameter FIFO_DEPTH = 8;
// 信号定义
reg clk;
reg rst_n;
reg wr_en;
reg [DATA_WIDTH-1:0] wr_data;
wire full;
reg rd_en;
wire [DATA_WIDTH-1:0] rd_data;
wire empty;
// 生成时钟周期为10ns100MHz
initial begin
clk = 1'b0;
forever #5 clk = ~clk;
end
// 实例化被测试的FIFO模块
sync_fifo #(
.DATA_WIDTH(DATA_WIDTH),
.FIFO_DEPTH(FIFO_DEPTH)
) u_sync_fifo (
.clk(clk),
.rst_n(rst_n),
.wr_en(wr_en),
.wr_data(wr_data),
.full(full),
.rd_en(rd_en),
.rd_data(rd_data),
.empty(empty)
);
// 生成FSDB波形文件
initial begin
$fsdbDumpfile("tb.fsdb"); // 指定波形文件名称
$fsdbDumpvars(0, tb_sync_fifo); // 记录所有层次的信号
end
// 测试过程
initial begin
// 初始化所有输入信号
rst_n = 1'b0;
wr_en = 1'b0;
wr_data = {DATA_WIDTH{1'b0}};
rd_en = 1'b0;
// 复位操作
#20;
rst_n = 1'b1;
#20;
// 测试1: 连续写入数据直到FIFO满
$display("Test 1: Writing until FIFO is full");
rd_en = 1'b0;
repeat (FIFO_DEPTH + 2) begin // 多写2个验证满状态保护
wr_data = $urandom_range(1, 255); // 生成1-255的随机数
wr_en = 1'b1;
@(posedge clk);
end
wr_en = 1'b0;
#20;
// 测试2: 连续读出数据直到FIFO空
$display("Test 2: Reading until FIFO is empty");
wr_en = 1'b0;
@(negedge clk);
rd_en = 1'b1;
repeat (FIFO_DEPTH + 2) begin // 多读2个验证空状态保护
@(posedge clk);
end
rd_en = 1'b0;
#20;
// 测试3: 同时读写操作
$display("Test 3: Simultaneous read and write");
repeat (30) begin // 同时进行30次读写
wr_en = 1'b1;
rd_en = 1'b1;
wr_data = $urandom_range(1, 255);
@(posedge clk);
end
wr_en = 1'b0;
rd_en = 1'b0;
#20;
// 测试4: 交替读写写入一个读出一个
$display("Test 4: Alternate read and write");
repeat (10) begin
// 写入一个数据
wr_en = 1'b1;
rd_en = 1'b0;
wr_data = $urandom_range(1, 255);
@(posedge clk);
// 读出一个数据
wr_en = 1'b0;
rd_en = 1'b1;
@(posedge clk);
end
wr_en = 1'b0;
rd_en = 1'b0;
#20;
// 测试5: 部分填充后连续读写
$display("Test 5: Partial fill then continuous read/write");
// 先填充一半
wr_en = 1'b1;
rd_en = 1'b0;
repeat (FIFO_DEPTH/2) begin
wr_data = $urandom_range(1, 255);
@(posedge clk);
end
// 然后同时读写
wr_en = 1'b1;
rd_en = 1'b1;
repeat (20) begin
wr_data = $urandom_range(1, 255);
@(posedge clk);
end
// 最后清空FIFO
wr_en = 1'b0;
rd_en = 1'b1;
repeat (FIFO_DEPTH/2) begin
@(posedge clk);
end
rd_en = 1'b0;
#50;
$display("All tests completed!");
$finish;
end
// 监控异常操作并打印信息
always @(posedge clk) begin
if (wr_en && full) begin
$display("%t: Error: Trying to write to a full FIFO!", $time);
end
if (rd_en && empty) begin
$display("%t: Error: Trying to read from an empty FIFO!", $time);
end
end
endmodule