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

运行方式与通过条件

  1. 将模块代码保存为blc.v,testbench保存为tb_blc.v。
  2. 使用任意支持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)。
  3. 通过条件:仿真输出4行均为PASS,且无FAIL信息。

预期输出

PASS: case1 = 36
PASS: case2 = 50
PASS: case3 = 0
PASS: case4 = 1023

集成说明

在ISP流水线中,本模块位于RAW数据输入之后、去马赛克之前。偏移量通常由传感器暗帧统计得到,通过寄存器总线(如APB/AXI-Lite)写入offset端口。若像素位宽不是10位,修改DATA_WIDTH参数即可,饱和边界自动适配。