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
运行与验证方法
- 仿真验证:使用Icarus Verilog运行
iverilog -o sim tb_mac_array.v mac_array.v && vvp sim,观察PASS/FAIL打印信息。 - 尺寸重配置:将测试平台中
ROWS/COLS改为8或16,重新仿真验证参数化正确性。 - 综合检查:在Vivado/Quartus中例化时修改参数,确认生成的硬件资源随尺寸线性增长。
通过标准:仿真输出全部PASS,且修改参数为8x8、16x16后功能仍正确,即证明阵列尺寸可配置。
常见问题
- 累加器位宽溢出:8位有符号乘法结果为16位,连续累加时需保证
ACC_W足够。本设计取32位,可支持65536次累加不溢出,满足多数卷积场景。 - 参数修改后仿真失败:修改
ROWS/COLS时需同步更新测试平台中数组声明与数据初始化循环范围。 - 综合性能:大尺寸阵列(16x16)组合逻辑路径变长,建议在乘法器输出插入流水寄存器以满足时序收敛。