3. 黑电平校正硬件模块
任务说明
设计一个用于RAW图像预处理的黑电平校正(Black Level Correction, BLC)硬件模块。模块对每个像素执行有符号减法:pixel_out = pixel_in - offset,其中偏移量为可配置寄存器,支持正值(校正暗电流)与负值(反向补偿)。输出结果饱和到输入位宽范围内,防止下溢或上溢。
接口定义
信号方向 名称 位宽 说明
---------------------------------------------
input clk 1 时钟
input rst_n 1 异步复位,低有效
input offset[9:0] 10 可配置偏移量(有符号,范围-512~511)
input pixel_in[9:0] 10 输入RAW像素(无符号)
input valid_in 1 输入有效标志
output pixel_out[9:0] 10 校正后像素(无符号)
output valid_out 1 输出有效标志(1拍延迟)
核心实现(Verilog)
module blc #(
parameter DATA_WIDTH = 10
)(
input wire clk,
input wire rst_n,
input wire [DATA_WIDTH-1:0] offset, // 有符号数,二进制补码
input wire [DATA_WIDTH-1:0] pixel_in,
input wire valid_in,
output reg [DATA_WIDTH-1:0] pixel_out,
output reg valid_out
);
// 有符号扩展做减法,防止位宽溢出
wire signed [DATA_WIDTH:0] diff;
assign diff = $signed({1'b0, pixel_in}) - $signed(offset);
// 饱和截断到无符号输出范围 [0, 2^DATA_WIDTH-1]
wire [DATA_WIDTH-1:0] sat_out;
assign sat_out = (diff[DATA_WIDTH] == 1'b1) ? {DATA_WIDTH{1'b0}} : // 负 → 0
(diff[DATA_WIDTH-1:0] > {DATA_WIDTH{1'b1}}) ? {DATA_WIDTH{1'b1}} : // 上溢 → 最大值
diff[DATA_WIDTH-1:0];
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pixel_out <= {DATA_WIDTH{1'b0}};
valid_out <= 1'b0;
end else begin
pixel_out <= sat_out;
valid_out <= valid_in;
end
end
endmodule
设计要点
- 减法在
DATA_WIDTH+1位有符号域内完成,避免中间结果溢出。 - 饱和逻辑:结果为负时输出0;结果超过最大值时输出全1;否则直接输出。
- 输出寄存器打拍1周期,
valid_out与数据对齐。
仿真验证
使用以下testbench验证三种关键场景:正偏移校正、负偏移补偿、饱和边界。
`timescale 1ns/1ps
module tb_blc;
reg clk = 0;
reg rst_n = 0;
reg [9:0] offset;
reg [9:0] pixel_in;
reg valid_in;
wire [9:0] pixel_out;
wire valid_out;
blc #(.DATA_WIDTH(10)) uut (
.clk(clk), .rst_n(rst_n),
.offset(offset), .pixel_in(pixel_in),
.valid_in(valid_in),
.pixel_out(pixel_out), .valid_out(valid_out)
);
always #5 clk = ~clk;
initial begin
// 复位
#20 rst_n = 1;
// 场景1:正偏移64,像素100 → 36
@(posedge clk); offset = 10'sd64; pixel_in = 10'd100; valid_in = 1;
@(posedge clk); valid_in = 0;
#10;
if (pixel_out !== 10'd36) $display("FAIL: case1 got %0d", pixel_out);
else $display("PASS: case1 = %0d", pixel_out);
// 场景2:负偏移-30,像素20 → 50
@(posedge clk); offset = -10'sd30; pixel_in = 10'd20; valid_in = 1;
@(posedge clk); valid_in = 0;
#10;
if (pixel_out !== 10'd50) $display("FAIL: case2 got %0d", pixel_out);
else $display("PASS: case2 = %0d", pixel_out);
// 场景3:正偏移200,像素50 → 饱和到0
@(posedge clk); offset = 10'sd200; pixel_in = 10'd50; valid_in = 1;
@(posedge clk); valid_in = 0;
#10;
if (pixel_out !== 10'd0) $display("FAIL: case3 got %0d", pixel_out);
else $display("PASS: case3 = %0d", pixel_out);
// 场景4:负偏移-100,像素1000 → 饱和到1023
@(posedge clk); offset = -10'sd100; pixel_in = 10'd1000; valid_in = 1;
@(posedge clk); valid_in = 0;
#10;
if (pixel_out !== 10'd1023) $display("FAIL: case4 got %0d", pixel_out);
else $display("PASS: case4 = %0d", pixel_out);
$finish;
end
endmodule
运行方式与通过条件
- 将模块代码保存为
blc.v,testbench保存为tb_blc.v。 - 使用任意支持Verilog-2001的仿真器运行:
vlog blc.v tb_blc.v && vsim tb_blc -c -do "run -all; quit"(ModelSim/Questa)或iverilog blc.v tb_blc.v && vvp a.out(Icarus)。 - 通过条件:仿真输出4行均为
PASS,且无FAIL信息。
预期输出
PASS: case1 = 36
PASS: case2 = 50
PASS: case3 = 0
PASS: case4 = 1023
集成说明
在ISP流水线中,本模块位于RAW数据输入之后、去马赛克之前。偏移量通常由传感器暗帧统计得到,通过寄存器总线(如APB/AXI-Lite)写入offset端口。若像素位宽不是10位,修改DATA_WIDTH参数即可,饱和边界自动适配。