書接上文,數字芯片中典型的低功耗設計是添加clk gate。另外還有一種方法是并行與流水技術。
并行與流水
硬件描述語言的一個突出優(yōu)點就是指令執(zhí)行的并行性。多條語句能夠在相同時鐘周期內并行處理多個信號數據。但是當數據串行輸入時,指令執(zhí)行的并行性并不能體現出其優(yōu)勢。而且很多時候有些計算并不能在一個或兩個時鐘周期內執(zhí)行完畢,如果每次輸入的串行數據都需要等待上一次計算執(zhí)行完畢后才能開啟下一次的計算,那效率是相當低的。流水線就是解決多周期下串行數據計算效率低的問題。一般來講,對于一個功能模塊,可以通過并行的方式實現,也可以通過流水線的方式實現,這兩種方法都是用資源換速度。
對于并行處理,可以同時處理多條執(zhí)行語句,使執(zhí)行效率變高。在滿足設計SPEC的前提下,通過并行處理,可減小時鐘頻率,從而達到降低功耗的目的。舉例,若實現4 個數據乘加運算,可以采用 1 個乘法器來實現,也可以通過 2 個乘法器(并行)來實現 ,verilog代碼描述分別如下:
//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
tb測試平臺描述如下:
`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
下圖為仿真結果,兩種實現方法的結果是相同的,但是對于并行處理方法,由于其時鐘頻率降低了1/2,功耗也會隨之降低,但是電路面積會隨之增加。
對于一個處于連續(xù)工作模式的 N 級流水線,效率提升N倍。與并行設計類似,流水線設計也可以通過降低工作頻率,達到降低功耗的目的。除此之外,我們可以將流水線設計分成 N 級流水線,假設新的路徑長度為Lnew,原始路徑長度為Lold,則Lnew=1/N*Lold。如果時鐘頻率保持固定,則在一個周期內,不需要對原來的電容 C 進行充放電,只需要對電容 C/N 進行充放電。因此可以采用較低的電壓來驅動芯片電路,從而達到降低功耗的目的。
資源共享與狀態(tài)編碼
對于某設計,一個相同的邏輯在多處被調用時,可以對該邏輯進行資源共享,以降低功耗。例如,對于一個比較邏輯,沒有使用資源共享的代碼描述如下:
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
改進后,使用資源共享的代碼描述如下:
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
第一種方法綜合實現時,如果編譯器優(yōu)化做的不好,可能需要 6 個比較器。第二種資源共享的方法只需要 2 個比較器即可完成相同的邏輯功能,因此在一定程度會減少功耗。
對于一些變化頻繁的信號,翻轉率相對較高,功耗相對較大??梢岳脿顟B(tài)編碼的方式來降低開關活動,減少功耗。例如高速計數器工作時,使用格雷碼代替二進制編碼時,每一時刻只有 1bit 的數據翻轉,翻轉率降低,功耗隨之降低。例如進行狀態(tài)機設計時,狀態(tài)機切換前后的狀態(tài)編碼如果只有 1bit 的差異,也會減少翻轉率。
操作數隔離
操作數隔離就是在進行一些操作比如選擇器的時候,我們選擇的那個選項有A和B,但是如果我們直到選擇的是A,那么B之前一大堆計算就顯得沒有必要了。所以操作數隔離也就是增加一些選擇器件,如果這個操作數不需要的話就不選擇它以及不進行之前計算這個操作數所需要的操作。 沒有使用操作數隔離時,Verilog 代碼描述如下:
module isolated(A,B,C,D,clk,clr,choose,result);
input wire clk;
input wire clr;
input wire [1:0]choose;
input wire [31:0]A;
input wire [31:0]B;
input wire [31:0]C;
input wire [31:0]D;
output reg [31:0]result;
wire [31:0]choose_a;
wire [31:0]choose_b;
wire [31:0]choose_c;
wire [31:0]choose_d;
//這是一個簡單的mux,先計算出A,B,C,D的值再選擇
assign choose_a = A*B;
assign choose_b = A+B+C+D;
assign choose_c = B*C;
assign choose_d = C*D;
always@(posedge clk,posedge clr)
begin
if(clr)
result <= 0;
else
begin
if(choose == 2'b00)
result <= choose_a;
else if(choose == 2'b01)
result <= choose_b;
else if(choose == 2'b10)
result <= choose_c;
else
result <= choose_d;
end
end
endmodule
使用操作數隔離時,Verilog 代碼描述如下:
module isolated2(A,B,C,D,clk,clr,choose,result);
input wire clk;
input wire clr;
input wire [1:0]choose;
input wire [31:0]A;
input wire [31:0]B;
input wire [31:0]C;
input wire [31:0]D;
output reg [31:0]result;
reg [31:0]choose_A;
reg [31:0]choose_B;
reg [31:0]choose_C;
reg [31:0]choose_D;
reg [1:0]cho;
//這是一個使用了isolated的mux,先根據信號然后相應計算所需的A,B,C或者D的值
always@(posedge clk,posedge clr)
begin
if(clr)
begin
choose_A <= 0;
choose_B <= 0;
choose_C <= 0;
choose_D <= 0;
cho <= 0;
end
else
if(choose == 2'b00)
begin
choose_A <= A;
choose_B <= B;
choose_C <= choose_C;
choose_D <= choose_D;
cho <= 0;
end
else if(choose == 2'b01)
begin
choose_A <= A;
choose_B <= B;
choose_C <= C;
choose_D <= D;
cho <= 1;
end
else if(choose == 2'b10)
begin
choose_A <= choose_A;
choose_B <= B;
choose_C <= C;
choose_D <= choose_D;
cho <= 2;
end
else
begin
choose_A <= choose_A;
choose_B <= choose_B;
choose_C <= C;
choose_D <= D;
cho <= 3;
end
end
always@(posedge clk,posedge clr)
begin
if(clr)
result <= 0;
else
begin
if(cho == 2'b00)
result <= choose_A*choose_B;
else if(cho == 2'b01)
result <= choose_A+choose_B+choose_C+choose_D;
else if(cho == 2'b10)
result <= choose_B*choose_C;
else
result <= choose_C*choose_D;
end
end
endmodule
參考文獻:https://www.runoob.com/w3cnote/verilog2-rtl-low-power-design-1.html