Verilog RTL級低功耗設(shè)計(上)

2022-05-20 14:37 更新

下表顯示了在數(shù)字設(shè)計的各個層次上可減少功耗的百分比。RTL 級之后,功耗的減少量已經(jīng)非常有限。

設(shè)計層次 改善程度
系統(tǒng)級 50% ~ 90%
RTL 級 20% ~ 50%
門級 10% ~ 15%
晶體管級 5% ~ 10%
版圖級 < 5%

 作為一個編寫 Verilog 的偽碼農(nóng),系統(tǒng)級減少功耗的工作也可參與一些,但重點應(yīng)該放在 RTL 級來減少功耗。

下面就分 2 節(jié)來介紹從 RTL 級來減少功耗的常用方法。

并行與流水

對于一個功能模塊,可以通過并行的方式實現(xiàn),也可以通過流水線的方式實現(xiàn),這兩種方法都是用資源換速度。在一定的場合下靈活的使用這兩種方法,可以降低功耗。

并行處理

并行處理,可以同時處理多條執(zhí)行語句,使執(zhí)行效率變高。所以在滿足工作需求的條件下,采用并行處理,可降低系統(tǒng)工作頻率,減少功耗。

例如,采用 1 個乘法器和 2 個乘法器(并行)來實現(xiàn) 4 個數(shù)據(jù)乘加運算的代碼描述分別如下:

//===========================================
//1 multiplier, high speed
module  mul1_hs
    (
        input           clk ,           //200MHz
        input           rstn ,
        input           en  ,
        input [3:0]     mul1 ,          //data in
        input [3:0]     mul2 ,          //data in
        output          dout_en ,
        output [8:0]    dout
     );

    reg                  flag ;
    reg                  en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            flag   <= 1'b0 ;
            en_r   <= 1'b0 ;
        end
        else if (en) begin
            flag   <= ~flag ;
            en_r   <= 1'b1 ;
        end
        else begin
            flag   <= 1'b0 ;
            en_r   <= 1'b0 ;
        end
    end

    wire [7:0]           result = mul1 * mul2 ;

    // data output en
    reg [7:0]            res1_r, res2_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            res1_r         <= 'b0 ;
            res2_r         <= 'b0 ;
        end
        else if (en & !flag) begin
            res1_r         <= result ;
        end
        else if (en & flag) begin
            res2_r         <= result ;
        end
    end

    assign dout_en = en_r & !flag ;
    assign dout = res1_r + res2_r ;

endmodule

//===========================================
// 2 multiplier2, low speed
module  mul2_ls
    (
        input           clk ,           //100MHz
        input           rstn ,
        input           en  ,
        input [3:0]     mul1 ,          //data in
        input [3:0]     mul2 ,          //data in
        input [3:0]     mul3 ,          //data in
        input [3:0]     mul4 ,          //data in
        output          dout_en,
        output [8:0]    dout
     );

    wire [7:0]           result1 = mul1 * mul2 ;
    wire [7:0]           result2 = mul3 * mul4 ;

    //en delay
    reg                  en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r           <= 1'b0 ;
        end
        else begin
          en_r           <= en ;
        end
    end

    // data output en
    reg [7:0]            res1_r, res2_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            res1_r         <= 'b0 ;
            res2_r         <= 'b0 ;
        end
        else if (en) begin
            res1_r         <= result1 ;
            res2_r         <= result2 ;
        end
    end
    assign dout          = res1_r + res2_r ;
    assign dout_en       = en_r ;

endmodule

testbench 描述如下。

