3. MAC阵列流水线优化
任务目标
在MAC阵列的数据通路中插入流水线寄存器,将组合逻辑路径分段,从而提升最高时钟频率。通过对比不同流水线级数下的时序余量与吞吐量,理解流水线深度与性能之间的权衡关系。
设计规格
构建一个4×4 MAC阵列,每个MAC单元执行8位有符号乘法累加运算。输入为两个4×4矩阵A和B,输出为4×4累加结果矩阵C。设计三种配置:无流水线(纯组合逻辑)、1级流水线(乘法器输出后插入寄存器)、2级流水线(乘法器输出和加法器输出各插一级寄存器)。
RTL实现(2级流水线版本)
// mac_array_pipelined.v
module mac_array_pipelined #(
parameter DATA_W = 8,
parameter ACC_W = 20,
parameter N = 4
)(
input wire clk,
input wire rst_n,
input wire [DATA_W-1:0] a [0:N-1][0:N-1], // 输入矩阵A
input wire [DATA_W-1:0] b [0:N-1][0:N-1], // 输入矩阵B
output reg [ACC_W-1:0] c [0:N-1][0:N-1] // 输出矩阵C
);
// 第0级:乘法结果寄存器
reg [DATA_W*2-1:0] mult_reg [0:N-1][0:N-1];
// 第1级:部分累加寄存器
reg [ACC_W-1:0] acc_reg [0:N-1][0:N-1];
integer i, j, k;
// 流水线第1级:乘法
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0; i < N; i = i + 1)
for (j = 0; j < N; j = j + 1)
mult_reg[i][j] <= 0;
end else begin
for (i = 0; i < N; i = i + 1)
for (j = 0; j < N; j = j + 1)
mult_reg[i][j] <= a[i][j] * b[i][j];
end
end
// 流水线第2级:沿列方向累加
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0; i < N; i = i + 1)
for (j = 0; j < N; j = j + 1)
acc_reg[i][j] <= 0;
end else begin
for (i = 0; i < N; i = i + 1) begin
for (j = 0; j < N; j = j + 1) begin
acc_reg[i][j] <= 0;
for (k = 0; k < N; k = k + 1)
acc_reg[i][j] <= acc_reg[i][j] + mult_reg[i][k];
end
end
end
end
// 输出赋值
always @(*) begin
for (i = 0; i < N; i = i + 1)
for (j = 0; j < N; j = j + 1)
c[i][j] = acc_reg[i][j];
end
endmodule
流水线级数切换说明
上述代码为2级流水线结构。若需对比无流水线版本,将两个always块合并为纯组合逻辑(直接assign c[i][j] = sum(a[i][k]*b[k][j]));若需1级流水线,仅保留乘法寄存器,累加部分改为组合逻辑。三种配置的顶层接口保持一致,便于测试平台复用。
测试平台与验证
// tb_mac_pipeline.v
`timescale 1ns/1ps
module tb_mac_pipeline;
reg clk = 0, rst_n = 0;
reg [7:0] a [0:3][0:3];
reg [7:0] b [0:3][0:3];
wire [19:0] c [0:3][0:3];
// 实例化2级流水线DUT
mac_array_pipelined dut (
.clk(clk), .rst_n(rst_n),
.a(a), .b(b), .c(c)
);
always #5 clk = ~clk; // 100MHz时钟
integer i, j, k;
reg [19:0] expected [0:3][0:3];
initial begin
// 输入数据:A[i][j] = i+j+1, B[i][j] = i*2+j
for (i = 0; i < 4; i = i + 1)
for (j = 0; j < 4; j = j + 1) begin
a[i][j] = i + j + 1;
b[i][j] = i * 2 + j;
end
// 计算期望结果(软件参考模型)
for (i = 0; i < 4; i = i + 1)
for (j = 0; j < 4; j = j + 1) begin
expected[i][j] = 0;
for (k = 0; k < 4; k = k + 1)
expected[i][j] = expected[i][j] + (a[i][k] * b[k][j]);
end
// 复位释放
#20 rst_n = 1;
// 等待流水线填充(2级流水线需2个时钟周期)
#30;
// 检查输出
for (i = 0; i < 4; i = i + 1)
for (j = 0; j < 4; j = j + 1) begin
if (c[i][j] !== expected[i][j]) begin
$display("MISMATCH at [%0d][%0d]: got %0d, expected %0d",
i, j, c[i][j], expected[i][j]);
$finish;
end
end
$display("TEST PASSED");
$finish;
end
endmodule
运行与综合方法
- 仿真验证:使用Vivado或ModelSim运行
tb_mac_pipeline.v,观察打印的TEST PASSED信息。 - 时序分析:在Vivado中对三种配置分别执行综合与实现,报告
WNS(最差负时序裕量)与最高可达时钟频率。 - 吞吐量对比:固定输入矩阵,测量从输入有效到输出稳定的时钟周期数。无流水线为1周期,1级流水线为2周期,2级流水线为3周期。
判定标准
- 仿真通过:测试平台输出
TEST PASSED,无MISMATCH报告。 - 时序改善:2级流水线配置的WNS优于无流水线配置,表明关键路径被有效切断。
- 吞吐量理解:确认流水线每增加一级,输出延迟增加1个时钟周期,但最高时钟频率提升,整体吞吐量(数据率×频率)随流水线深度增加而提高。
常见问题
- 输出延迟:流水线版本输出比输入晚2~3个周期,测试平台需等待足够周期再比对结果。
- 累加顺序:本设计按列方向累加,与数学定义的行列乘法一致;若需行方向,需调整累加索引。
- 位宽溢出:8位×8位乘法结果为16位,4次累加最大需18位,ACC_W取20位留有余量。