1. 3x3卷积核的滑动窗口生成器

任务目标

实现一个3x3卷积核的滑动窗口生成器,从串行输入的像素流中提取3x3窗口数据,为后续卷积计算提供并行窗口输出。输入为单像素时钟流,输出为9个对齐的像素值。

接口定义

module window_3x3 #(
    parameter DW = 8,      // 像素位宽
    parameter IW = 8,      // 图像宽度(像素数)
    parameter IH = 8       // 图像高度(像素数)
)(
    input  wire             clk,
    input  wire             rst_n,
    input  wire             px_valid,   // 输入像素有效
    input  wire [DW-1:0]    px_in,      // 串行输入像素
    output reg  [DW-1:0]    w11, w12, w13,  // 窗口第1行
    output reg  [DW-1:0]    w21, w22, w23,  // 窗口第2行
    output reg  [DW-1:0]    w31, w32, w33,  // 窗口第3行
    output reg              win_valid   // 窗口有效(已填满3行)
);

实现方案

采用行缓冲(Line Buffer)结构:使用2个移位寄存器分别缓存第1行和第2行数据,当前输入像素作为第3行。每个时钟周期,当像素有效时,三行数据同步右移,形成3x3窗口。窗口有效信号在输入像素位于第3行第3列及之后的位置拉高。

关键完整代码

module window_3x3 #(
    parameter DW = 8,
    parameter IW = 8,
    parameter IH = 8
)(
    input  wire             clk,
    input  wire             rst_n,
    input  wire             px_valid,
    input  wire [DW-1:0]    px_in,
    output reg  [DW-1:0]    w11, w12, w13,
    output reg  [DW-1:0]    w21, w22, w23,
    output reg  [DW-1:0]    w31, w32, w33,
    output reg              win_valid
);
    // 行缓冲:每行缓存 IW 个像素
    reg [DW-1:0] line_buf [0:1][0:IW-1];  // [行][列]
    
    // 列计数器与行计数器
    reg [$clog2(IW)-1:0] col_cnt;
    reg [$clog2(IH)-1:0] row_cnt;
    
    // 内部窗口寄存器
    reg [DW-1:0] w11_r, w12_r, w13_r;
    reg [DW-1:0] w21_r, w22_r, w23_r;
    reg [DW-1:0] w31_r, w32_r, w33_r;
    
    integer i;
    
    // 行列计数
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            col_cnt <= 0;
            row_cnt <= 0;
        end else if (px_valid) begin
            if (col_cnt == IW-1) begin
                col_cnt <= 0;
                row_cnt <= (row_cnt == IH-1) ? 0 : row_cnt + 1;
            end else begin
                col_cnt <= col_cnt + 1;
            end
        end
    end
    
    // 行缓冲写入与移位
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i < IW; i = i + 1) begin
                line_buf[0][i] <= {DW{1'b0}};
                line_buf[1][i] <= {DW{1'b0}};
            end
        end else if (px_valid) begin
            // 第0行缓冲:写入当前像素,同时整体左移
            for (i = IW-1; i > 0; i = i - 1)
                line_buf[0][i] <= line_buf[0][i-1];
            line_buf[0][0] <= px_in;
            
            // 第1行缓冲:从第0行缓冲的移出端取数
            for (i = IW-1; i > 0; i = i - 1)
                line_buf[1][i] <= line_buf[1][i-1];
            line_buf[1][0] <= line_buf[0][IW-1];
        end
    end
    
    // 窗口输出组合逻辑
    always @(*) begin
        // 第1行窗口 = 第0行缓冲末尾3个
        w11_r = line_buf[0][IW-3];
        w12_r = line_buf[0][IW-2];
        w13_r = line_buf[0][IW-1];
        // 第2行窗口 = 第1行缓冲末尾3个
        w21_r = line_buf[1][IW-3];
        w22_r = line_buf[1][IW-2];
        w23_r = line_buf[1][IW-1];
        // 第3行窗口 = 当前输入像素及其前2个(需额外寄存器)
        w31_r = px_dly2;
        w32_r = px_dly1;
        w33_r = px_in;
    end
    
    // 当前行像素延迟
    reg [DW-1:0] px_dly1, px_dly2;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            px_dly1 <= {DW{1'b0}};
            px_dly2 <= {DW{1'b0}};
        end else if (px_valid) begin
            px_dly1 <= px_in;
            px_dly2 <= px_dly1;
        end
    end
    
    // 输出赋值
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            w11 <= 0; w12 <= 0; w13 <= 0;
            w21 <= 0; w22 <= 0; w23 <= 0;
            w31 <= 0; w32 <= 0; w33 <= 0;
            win_valid <= 0;
        end else begin
            w11 <= w11_r; w12 <= w12_r; w13 <= w13_r;
            w21 <= w21_r; w22 <= w22_r; w23 <= w23_r;
            w31 <= w31_r; w32 <= w32_r; w33 <= w33_r;
            // 窗口有效条件:已处理至少3行且当前列>=2
            win_valid <= (row_cnt >= 2) && (col_cnt >= 2) && px_valid;
        end
    end
    
endmodule

运行与验证

Testbench要点:

  1. 按行输入8x8灰度图像,每个时钟一个像素,px_valid拉高
  2. 观察win_valid拉高时的w11~w33输出
  3. 验证第3行第3列像素输入后,窗口内容应为图像左上角3x3区域
// 测试激励核心片段
initial begin
    rst_n = 0; px_valid = 0; px_in = 0;
    #20 rst_n = 1;
    // 输入8x8图像,像素值 = row*8 + col
    for (r = 0; r < 8; r = r + 1) begin
        for (c = 0; c < 8; c = c + 1) begin
            @(posedge clk);
            px_valid = 1;
            px_in = r*8 + c;
        end
    end
    @(posedge clk); px_valid = 0;
end

通过条件:当第3行第3列像素(值=18)输入后的下一个时钟沿,win_valid拉高,且w11=0, w12=1, w13=2, w21=8, w22=9, w23=10, w31=16, w32=17, w33=18。仿真波形中窗口数据与预期一致即验证通过。

边界说明

本设计针对图像尺寸≥3x3的情况。当图像边缘像素输入时,窗口包含边界外数据(填充为0),实际卷积时需根据边界处理策略(如复制、补零)调整。行缓冲深度由IW参数决定,综合时按实际图像宽度配置。