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

运行与验证

  1. 仿真运行:使用Icarus Verilog执行以下命令:
iverilog -o mac_tb mac_lowpower.v tb_mac.v
vvp mac_tb
  1. 功能正确性判定:观察acc_out,在使能期间应逐次累加乘积;使能关闭期间输出保持不变;重新使能后继续正确累加。预期最终累加值:0x0F*0x03 + 0xF0*0x0C + 0x55*0xAA + 0xA5*0x5A + 0x02*0x03 = 0x2D + 0xB40 + 0x3872 + 0x3B22 + 0x06 = 0x7A07(按无符号计算,位宽足够)。
  2. 功耗降低验证:仿真结束时观察toggle_cnt_iso与toggle_cnt_raw。由于使能关闭期间输入被锁存为0,toggle_cnt_iso应显著小于toggle_cnt_raw,证明隔离后进入乘法器的翻转减少,动态功耗随之降低。

综合与实现说明

在DC综合或Vivado中,该RTL无需额外约束即可综合。操作数隔离逻辑(两个多路选择器)由综合工具自动映射为标准单元。实际功耗对比可在综合后使用PT(PrimeTime)或Vivado Power Analysis,分别对有/无隔离结构的网表在相同输入激励下估算功耗。本案例仅提供RTL级功能验证与翻转活动对比,作为功耗优化的设计依据。