`timescale 1ns/1ps
module test ;
    reg          rstn ;
    //mul1_hs
    reg          hs_clk;
    reg          hs_en ;
    reg [3:0]    hs_mul1 ;
    reg [3:0]    hs_mul2 ;
    wire         hs_dout_en ;
    wire [8:0]   hs_dout ;
    //mul1_ls
    reg          ls_clk = 0;
    reg          ls_en ;
    reg [3:0]    ls_mul1 ;
    reg [3:0]    ls_mul2 ;
    reg [3:0]    ls_mul3 ;
    reg [3:0]    ls_mul4 ;
    wire         ls_dout_en ;
    wire [8:0]   ls_dout ;

    //clock generating
    real         CYCLE_200MHz = 5 ; //
    always begin
        hs_clk = 0 ; #(CYCLE_200MHz/2) ;
        hs_clk = 1 ; #(CYCLE_200MHz/2) ;
    end
    always begin
        @(posedge hs_clk) ls_clk = ~ls_clk ;
    end

    //reset generating
    initial begin
        rstn      = 1'b0 ;
        #8 rstn      = 1'b1 ;
    end

    //motivation
    initial begin
        hs_mul1   = 0 ;
        hs_mul2   = 16 ;
        hs_en     = 0 ;
        #103 ;
        repeat(12) begin
            @(negedge hs_clk) ;
            hs_en          = 1 ;
            hs_mul1        = hs_mul1 + 1;
            hs_mul2        = hs_mul2 - 1;
        end
        hs_en = 0 ;
    end

    initial begin
        ls_mul1   = 1 ;
        ls_mul2   = 15 ;
        ls_mul3   = 2 ;
        ls_mul4   = 14 ;
        ls_en     = 0 ;
        #103 ;
        @(negedge ls_clk) ls_en = 1;
        repeat(5) begin
            @(negedge ls_clk) ;
           ls_mul1        = ls_mul1 + 2;
           ls_mul2        = ls_mul2 - 2;
           ls_mul3        = ls_mul3 + 2;
           ls_mul4        = ls_mul4 - 2;
        end
        ls_en = 0 ;
    end

    //module instantiation
    mul1_hs    u_mul1_hs
    (
      .clk              (hs_clk),
      .rstn             (rstn),
      .en               (hs_en),
      .mul1             (hs_mul1),
      .mul2             (hs_mul2),
      .dout             (hs_dout),
      .dout_en          (hs_dout_en)
    );

    mul2_ls    u_mul2_ls
    (
      .clk              (ls_clk),
      .rstn             (rstn),
      .en               (ls_en),
      .mul1             (ls_mul1),
      .mul2             (ls_mul2),
      .mul3             (ls_mul3),
      .mul4             (ls_mul4),
      .dout             (ls_dout),
      .dout_en          (ls_dout_en)
    );

    //simulation finish
    always begin
        #100;
        if ($time >= 1000)  begin
            #1 ;
            $finish ;
        end
    end
   
endmodule

仿真結(jié)果如下。

由圖可知,兩種實現(xiàn)方法輸出結(jié)果一致,但并行處理方法的工作頻率降低了一半,功耗會有所降低,此時設(shè)計面積也會有所增加。


流水線處理

在 《Verilog 教程》中講述過,一個連續(xù)工作的 N 級流水線設(shè)計,效率提升倍數(shù)約為 N。同并行設(shè)計一樣,采用流水線設(shè)計時,也可以適當(dāng)降低工作頻率來減少功耗。

從另一個角度講,流水線設(shè)計可以將一個較長的組合路徑分成 N 級流水線。路徑長度縮短為原始路徑長度的 1/N。此時如果時鐘頻率不變,則在一個周期內(nèi),只需要對電容 C/N 進行充放電,而不是對原來的電容 C 進行充放電。因此在相同的頻率要求下,可以采用較低的電源電壓來驅(qū)動系統(tǒng),使功耗降低。

假設(shè)在一個設(shè)計中,關(guān)鍵路徑是一個 32bit X 32bit 的乘法器。該乘法器的整體電容為 C,工作電壓為 V。

不加流水線時,要達到此工作頻率,工作電壓應(yīng)該為 V。

采用兩級流水線方式時,該路徑被分成兩部分。對于每一部分,整體電容變?yōu)?nbsp;C/2。如果要達到原來的工作頻率,工作電壓可以降為 βV(β<1)。整個系統(tǒng)功耗降低為原來的 β^2。

流水線具體設(shè)計方法,可參考 《Verilog 教程》章節(jié)中 《Verilog 流水線》一節(jié)。

資源共享與狀態(tài)編碼

資源共享

當(dāng)設(shè)計中一些相同的運算邏輯在多處使用時,就可以使用資源共享的方法避免多個運算邏輯的重復(fù)出現(xiàn),減少資源的消耗。

例如一個比較邏輯,沒有使用資源共享的代碼描述如下:

    always @(*) begin
        case (mode) :
            3'b000:         result  = 1'b1 ;
            3'b001:         result  = 1'b0 ;
            3'b010:         result  = value1 == value2 ;
            3'b011:         result  = value1 != value2 ;
            3'b100:         result  = value1 > value2 ;
            3'b101:         result  = value1 < value2 ;
            3'b110:         result  = value1 >= value2 ;
            3'b111:         result  = value1 <= value2 ;
        endcase
    end
//對上述代碼進行優(yōu)化,描述如下:
    wire equal_con       = value1 == value2 ;
    wire great_con       = value1 > value2 ;
    always @(*) begin
        case (mode) :
            3'b000:         result  = 1'b1 ;
            3'b001:         result  = 1'b0 ;
            3'b010:         result  = equal_con ;
            3'b011:         result  = equal_con ;
            3'b100:         result  = great_con ;
            3'b101:         result  = !great_con && !equal_con ;
            3'b110:         result  = great_con && equal_con ;
            3'b111:         result  = !great_con ;
        endcase
    end

第一種方法綜合實現(xiàn)時,如果編譯器優(yōu)化做的不好,可能需要 6 個比較器。第二種資源共享的方法只需要 2 個比較器即可完成相同的邏輯功能,因此在一定程度會減少功耗。

狀態(tài)編碼

對于一些變化頻繁的信號,翻轉(zhuǎn)率相對較高,功耗相對較大??梢岳脿顟B(tài)編碼的方式來降低開關(guān)活動,減少功耗。

例如高速計數(shù)器工作時,使用格雷碼代替二進制編碼時,每一時刻只有 1bit 的數(shù)據(jù)翻轉(zhuǎn),翻轉(zhuǎn)率降低,功耗隨之降低。

例如進行狀態(tài)機設(shè)計時,狀態(tài)機切換前后的狀態(tài)編碼如果只有 1bit 的差異,也會減少翻轉(zhuǎn)率。

操作數(shù)隔離

操作數(shù)隔離原理:如果在某一段時間內(nèi),數(shù)據(jù)通路的輸出是無用的,將輸入置成固定值,數(shù)據(jù)通路部分沒有翻轉(zhuǎn),功耗就會降低?!?

一個乘法器電路圖如下所示。


當(dāng) sel0 = 0 或 sel1 = 1 時,乘法器 Multiplier 的輸出結(jié)果并不能通過兩個 Mux 到達寄存器的輸入端。即寄存器并不能保存當(dāng)前乘法器的結(jié)果,此次乘法運算是沒有必要的。在此種條件下,采用操作數(shù)隔離,使乘法器不工作保持靜態(tài),也可以節(jié)省功耗。

對上述電路進行一個優(yōu)化,如下圖所示。


操作數(shù)隔離之后,當(dāng) sel0 = 0 或 sel1 = 1 時,乘法器輸入端始終為 0,沒有信號翻轉(zhuǎn),乘法器沒有進行額外的無效工作,所以功耗會降低。

一般來說,操作數(shù)隔離的操作發(fā)生在代碼綜合的時候。這個過程往往是人為可設(shè)置、編譯器可自動識別的。當(dāng)然,良好的代碼風(fēng)格,在編寫 RTL 電路時就考慮周全,更加有助于實現(xiàn)操作數(shù)隔離,從而降低功耗。

乘法器沒有使用操作數(shù)隔離時,Verilog 代碼描述如下:

//no isolation
module  oper_isolation1
    (
     input                clk ,           //100MHz
     input [1:0]          sel ,
     input [3:0]          din1 ,          //data in
     input [3:0]          din2 ,          //data in
     output reg [7:0]     dout
     );

    reg [7:0]       res ;
    always @(*) begin
        res       = din1 * din2 ;
    end

    always @(posedge clk) begin
        if (sel == 2'b01) begin
            dout   <= res ;
        end
    end
endmodule

乘法器使用操作數(shù)隔離時,Verilog 代碼描述如下:

//using isolation
module  oper_isolation2
    (
    input                clk ,           //100MHz
    input [1:0]          sel ,
    input [3:0]          din1 ,          //data in
    input [3:0]          din2 ,          //data in
    output reg [7:0]     dout
    );

    wire [3:0]           mul1 = sel == 2'b01 ? din1 : 0 ;
    wire [3:0]           mul2 = sel == 2'b01 ? din2 : 0 ;
    reg [7:0]            res ;
    always @(*) begin
        res       = mul1 * mul2 ;
    end

    always @(posedge clk) begin
        if (sel == 2'b01) begin
            dout   <= res ;
        end
    end
endmodule

點擊這里下載源碼


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號