前面已经介绍过低功耗相关概念【IC】低功耗设计理论知识,这里主要分享下RTL级的常用低功耗设计,欢迎讨论交流。
一、时钟门控clock gating
毫无疑问,时钟门控是前端设计中最有效的低功耗设计。
时钟门控的基本思想是在寄存器不工作的时候,把时钟clock关掉,从而时钟树clock tree上的buffer和寄存器就不会有时钟工作,也就没有动态功耗。
单元库中会有专门提供用于时钟门控的cell,优秀的代码在工具进行综合时也会用命令自动插入cell。
由上面的图可以看出,插入门控时钟单元后,原有电路中的MUX就不需要了,如果数据位宽是多bit的,插入clock_gating后,电路面积可能反而会减少;但如果数据是单bit信号,那么节省的功耗就比较少。
因此,工具可以根据位宽来决定是否增加门控时钟。如果位宽很小,那么插入的clock_gating面积比原来的MUX有可能大很多,但节省的功耗又很少,性价比不高;只有当位宽超过了一定的bit数(>4bit)后,才有必要插入clock_gating。
1.1 使用DC添加门控时钟
在dc_shell中输入man set_clock_gating_style可以查看具体的配置说明,这里列出其中几个主要的配置:
-global:可以使门控时钟穿越层次结构。如果不用选项“-global",在每个模块里都会产生一个门控时钟单元;
-minimum_bitwidth:用于设置进行时钟门控的寄存器阵列的最小宽度。对于宽度小于该设置的寄存器阵列,不进行时钟门控;然而当电路由有公共使能时,会对电路进行分解进行集体门控;
-max_fanout:设置一个门控单元所驱动的最大负载数目,定义CG单元最大扇出的一个目的是减少CG后面的时钟延迟,门控时钟单元的扇出越大,它到达寄存器的延迟越长;此外,还有用来约束重新平衡;
-num_stages:用于设置一个多级门控的级数。在有些设计中,顶层的门控信号会分解成不同的子门控信号。在缺省情况下,仅对跟寄存器阵列相连的门控制信号生成门控逻辑;
-sequential_cell,该选项设置采用何种门控时钟风格:
-sequential_cell latch:基于锁存器的离散门控单元,该配置为默认值;
-sequential_cell none:采用不适用锁存器的门控单元;
-positive_edg_logic:简写为-positive或-pos,设置在RTL代码中用上升沿锁存的寄存器(也就是上升沿沿触发的寄存器)采用何种门控逻辑,如-pos “or”表示对于上升沿触发的寄存器,采用或门逻辑进行门控,-pos "integrated"表示对于上升沿触发的寄存器采用集成门控单元进行门控;
-negative_edg_logic:简写为-negative或-neg,设置在RTL代码中用下降沿锁存的寄存器(也就是下降沿触发的寄存器)采用何种门控逻辑;
-control_point和-control_signal:这两个开关和DFT有关,用于设置该门控单元在DFT时是否可控,DFT控制信号是scan-enable还是test-mode,以及DFT控制信号与EN信号的组合逻辑是放在门逻辑中的锁存器之前还是之后。通常,将DFT控制信号与EN信号进行或操作,这样在DFT时,可以控制该门控逻辑;
-observation_point:该开关和DFT有关,用于设置是否要插入观测逻辑,以便在DFT时能看到门控逻辑内部的信号;
-setup:设置建立时间约束;
-hold:设置保持时间约束;
-observation_logic_depth:设置观察电路中异或门的数目。
通常的设置参数如下:
set_clock_gating_style "integrated" -max_fanout 30 -minimum_bitwidth 4
insert_clock_gating -global
propagate_constraints -gate_clock
1.2 RTL推荐代码风格
为了便于工具自动识别enable并插入门控cell,良好的代码风格是必须的,下面举例说明:
理想的代码风格:
always(posedge clk or negedge rst_n)beginif(rst_n == 1’b0)beginq <= 32’b0;endelse if(en)beginq <= d;end
end
错误的代码风格:
always(posedge clk or negedge rst_n)beginif(rst_n == 1’b0)beginq <= 32’b0;endelse if(en)beginq <= d;endelse beginq <= 32’b0;end
end
当然这里只是一个最简单的示例,核心思想就是理想的代码风格就是寄存器的赋值一定需要有相应的条件,如果没有条件就赋值,就会造成无法自动插入时钟门控了。
对这种编码模式,可以通过脚本进行检查,如果代码风格不符合ICG时钟门控的插入,则报错并提醒设计者进行手动代码优化。
二、减少数据翻转率
2.1减少不必要的复位
对于控制寄存器,清理掉残余状态是很有必要的,但对于data数据寄存器,数据残余可以不要复位。如果只在wr_en的条件下把有效的数据写入data_reg,则没有必要在wr_en=0的时候复位寄存器,这么做只会白白增加翻转率toggle rate。总的来说,寄存器使能条件越精确(打开ICG几率越少),越有利于power优化。
2.2如何进行数据传输
当数据需要穿过N拍delay后再使用时,我们通常有两种方法来实现,一是将数据打N拍,一级一级往后传。二是将数据存入FIFO,到后级需要使用时直接读出来。两种方式valid信号都需要单独传递。如果数据特别大,也可以考虑第三种使用RAM的形式搭建资源池进行读写,而RAM也可以考虑加上时钟门控进一步优化功耗。
对比PIPE打拍的方式,FIFO的方式有额外地址计算比较的开销,但每个数据只需要读写各一次,很明显FIFO方式每个寄存器的toggle rate将大幅下降。
因为FIFO额外地址计算的开销,所以这里要根据FIFO的深度和位宽进行选择,数据宽度越宽,FIFO深度越深,功耗优化效果越明显。
三、操作数隔离
操作数隔离的原理是: 如果在某一段时间内,数据通路的输出是无用的,则将它的输入置成个固定值,这样,数据通路部分没有翻转,功耗就会降低。
四、总线数据分割
总线数据分割的思想是将多位总线数据分割成高位和低位两部分,并且优先判断高位。
比如,32bit总线数据分成高1bit和低31bit,只有当高位bit符合要求时再进行低位的比较。
五、其他
状态机编码:传统是用二进制编码,改成格雷码,相邻状态可以减少瞬变的次数;同理,可用独热码。
并行结构:增加资源做处理,可以降低时钟频率。这种设计需要在增加的面积和节约的功耗之间做平衡。