You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SystemVerilog实现的2×2最大池化模块是否可综合?

代码可综合性分析与优化方案

一、代码是否可综合?

你的代码是可综合的——Vivado综合完成且无不可综合提示,说明综合器能将其映射为硬件电路。ChatGPT的判断不准确,问题出在资源利用率严重超标,而非不可综合:

  • I/O利用率达31360%:因为你把28×28×64bit的整幅图像作为并行输入端口,相当于直接占用了50176个I/O引脚,这远远超出了任何FPGA的实际I/O数量。
  • LUT利用率达106%:代码为每个14×14的输出池化窗口都实例化了独立的max模块(共196个),每个模块要完成4个64bit数的比较,导致LUT资源被过度占用。

二、核心问题分析

你的实现是全并行架构,一次性处理整幅图像的所有池化窗口,这种方式在硬件上完全不现实:

  1. 无法提供足够的I/O引脚来承载整幅图像的并行输入;
  2. 过度并行化导致逻辑资源(LUT、FF)被大量消耗,超出FPGA的承载能力。

三、正确的硬件实现方式

FPGA上实现池化模块的合理方案是串行输入+分周期/流水线处理,通过行缓存(Line Buffer)逐行输入图像数据,缓存足够的行数据后,对2×2窗口进行最大值计算。以下是优化后的实现:

1. 行缓存式最大池化模块

`timescale 1ns / 1ps

module max_pooling_module 
#(
    parameter N = 64,
    parameter WIDTH = 28,
    parameter FILTER_SIZE = 2,
    parameter STRIDE = 2
)
(
    input logic clk,
    input logic rst_n,
    input logic signed [N-1:0] pixel_in,  // 串行输入单个像素
    input logic pixel_valid,              // 输入像素有效信号
    output logic signed [N-1:0] pixel_out,// 串行输出池化结果
    output logic out_valid                // 输出结果有效信号
);
    localparam OUT_WIDTH = (WIDTH - FILTER_SIZE) / STRIDE + 1;
    localparam BUFFER_DEPTH = WIDTH;

    // 行缓存:存储上一行的像素数据
    logic signed [N-1:0] line_buffer [BUFFER_DEPTH];
    // 当前行和上一行的窗口数据
    logic signed [N-1:0] curr_pixel, prev_pixel;
    logic signed [N-1:0] window [FILTER_SIZE][FILTER_SIZE];
    // 计数器:跟踪当前输入的行、列位置
    logic [$clog2(WIDTH)-1:0] col_cnt;
    logic [$clog2(WIDTH)-1:0] row_cnt;
    // 池化结果寄存器
    logic signed [N-1:0] max_val;

    // 行缓存更新:当输入有效时,写入当前像素并读出上一行对应位置的像素
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            line_buffer <= '{default: '0};
            curr_pixel <= '0;
            prev_pixel <= '0;
        end else if(pixel_valid) begin
            line_buffer[col_cnt] <= pixel_in;
            curr_pixel <= pixel_in;
            prev_pixel <= line_buffer[col_cnt];
        end
    end

    // 行列计数器更新
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            col_cnt <= '0;
            row_cnt <= '0;
        end else if(pixel_valid) begin
            if(col_cnt == WIDTH - 1) begin
                col_cnt <= '0;
                row_cnt <= row_cnt + 1;
            end else begin
                col_cnt <= col_cnt + 1;
            end
        end
    end

    // 构建2×2窗口:仅当列未到边界时有效
    always_comb begin
        window[0][0] = prev_pixel;          
        window[0][1] = (col_cnt < WIDTH-1) ? line_buffer[col_cnt+1] : '0;
        window[1][0] = curr_pixel;          
        window[1][1] = (col_cnt < WIDTH-1) ? pixel_in : '0;
    end

    // 计算窗口最大值:仅在满足步长要求的位置输出结果
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            max_val <= '0;
            out_valid <= '0;
            pixel_out <= '0;
        end else begin
            out_valid <= '0;
            // 仅在偶数行、偶数列(步长2)且窗口有效时计算
            if(pixel_valid && (row_cnt % STRIDE == 1) && (col_cnt % STRIDE == 1) && (col_cnt < WIDTH - 1)) begin
                max_val = window[0][0];
                if(window[0][1] > max_val) max_val = window[0][1];
                if(window[1][0] > max_val) max_val = window[1][0];
                if(window[1][1] > max_val) max_val = window[1][1];
                pixel_out <= max_val;
                out_valid <= '1;
            end
        end
    end

endmodule

2. 优化要点说明

  • 串行输入:仅用1个64bit的像素输入端口,彻底解决I/O资源超标问题;
  • 行缓存:用深度为图像宽度的寄存器组存储上一行数据,配合当前行数据构建2×2窗口;
  • 分周期处理:通过行列计数器跟踪位置,仅在满足步长要求的位置计算窗口最大值,减少不必要的逻辑操作;
  • 资源复用:仅用一套比较逻辑完成所有窗口的最大值计算,大幅降低LUT资源占用。

四、补充说明

如果需要更高的吞吐量,也可以采用流水线并行架构:在每行内同时处理多个窗口,但仍保持逐行输入的方式,平衡资源占用和吞吐量需求。

内容的提问来源于stack exchange,提问作者Becker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:44:51