目录
《SoC设计方法与实现》
《硬件架构的艺术》
菜鸟教程 - 6.1 Verilog 低功耗简介
《ug907 Vivado Design Suite User Guide Power Analysis and Optimization》
1. 低功耗简介
功耗和性能是IC设计中两个必须权衡的量,这里讲讲低功耗的设计。
1.1. 功耗类型
CMOS电路的功耗一般分为以下三类
● 翻转功耗:也叫开关功耗,就是对负载电容充放电时的功耗。
如上图CMOS非门:
Vin为低电平时,PMOS导通、NMOS截止。VDD 对负载电容 Cload 充电,充电完成后,Vout 的电平为高。
Vin为高电平时,PMOS截止、NMOS导通。负载电容通过 NMOS 进行放电。放电完成后,Vout 的电平为低。
这样电源的开闭就构成了翻转功耗,IC中的时钟、触发器、逻辑门中信号的翻转都会产生翻转功耗,整个电路的翻转功耗的计算式如下
P = 1 2 ⋅ V D D 2 ⋅ C ⋅ f ⋅ N s w (a) P=\frac{1}{2}·V_{DD}^2·C·f·N_{sw} \tag{a} P=21⋅VDD2⋅C⋅f⋅Nsw(a)
其中 P P P为功率, V D D 2 V_{DD}^2 VDD2为电源电压, C C C为后级电路等效的负载电容, f f f为时钟频率, N s w N_{sw} Nsw为单时钟周期内翻转的晶体管数目
● 短路功耗:在信号翻转过程中,有一段时间PMOS和NMOS是同时导通的,所以会形成电源到地的短路电流,造成短路功耗
● 漏电流功耗:也叫静态功耗,主要包括PN结反向电流、亚阈值漏电流、栅极漏电流和隧道漏电流
1.2. SoC的主要功耗
基于微处理器的SoC,不同芯片的组成可能很大,复杂性也各不相同,功耗组成也各不相同。
不过一般而言,SoC的功耗主要聚焦在时钟树、处理器和存储器三部分
1.3. 低功耗设计
基于芯片的设计流程,不同层级的功耗优化效果不一,如下图
可见IC前端设计人员在低功耗方面还是有点贡献的。
优化最明显的就是系统级。
系统级低功耗设计
这一级的低功耗设计主要由系统和架构工程师完成,他们有着丰富的经验。
● 多电压:不同模块根据性能不同要求分配不同的电压域。可以固定电压,也可软件自适应控制。
● 时钟分配:与多电压类似,不同模块使用不同频率的时钟。
● 软硬件分配:不是所有功能都要由硬件实现,所以哪些功能由软件实现,哪些由硬件实现对功耗也有影响。
2. RTL级低功耗设计
OK,现在咱们谈谈跟前端设计有关的RTL级怎么进行低功耗设计
2.1. 资源共享
其实就是复用。如果有同一个逻辑运算在多处使用时,就直接复用就行了,不要每一处都重新计算一次。
例如
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
如果是上面的代码,则可能需要6个比较器,但如果是下面的代码只需要2个,外加四个非门、两个与门完事。
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
Vivado综合时,对同一个module内多个相同的组合逻辑/时序逻辑表达式,会自动识别并复用
2.2. 状态编码
多bit信号频繁变化时,使用一般的二进制计数法可能会产生很多翻转功耗,可以在状态机、高速变换的数据使用其他编码方式。
Grey码
格雷码相邻之间只有1bit变化,相比二进制计数器翻转率得到了有效降低,状态机尽量还是用格雷码。
除此之外,格雷码还消除了二进制计数的毛刺问题,并且是多bit信号跨时钟域的最优解决方案。
关于格雷码的原理和使用策略可见异步时钟亚稳态 的解决方案——多bit信号
独热码
onehot码,指只有一位是1、其余位全是0的编码方式,常用于多路选择器MUX,同时在机器学习的数据特征处理中得到应用。
独热码的优势在于省去了译码电路,下面的例子就很能说明问题
always@(*) begin
case(sel)
2'b00: out = a;
2'b01: out = a;
2'b11: out = a;
2'b10: out = a;
endcase
end
综合出来是这样的:
always@(*) begin
case(sel)
4'b0001: out = a;
4'b0010: out = a;
4'b0100: out = a;
4'b1000: out = a;
endcase
end
综合出来则是这样的
2.3. 输入隔离、输出保持
意思就是如果逻辑计算是无效的、未被使用的,就别让它计算了,使用使能信号隔离掉输入。最终的输出信号呢也尽量保持,而不是非得置个0啊啥的,减少翻转,这样的时序逻辑还能够被综合出门控时钟进一步减少功耗。
例如下面的代码,若sel == 2’b00、2’b10、2’b11时,dout <= dout
,那么a * b
的结果就不会被使用,就可以禁用乘法计算,毕竟乘法的功耗和面积还是蛮大的。
assign res = a * b;
always@(posedge clk) begin
if(sel == 2'b01) //sel != 2'b01;时就不需要乘法的结果了
dout <= res;
end
那么怎么禁用乘法呢?assign res = (sel == 2'b01)? (a*b):0;
可不可以呢?
这是大错特错,虽然在sel不是2’b01时,res的值为0,但是实际综合出的电路其实是在乘法器结果的一端连在一个MUX上!所以实际综合出的电路还是会计算乘法的!!!
如果犯了上述错误,其实还是软件思维存在。软件中的
?:
运算符才是如果满足哪个条件计算哪个,而硬件不是!
那怎么将乘法隔离呢?思路是隔离输入,即让所有输入为0,即使a或b进行了翻转,乘法器内的信号也不会翻转,降低了功耗。
wire a_en, b_en;
assign a_en = (sel == 2'b01)? a:0;
assign b_en = (sel == 2'b01)? b:0;
assign res = a_en * b_en;
always@(posedge clk) begin
if(sel == 2'b01)
dout <= res;
end
其本质就是为输入信号 增加使能 en,对于较复杂的逻辑云可以进行输入隔离以降低功耗
门控时钟 clock gating
时钟的不断翻转会产生很大的翻转功耗,因此在某个模块不工作时及时地停下时钟常见的一种低功耗手段。
顾名思义就是为时钟输入增加一个使能en,详情如下