3. 可配置尺寸的滑动窗口生成器
任务目标
设计一个参数化窗口大小的滑动窗口生成器,通过参数WIN_SIZE配置窗口尺寸(支持3x3、5x5等),从串行像素输入流中生成对应的二维窗口数据,供后续卷积运算直接使用。
设计要点
- 输入为单像素流(每时钟1个有效像素),输出为
WIN_SIZE*WIN_SIZE个并行像素,构成当前窗口快照。 - 内部使用行缓冲(Line Buffer)暂存
WIN_SIZE-1行数据,每行深度为IMG_WIDTH。 - 窗口数据在像素有效时逐周期更新,输出与输入像素对齐(当前像素位于窗口右下角)。
- 通过
parameter实现窗口尺寸可配置,无需修改RTL逻辑结构。
Verilog实现
module sliding_window #(
parameter WIN_SIZE = 3, // 窗口尺寸:3或5
parameter IMG_WIDTH = 8, // 图像行宽(像素数)
parameter DATA_W = 8 // 像素位宽
)(
input wire clk,
input wire rst_n,
input wire px_valid, // 输入像素有效
input wire [DATA_W-1:0] px_in, // 输入像素
output reg [WIN_SIZE*WIN_SIZE*DATA_W-1:0] win_data, // 窗口并行输出
output reg win_valid // 窗口有效(已填满)
);
// 行缓冲:WIN_SIZE-1行,每行IMG_WIDTH个像素
reg [DATA_W-1:0] line_buf [WIN_SIZE-1][IMG_WIDTH-1:0];
// 当前窗口寄存器
reg [DATA_W-1:0] win_reg [WIN_SIZE-1:0][WIN_SIZE-1:0];
integer i, j;
reg [7:0] col_cnt; // 当前列计数
reg [7:0] row_cnt; // 当前行计数
// 列计数与行计数
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 == IMG_WIDTH-1) begin
col_cnt <= 0;
if (row_cnt == IMG_WIDTH-1)
row_cnt <= 0;
else
row_cnt <= row_cnt + 1;
end else begin
col_cnt <= col_cnt + 1;
end
end
end
// 写入行缓冲(延迟1拍,使当前像素与窗口输出对齐)
reg [DATA_W-1:0] px_d1;
reg px_valid_d1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
px_d1 <= 0;
px_valid_d1 <= 0;
end else begin
px_d1 <= px_in;
px_valid_d1 <= px_valid;
end
end
// 行缓冲写入与移位
integer k;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0; i < WIN_SIZE-1; i = i + 1)
for (j = 0; j < IMG_WIDTH; j = j + 1)
line_buf[i][j] <= 0;
end else if (px_valid_d1) begin
// 第0行缓冲接收新像素
for (j = IMG_WIDTH-1; j > 0; j = j - 1)
line_buf[0][j] <= line_buf[0][j-1];
line_buf[0][0] <= px_d1;
// 其余行缓冲从上一行移位
for (i = 1; i < WIN_SIZE-1; i = i + 1) begin
for (j = IMG_WIDTH-1; j > 0; j = j - 1)
line_buf[i][j] <= line_buf[i][j-1];
line_buf[i][0] <= line_buf[i-1][IMG_WIDTH-1];
end
end
end
// 窗口寄存器更新
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0; i < WIN_SIZE; i = i + 1)
for (j = 0; j < WIN_SIZE; j = j + 1)
win_reg[i][j] <= 0;
win_valid <= 0;
end else if (px_valid_d1) begin
// 新像素进入窗口右下角
win_reg[WIN_SIZE-1][WIN_SIZE-1] <= px_d1;
// 窗口内右移
for (i = 0; i < WIN_SIZE; i = i + 1)
for (j = WIN_SIZE-1; j > 0; j = j - 1)
win_reg[i][j] <= win_reg[i][j-1];
// 窗口行间下移(从行缓冲取上一行最右像素)
for (i = WIN_SIZE-1; i > 0; i = i - 1)
win_reg[i][0] <= win_reg[i-1][WIN_SIZE-1];
win_reg[0][0] <= line_buf[WIN_SIZE-2][IMG_WIDTH-1];
// 窗口有效条件:行列计数均达到窗口边界
if (row_cnt >= WIN_SIZE-1 && col_cnt >= WIN_SIZE-1)
win_valid <= 1;
else
win_valid <= 0;
end else begin
win_valid <= 0;
end
end
// 并行输出
always @(*) begin
for (i = 0; i < WIN_SIZE; i = i + 1)
for (j = 0; j < WIN_SIZE; j = j + 1)
win_data[(i*WIN_SIZE + j)*DATA_W +: DATA_W] = win_reg[i][j];
end
endmodule
仿真测试要点
- 实例化时设置
WIN_SIZE=3、IMG_WIDTH=8,输入连续递增像素值0~63。 - 观察
win_valid信号:当输入第(WIN_SIZE-1)*IMG_WIDTH + WIN_SIZE-1个像素(即第3行第3列)时,win_valid首次拉高。 - 检查
win_data输出:窗口内容应为当前像素及其上方、左方相邻像素,例如第3行第3列像素值为18时,窗口应为[8,9,10; 16,17,18](按行展开)。 - 修改
WIN_SIZE=5重新仿真,验证窗口尺寸参数化生效。
验证通过标准
win_valid拉高时机与理论计算一致(第(WIN_SIZE-1)*IMG_WIDTH + WIN_SIZE-1个有效像素)。win_data中每个像素位置与输入流顺序严格对应,无错位、无重复。- 参数
WIN_SIZE改为5后,窗口输出尺寸变为25个像素,且时序逻辑无需修改。