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即可适配不同分辨率与窗口大小