下表顯示了在數(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é)。
當(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 個比較器即可完成相同的邏輯功能,因此在一定程度會減少功耗。
對于一些變化頻繁的信號,翻轉(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ù)隔離原理:如果在某一段時間內(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
點擊這里下載源碼
更多建議: