2. 行缓冲器设计(Line Buffer)

任务目标

设计一个可配置的行缓冲器(Line Buffer),用于图像处理流水线中存储多行像素数据,并支持以滑动窗口形式并行读取任意位置的3×3邻域数据。本案例以8bit灰度图像、3行缓冲、每行64像素为具体参数完成RTL设计。

设计规格

  • 输入:单像素8bit数据 pixel_in,像素有效标志 valid_in,行同步信号 line_start(每行第一个像素时拉高一个周期)
  • 输出:3×3窗口并行数据 win_00 ~ win_22(共9个8bit输出),窗口数据有效标志 valid_out
  • 内部存储:3行 × 64像素 × 8bit 移位寄存器阵列
  • 工作方式:像素按行顺序流入,当第3行第3个像素到达时,首个完整3×3窗口形成,此后每个有效输入像素产生一个窗口输出

核心实现

module line_buffer #(
    parameter DATA_WIDTH = 8,
    parameter LINE_WIDTH = 64,
    parameter LINE_NUM   = 3
)(
    input  wire clk,
    input  wire rst_n,
    input  wire [DATA_WIDTH-1:0] pixel_in,
    input  wire valid_in,
    input  wire line_start,
    output wire [DATA_WIDTH-1:0] win_00, win_01, win_02,
    output wire [DATA_WIDTH-1:0] win_10, win_11, win_12,
    output wire [DATA_WIDTH-1:0] win_20, win_21, win_22,
    output reg  valid_out
);

    // 行缓冲存储:LINE_NUM 行,每行 LINE_WIDTH 个像素
    reg [DATA_WIDTH-1:0] line_buf [LINE_NUM-1:0][LINE_WIDTH-1:0];

    // 行写指针与列计数
    reg [$clog2(LINE_NUM)-1:0]   wr_line;
    reg [$clog2(LINE_WIDTH)-1:0] col_cnt;

    // 像素写入控制
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_line <= 0;
            col_cnt <= 0;
        end else if (valid_in) begin
            line_buf[wr_line][col_cnt] <= pixel_in;
            if (line_start)
                col_cnt <= 0;
            else if (col_cnt == LINE_WIDTH-1)
                col_cnt <= 0;
            else
                col_cnt <= col_cnt + 1;

            if (line_start) begin
                if (wr_line == LINE_NUM-1)
                    wr_line <= 0;
                else
                    wr_line <= wr_line + 1;
            end
        end
    end

    // 窗口读取:当前写入位置为窗口右下角 (wr_line, col_cnt)
    // 窗口左上角为 (wr_line-2, col_cnt-2),需循环寻址
    wire [$clog2(LINE_NUM)-1:0]   r0 = (wr_line == 0) ? LINE_NUM-1 : wr_line-1;
    wire [$clog2(LINE_NUM)-1:0]   r1 = wr_line;
    wire [$clog2(LINE_NUM)-1:0]   r2 = (wr_line == LINE_NUM-1) ? 0 : wr_line+1;

    wire [$clog2(LINE_WIDTH)-1:0] c0 = (col_cnt < 2) ? (LINE_WIDTH + col_cnt - 2) : (col_cnt - 2);
    wire [$clog2(LINE_WIDTH)-1:0] c1 = (col_cnt < 1) ? (LINE_WIDTH - 1) : (col_cnt - 1);
    wire [$clog2(LINE_WIDTH)-1:0] c2 = col_cnt;

    // 组合逻辑并行读取9个窗口像素
    assign win_00 = line_buf[r0][c0];
    assign win_01 = line_buf[r0][c1];
    assign win_02 = line_buf[r0][c2];
    assign win_10 = line_buf[r1][c0];
    assign win_11 = line_buf[r1][c1];
    assign win_12 = line_buf[r1][c2];
    assign win_20 = line_buf[r2][c0];
    assign win_21 = line_buf[r2][c1];
    assign win_22 = line_buf[r2][c2];

    // 窗口有效:第3行第3列像素写入后产生有效输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            valid_out <= 1'b0;
        else if (valid_in && (wr_line == LINE_NUM-1) && (col_cnt == LINE_WIDTH-1))
            valid_out <= 1'b1;
        else if (valid_in)
            valid_out <= 1'b1;
        else
            valid_out <= 1'b0;
    end

endmodule

运行与验证

Testbench要点:

// 输入3行数据,每行64个像素,像素值 = 行号*100 + 列号
// 例如:第0行像素值0~63,第1行像素值100~163,第2行像素值200~263
// 当第2行第2个像素(值202)输入后,valid_out拉高
// 此时 win_00 应等于 0(第0行第0列),win_11 应等于 202(当前像素)

// 验证方法:
// 1. 前两行输入时 valid_out 保持为0
// 2. 第3行第3个像素输入后 valid_out=1
// 3. 检查 win_00=0, win_01=1, win_02=2
//    win_10=100, win_11=101, win_12=102
//    win_20=200, win_21=201, win_22=202
// 4. 继续输入第3行第4个像素(203),窗口右移:
//    win_00=1, win_01=2, win_02=3
//    win_10=101, win_11=102, win_12=103
//    win_20=201, win_21=202, win_22=203

通过条件:仿真中窗口数据与上述预期完全一致,且 valid_out 时序正确(第3行第3个像素输入后拉高,之后每个有效像素持续拉高)。

关键设计说明

  • 行缓冲采用循环写指针,每行写满后自动切换至下一行,第3行写满后回卷至第0行,实现滚动更新
  • 窗口读取采用循环寻址,保证图像边缘(第0列/第0行)也能正确输出邻域数据(回卷到行尾/行首)
  • 窗口数据为组合逻辑输出,随像素时钟实时变化;valid_out 为寄存器输出,与窗口数据对齐
  • 参数化设计:修改 LINE_WIDTH 和 LINE_NUM 即可适配不同分辨率与窗口大小