4. 低功耗MAC单元设计
任务目标
设计一个低功耗MAC(乘累加)单元,通过操作数隔离技术降低动态功耗:当使能信号无效时,锁存输入操作数,阻止无效翻转传播到乘法器与加法器内部,同时保持输出寄存器不变。最终通过仿真验证功能正确性,并对比隔离前后的翻转活动。
实现方案
采用操作数隔离(Operand Isolation)而非门控时钟,原因在于:门控时钟需要额外时钟树综合约束,且在后端实现中可能引入毛刺;操作数隔离仅需在数据通路上添加锁存器,逻辑简单、可移植性强。核心思想:当mac_en为低时,将乘法器输入锁存为0,使内部节点保持稳定,仅时钟与使能信号翻转,从而降低动态功耗。
RTL代码(mac_lowpower.v)
module mac_lowpower #(
parameter DW = 8 // 数据位宽
)(
input wire clk,
input wire rst_n,
input wire mac_en, // 乘累加使能
input wire [DW-1:0] a_in,
input wire [DW-1:0] b_in,
output reg [2*DW+1:0] acc_out // 累加输出(含符号扩展)
);
// 操作数隔离锁存
reg [DW-1:0] a_iso, b_iso;
always @(*) begin
if (mac_en) begin
a_iso = a_in;
b_iso = b_in;
end else begin
a_iso = {DW{1'b0}};
b_iso = {DW{1'b0}};
end
end
// 乘法与累加
wire [2*DW-1:0] product = a_iso * b_iso;
wire [2*DW+1:0] sum = acc_out + product;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
acc_out <= 0;
else if (mac_en)
acc_out <= sum;
// mac_en=0时保持原值
end
endmodule
测试平台(tb_mac.v)
module tb_mac;
parameter DW = 8;
reg clk = 0, rst_n = 0, mac_en = 0;
reg [DW-1:0] a_in, b_in;
wire [2*DW+1:0] acc_out;
mac_lowpower #(.DW(DW)) uut (
.clk(clk), .rst_n(rst_n), .mac_en(mac_en),
.a_in(a_in), .b_in(b_in), .acc_out(acc_out)
);
always #5 clk = ~clk;
// 翻转活动统计(用于功耗对比)
integer toggle_cnt_iso, toggle_cnt_raw;
reg [DW-1:0] prev_a_iso, prev_b_iso, prev_a_raw, prev_b_raw;
initial begin
$dumpfile("mac_lowpower.vcd");
$dumpvars(0, tb_mac);
// 复位
rst_n = 0; #20 rst_n = 1;
// 使能期间输入翻转
mac_en = 1;
a_in = 8'h0F; b_in = 8'h03; #10;
a_in = 8'hF0; b_in = 8'h0C; #10;
a_in = 8'h55; b_in = 8'hAA; #10;
a_in = 8'hA5; b_in = 8'h5A; #10;
// 关闭使能,输入继续翻转(应被隔离)
mac_en = 0;
a_in = 8'hFF; b_in = 8'h00; #10;
a_in = 8'h00; b_in = 8'hFF; #10;
a_in = 8'h33; b_in = 8'hCC; #10;
a_in = 8'hCC; b_in = 8'h33; #10;
// 重新使能,验证功能恢复
mac_en = 1;
a_in = 8'h02; b_in = 8'h03; #10;
#50 $finish;
end
// 统计隔离后输入翻转次数
always @(posedge clk) begin
if (rst_n) begin
if (a_iso !== prev_a_iso) toggle_cnt_iso = toggle_cnt_iso + 1;
if (b_iso !== prev_b_iso) toggle_cnt_iso = toggle_cnt_iso + 1;
prev_a_iso = a_iso;
prev_b_iso = b_iso;
end
end
// 统计原始输入翻转次数(对比用)
always @(posedge clk) begin
if (rst_n) begin
if (a_in !== prev_a_raw) toggle_cnt_raw = toggle_cnt_raw + 1;
if (b_in !== prev_b_raw) toggle_cnt_raw = toggle_cnt_raw + 1;
prev_a_raw = a_in;
prev_b_raw = b_in;
end
end
// 内部信号引用(用于统计)
wire [DW-1:0] a_iso = uut.a_iso;
wire [DW-1:0] b_iso = uut.b_iso;
initial begin
$monitor("Time=%0t en=%b a=%h b=%h acc=%h | iso_toggle=%0d raw_toggle=%0d",
$time, mac_en, a_in, b_in, acc_out, toggle_cnt_iso, toggle_cnt_raw);
end
endmodule
运行与验证
- 仿真运行:使用Icarus Verilog执行以下命令:
iverilog -o mac_tb mac_lowpower.v tb_mac.v
vvp mac_tb
- 功能正确性判定:观察
acc_out,在使能期间应逐次累加乘积;使能关闭期间输出保持不变;重新使能后继续正确累加。预期最终累加值:0x0F*0x03 + 0xF0*0x0C + 0x55*0xAA + 0xA5*0x5A + 0x02*0x03 = 0x2D + 0xB40 + 0x3872 + 0x3B22 + 0x06 = 0x7A07(按无符号计算,位宽足够)。 - 功耗降低验证:仿真结束时观察
toggle_cnt_iso与toggle_cnt_raw。由于使能关闭期间输入被锁存为0,toggle_cnt_iso应显著小于toggle_cnt_raw,证明隔离后进入乘法器的翻转减少,动态功耗随之降低。
综合与实现说明
在DC综合或Vivado中,该RTL无需额外约束即可综合。操作数隔离逻辑(两个多路选择器)由综合工具自动映射为标准单元。实际功耗对比可在综合后使用PT(PrimeTime)或Vivado Power Analysis,分别对有/无隔离结构的网表在相同输入激励下估算功耗。本案例仅提供RTL级功能验证与翻转活动对比,作为功耗优化的设计依据。