书接上文,数字芯片中典型的低功耗设计是添加clk gate。另外还有一种方法是并行与流水技术。
并行与流水
硬件描述语言的一个突出优点就是指令执行的并行性。多条语句能够在相同时钟周期内并行处理多个信号数据。但是当数据串行输入时,指令执行的并行性并不能体现出其优势。而且很多时候有些计算并不能在一个或两个时钟周期内执行完毕,如果每次输入的串行数据都需要等待上一次计算执行完毕后才能开启下一次的计算,那效率是相当低的。流水线就是解决多周期下串行数据计算效率低的问题。一般来讲,对于一个功能模块,可以通过并行的方式实现,也可以通过流水线的方式实现,这两种方法都是用资源换速度。
对于并行处理,可以同时处理多条执行语句,使执行效率变高。在满足设计SPEC的前提下,通过并行处理,可减小时钟频率,从而达到降低功耗的目的。举例,若实现4 个数据乘加运算,可以采用 1 个乘法器来实现,也可以通过 2 个乘法器(并行)来实现 ,verilog代码描述分别如下:
//1 multiplier, high speedmodule mul1_hs(input clk , //200MHzinput rstn ,input en ,input [3:0] mul1 , //data ininput [3:0] mul2 , //data inoutput dout_en ,output [8:0] dout);reg flag ;reg en_r ;always @(posedge clk or negedge rstn) beginif (!rstn) beginflag <= 1'b0 ;en_r <= 1'b0 ;endelse if (en) beginflag <= ~flag ;en_r <= 1'b1 ;endelse beginflag <= 1'b0 ;en_r <= 1'b0 ;endendwire [7:0] result = mul1 * mul2 ;// data output enreg [7:0] res1_r, res2_r ;always @(posedge clk or negedge rstn) beginif (!rstn) beginres1_r <= 'b0 ;res2_r <= 'b0 ;endelse if (en & !flag) beginres1_r <= result ;endelse if (en & flag) beginres2_r <= result ;endendassign dout_en = en_r & !flag ;assign dout = res1_r + res2_r ;endmodule//===========================================// 2 multiplier2, low speedmodule mul2_ls(input clk , //100MHzinput rstn ,input en ,input [3:0] mul1 , //data ininput [3:0] mul2 , //data ininput [3:0] mul3 , //data ininput [3:0] mul4 , //data inoutput dout_en,output [8:0] dout);wire [7:0] result1 = mul1 * mul2 ;wire [7:0] result2 = mul3 * mul4 ;//en delayreg en_r ;always @(posedge clk or negedge rstn) beginif (!rstn) beginen_r <= 1'b0 ;endelse beginen_r <= en ;endend// data output enreg [7:0] res1_r, res2_r ;always @(posedge clk or negedge rstn) beginif (!rstn) beginres1_r <= 'b0 ;res2_r <= 'b0 ;endelse if (en) beginres1_r <= result1 ;res2_r <= result2 ;endendassign dout = res1_r + res2_r ;assign dout_en = en_r ;endmodule
tb测试平台描述如下:
`timescale 1ns/1psmodule test ;reg rstn ;//mul1_hsreg 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_lsreg 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 generatingreal CYCLE_200MHz = 5 ; //always beginhs_clk = 0 ; #(CYCLE_200MHz/2) ;hs_clk = 1 ; #(CYCLE_200MHz/2) ;endalways begin@(posedge hs_clk) ls_clk = ~ls_clk ;end//reset generatinginitial beginrstn = 1'b0 ;#8 rstn = 1'b1 ;end//motivationinitial beginhs_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;endhs_en = 0 ;endinitial beginls_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;endls_en = 0 ;end//module instantiationmul1_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 finishalways begin#100;if ($time >= 1000) begin#1 ;$finish ;endendendmodule
下图为仿真结果,两种实现方法的结果是相同的,但是对于并行处理方法,由于其时钟频率降低了1/2,功耗也会随之降低,但是电路面积会随之增加。
对于一个处于连续工作模式的 N 级流水线,效率提升N倍。与并行设计类似,流水线设计也可以通过降低工作频率,达到降低功耗的目的。除此之外,我们可以将流水线设计分成 N 级流水线,假设新的路径长度为Lnew,原始路径长度为Lold,则Lnew=1/N*Lold。如果时钟频率保持固定,则在一个周期内,不需要对原来的电容 C 进行充放电,只需要对电容 C/N 进行充放电。因此可以采用较低的电压来驱动芯片电路,从而达到降低功耗的目的。
资源共享与状态编码
对于某设计,一个相同的逻辑在多处被调用时,可以对该逻辑进行资源共享,以降低功耗。例如,对于一个比较逻辑,没有使用资源共享的代码描述如下:
always @(*) begincase (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 ;endcaseend
改进后,使用资源共享的代码描述如下:
wire equal_con = value1 == value2 ;wire great_con = value1 > value2 ;always @(*) begincase (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 ;endcaseend
第一种方法综合实现时,如果编译器优化做的不好,可能需要 6 个比较器。第二种资源共享的方法只需要 2 个比较器即可完成相同的逻辑功能,因此在一定程度会减少功耗。
对于一些变化频繁的信号,翻转率相对较高,功耗相对较大。可以利用状态编码的方式来降低开关活动,减少功耗。例如高速计数器工作时,使用格雷码代替二进制编码时,每一时刻只有 1bit 的数据翻转,翻转率降低,功耗随之降低。例如进行状态机设计时,状态机切换前后的状态编码如果只有 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)beginif(clr)result <= 0;elsebeginif(choose == 2'b00)result <= choose_a;else if(choose == 2'b01)result <= choose_b;else if(choose == 2'b10)result <= choose_c;elseresult <= choose_d;endendendmodule
使用操作数隔离时,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)beginif(clr)beginchoose_A <= 0;choose_B <= 0;choose_C <= 0;choose_D <= 0;cho <= 0;endelseif(choose == 2'b00)beginchoose_A <= A;choose_B <= B;choose_C <= choose_C;choose_D <= choose_D;cho <= 0;endelse if(choose == 2'b01)beginchoose_A <= A;choose_B <= B;choose_C <= C;choose_D <= D;cho <= 1;endelse if(choose == 2'b10)beginchoose_A <= choose_A;choose_B <= B;choose_C <= C;choose_D <= choose_D;cho <= 2;endelsebeginchoose_A <= choose_A;choose_B <= choose_B;choose_C <= C;choose_D <= D;cho <= 3;endendalways@(posedge clk,posedge clr)beginif(clr)result <= 0;elsebeginif(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;elseresult <= choose_C*choose_D;endendendmodule
参考文献:https://www.runoob.com/w3cnote/verilog2-rtl-low-power-design-1.html
1740