3. 卷积计算单元(MAC)

3. 卷积计算单元(MAC) 学习卡片

任务目标

实现一个可综合的乘累加单元(MAC),完成3×3卷积核与图像窗口的点积运算。输入为9个像素值与9个权重值,输出为单次乘累加结果。

接口定义

module mac_3x3 #(
    parameter DATA_WIDTH = 8,
    parameter ACC_WIDTH  = 24
)(
    input  wire                     clk,
    input  wire                     rst_n,
    input  wire                     valid_in,
    input  wire [DATA_WIDTH-1:0]    pixel [0:8],   // 3x3窗口像素
    input  wire [DATA_WIDTH-1:0]    weight [0:8],  // 3x3卷积核权重
    output reg                      valid_out,
    output reg  [ACC_WIDTH-1:0]     result         // 点积结果
);

核心实现

module mac_3x3 #(
    parameter DATA_WIDTH = 8,
    parameter ACC_WIDTH  = 24
)(
    input  wire                     clk,
    input  wire                     rst_n,
    input  wire                     valid_in,
    input  wire [DATA_WIDTH-1:0]    pixel [0:8],
    input  wire [DATA_WIDTH-1:0]    weight [0:8],
    output reg                      valid_out,
    output reg  [ACC_WIDTH-1:0]     result
);
    // 乘积宽度 = 8x8 = 16位,9个乘积累加最大需要 16+4 = 20位
    // ACC_WIDTH=24 留有余量,防止溢出
    reg [DATA_WIDTH*2-1:0] prod [0:8];
    reg [ACC_WIDTH-1:0]   sum;
    integer i;

    // 流水线第一级:乘法
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i < 9; i = i + 1)
                prod[i] <= 0;
        end else if (valid_in) begin
            for (i = 0; i < 9; i = i + 1)
                prod[i] <= pixel[i] * weight[i];
        end
    end

    // 流水线第二级:加法树累加
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            sum <= 0;
        end else begin
            // 加法树:3级并行加法
            sum <= ({ACC_WIDTH{1'b0}}, prod[0]) + ({ACC_WIDTH{1'b0}}, prod[1]) +
                   ({ACC_WIDTH{1'b0}}, prod[2]) + ({ACC_WIDTH{1'b0}}, prod[3]) +
                   ({ACC_WIDTH{1'b0}}, prod[4]) + ({ACC_WIDTH{1'b0}}, prod[5]) +
                   ({ACC_WIDTH{1'b0}}, prod[6]) + ({ACC_WIDTH{1'b0}}, prod[7]) +
                   ({ACC_WIDTH{1'b0}}, prod[8]);
        end
    end

    // 流水线第三级:输出寄存
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            valid_out <= 1'b0;
            result    <= {ACC_WIDTH{1'b0}};
        end else begin
            valid_out <= valid_in;  // 两级延迟后输出有效标志
            result    <= sum;
        end
    end

endmodule

设计说明

采用三级流水线结构:第一级完成9个并行乘法,第二级通过加法树并行累加,第三级寄存输出。valid_out信号延迟两级与数据同步,保证输出时序正确。加法树使用位扩展防止累加溢出,ACC_WIDTH=24位可容纳最大结果(8位无符号数×8位无符号数×9=2^20量级)。

仿真验证

// testbench 关键片段
module tb_mac_3x3;
    reg clk = 0, rst_n = 0, valid_in = 0;
    reg [7:0] pixel [0:8], weight [0:8];
    wire valid_out;
    wire [23:0] result;

    mac_3x3 dut(.*);

    always #5 clk = ~clk;

    initial begin
        // 复位
        #20 rst_n = 1;

        // 输入:pixel = 1~9, weight = 1~9
        for (int i = 0; i < 9; i++) begin
            pixel[i] = i + 1;
            weight[i] = i + 1;
        end

        @(posedge clk);
        valid_in = 1;
        @(posedge clk);
        valid_in = 0;

        // 等待流水线输出
        #30;
        $display("result = %0d", result);
        // 预期:1*1+2*2+...+9*9 = 285
        if (result == 285 && valid_out)
            $display("TEST PASSED");
        else
            $display("TEST FAILED");
        $finish;
    end
endmodule

运行与验证

  1. 使用Vivado/Quartus或ModelSim/Verilator编译上述RTL与testbench;
  2. 运行仿真,观察波形或控制台输出;
  3. 通过条件:当输入pixel[i]=i+1、weight[i]=i+1时,result输出285,且valid_out在数据有效后延迟2个时钟周期拉高。

综合建议

若需提升吞吐率,可将乘法级与加法级之间插入额外寄存器;若资源紧张,可改为串行累加(需9个周期)。本设计为全并行结构,单周期完成一次3×3点积,适合作为卷积流水线的核心计算单元。