2. 可配置MAC阵列尺寸

任务目标

设计一个参数化MAC(乘累加)阵列,通过Verilog参数MAC_ROWS与MAC_COLS配置阵列尺寸(4x4、8x8、16x16),实现不同算力需求下的硬件复用。阵列每个单元完成一次8位有符号乘法与32位累加,支持同步使能与清零。

实现方案

采用二维generate循环生成MAC单元网格,每个单元内部实例化一个乘法器与加法器。阵列尺寸完全由参数决定,无需修改RTL代码即可重新综合。输入数据按行广播、按列独立,权重按列广播、按行独立,符合典型脉动阵列数据流。

关键完整代码

// mac_array.v - 参数化MAC阵列
module mac_array #(
    parameter MAC_ROWS = 4,        // 行数(可配4/8/16)
    parameter MAC_COLS = 4,        // 列数(可配4/8/16)
    parameter DATA_W   = 8,        // 输入数据位宽
    parameter ACC_W    = 32        // 累加器位宽
)(
    input  wire                     clk,
    input  wire                     rst_n,
    input  wire                     en,          // 同步使能
    input  wire                     clr,         // 同步清零(高有效)
    input  wire [DATA_W-1:0]        row_data [MAC_ROWS-1:0], // 行输入
    input  wire [DATA_W-1:0]        col_wt   [MAC_COLS-1:0], // 列权重
    output wire [ACC_W-1:0]         acc_out  [MAC_ROWS*MAC_COLS-1:0]
);

    // 内部累加寄存器
    reg [ACC_W-1:0] acc_reg [MAC_ROWS*MAC_COLS-1:0];

    // 生成MAC单元网格
    genvar i, j;
    generate
        for (i = 0; i < MAC_ROWS; i = i + 1) begin : row_loop
            for (j = 0; j < MAC_COLS; j = j + 1) begin : col_loop
                // 当前单元索引
                localparam integer idx = i * MAC_COLS + j;

                // 乘法:行数据 * 列权重
                wire signed [DATA_W-1:0] a = row_data[i];
                wire signed [DATA_W-1:0] b = col_wt[j];
                wire signed [2*DATA_W-1:0] product;

                assign product = a * b;

                // 累加逻辑
                always @(posedge clk or negedge rst_n) begin
                    if (!rst_n)
                        acc_reg[idx] <= {ACC_W{1'b0}};
                    else if (clr)
                        acc_reg[idx] <= {ACC_W{1'b0}};
                    else if (en)
                        acc_reg[idx] <= acc_reg[idx] + {{(ACC_W-2*DATA_W){product[2*DATA_W-1]}}, product};
                end

                assign acc_out[idx] = acc_reg[idx];
            end
        end
    endgenerate

endmodule

测试平台与验证

// tb_mac_array.v - 参数化验证
`timescale 1ns/1ps
module tb_mac_array;

    parameter ROWS = 4;
    parameter COLS = 4;

    reg clk = 0, rst_n = 0, en = 0, clr = 0;
    reg [7:0] row_data [ROWS-1:0];
    reg [7:0] col_wt   [COLS-1:0];
    wire [31:0] acc_out [ROWS*COLS-1:0];

    // 待测设计
    mac_array #(.MAC_ROWS(ROWS), .MAC_COLS(COLS)) dut (
        .clk(clk), .rst_n(rst_n), .en(en), .clr(clr),
        .row_data(row_data), .col_wt(col_wt), .acc_out(acc_out)
    );

    always #5 clk = ~clk;

    integer k;
    initial begin
        // 复位
        #10 rst_n = 1; clr = 1; #10 clr = 0;

        // 输入数据:行i = i+1,列j = j+1
        for (k = 0; k < ROWS; k = k + 1) row_data[k] = k + 1;
        for (k = 0; k < COLS; k = k + 1) col_wt[k] = k + 1;

        // 使能累加
        en = 1;
        repeat (3) @(posedge clk);   // 累加3次
        en = 0;

        // 检查单元(0,0):3次累加 1*1=1 → 3
        if (acc_out[0] !== 32'd3) $display("FAIL: acc[0]=%0d, expect 3", acc_out[0]);
        else $display("PASS: acc[0]=%0d", acc_out[0]);

        // 检查单元(1,2):3次累加 2*3=6 → 18
        if (acc_out[1*COLS+2] !== 32'd18) $display("FAIL: acc[5]=%0d, expect 18", acc_out[1*COLS+2]);
        else $display("PASS: acc[5]=%0d", acc_out[1*COLS+2]);

        // 清零验证
        clr = 1; #10 clr = 0;
        if (acc_out[0] !== 32'd0) $display("FAIL: clear failed");
        else $display("PASS: clear works");

        $finish;
    end

    initial begin
        $dumpfile("tb_mac_array.vcd");
        $dumpvars(0, tb_mac_array);
    end

endmodule

运行与验证方法

  1. 仿真验证:使用Icarus Verilog运行iverilog -o sim tb_mac_array.v mac_array.v && vvp sim,观察PASS/FAIL打印信息。
  2. 尺寸重配置:将测试平台中ROWS/COLS改为8或16,重新仿真验证参数化正确性。
  3. 综合检查:在Vivado/Quartus中例化时修改参数,确认生成的硬件资源随尺寸线性增长。

通过标准:仿真输出全部PASS,且修改参数为8x8、16x16后功能仍正确,即证明阵列尺寸可配置。

4x4 MAC阵列数据流示意 行输入 r0 r1 r2 r3 列权重 c0 c1 c2 c3 MAC[0,0] MAC[0,1] MAC[0,2] MAC[0,3] MAC[1,0] MAC[1,1] MAC[1,2] MAC[1,3] MAC[2,0] MAC[2,1] MAC[2,2] MAC[2,3] MAC[3,0] MAC[3,1] MAC[3,2] MAC[3,3] 每个单元输出 acc = Σ(row_i × col_j) 共 ROWS×COLS 个 独立累加结果 参数化配置 MAC_ROWS × MAC_COLS 4×4 / 8×8 / 16×16

常见问题

  • 累加器位宽溢出:8位有符号乘法结果为16位,连续累加时需保证ACC_W足够。本设计取32位,可支持65536次累加不溢出,满足多数卷积场景。
  • 参数修改后仿真失败:修改ROWS/COLS时需同步更新测试平台中数组声明与数据初始化循环范围。
  • 综合性能:大尺寸阵列(16x16)组合逻辑路径变长,建议在乘法器输出插入流水寄存器以满足时序收敛。