cache module
This commit is contained in:
314
tb/data_cache/tb_axi_write_ctrl.v
Normal file
314
tb/data_cache/tb_axi_write_ctrl.v
Normal 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. 实例化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
|
138
tb/data_cache/tb_data_assemble.v
Normal file
138
tb/data_cache/tb_data_assemble.v
Normal 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;
|
||||
|
||||
// -------------------------- 测试1:Gray模式拼接 --------------------------
|
||||
$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;
|
||||
// 验证首8bit(0x01)和尾8bit(0x20)
|
||||
$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;
|
||||
|
||||
// -------------------------- 测试2:RGB模式拼接 --------------------------
|
||||
$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*N(N=1~8)
|
||||
repeat (RGB_PIXEL_CNT) begin
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
ir_ch0 = ir_ch0 + 8'd10; // 10,20,...,80(0x0a,0x14,...,0x50)
|
||||
ir_ch1 = ir_ch1 + 8'd20; // 20,40,...,160(0x14,0x28,...,0xa0)
|
||||
ir_ch2 = ir_ch2 + 8'd30; // 30,60,...,240(0x1e,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
|
450
tb/data_cache/tb_data_cache.v
Normal file
450
tb/data_cache/tb_data_cache.v
Normal 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);
|
||||
|
||||
// 测试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
|
208
tb/data_cache/tb_histogram_ctrl.v
Normal file
208
tb/data_cache/tb_histogram_ctrl.v
Normal 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=Gray,1=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内部RAM(hist_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数据(有效像素值:10、20、30、40、50)
|
||||
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=20,max_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);
|
||||
|
||||
// --------------- 步骤4:RGB模式写入与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=R,CH1=G,CH2=B)
|
||||
#(CLK_PERIOD * 2);
|
||||
|
||||
// 4.1 写入RGB数据(R:15、25、35;G:45、55、65;B:75、85、95)
|
||||
// 写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/25,G=55/55,B=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
|
156
tb/data_cache/tb_sync_fifo.v
Normal file
156
tb/data_cache/tb_sync_fifo.v
Normal 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;
|
||||
|
||||
// 生成时钟,周期为10ns(100MHz)
|
||||
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
|
Reference in New Issue
Block a user