大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是已经还准备入行,看过之后都会有有一些收获,如果看完后喜欢的话就请关注我吧~谢谢~
本篇文章主要来聊聊数字前端设计师需要关注的行为级和RTL级低功耗技术。首先来聊聊行为级低功耗技术。
一、行为级低功耗技术
行为级低功耗技术主要讨论的是流水线和并行两种电路结构。
1.1 并行结构
并行结构是将原本一条数据路径的工作拆分至多条数据路径上完成,通过采样并行话结构,使得完成工作的效率变高,数据路径运行的延时变多,因此可以将原本的工作电压和系统时钟频率降低,进而实现了功耗的降低。
下图就是一个并行化实现乘法器的例子,通过使用两个乘法器来取代原本设计中的一个乘法器,使得时钟频率可以降低,进而系统的功耗也会降低:
1.2 流水线结构
流水线结构就是通过将原本的一条路径,通过插入寄存器的方式分割成多份,例如如果是分割成N级流水线的话,这样路径长度就会缩短为原本路径的1/N,这就使得一个周期内的充/放电容可以变为原本的C/N。因此,在相同的速度要求下,可以通过较低的电源电压来驱动系统,进而降低系统的功耗。
流水线的示意图如下图所示:
并行结构和流水结构都可以降低功耗,但是同时也会带来面积上的增加,需要按照实际需求使用。
二、RTL级低功耗技术
接下来是我们在写代码时可以使用的一些低功耗方法~
2.1 门控时钟:
在前面的系统级低功耗技术中,我们提到了高级门控时钟,这种方法是通过直接在时钟树的源头加入时钟门控单元,因为在时钟源后面分布着大量的缓冲器buffer和触发器,直接在源头关断时钟,可以停止后续器件的活动,以达到大大节省动态功耗的目的。
在这里,我们接着从RTL的角度来讨论门控时钟,我们可以通过满足某种RTL编码风格的方式,使EDA工具在不改变逻辑功能的情况下自动插入时钟门控,例如下面的verilog代码:
always @(posedge clock or negedge reset_n) begin
if (!reset_n) begin
test_ff <= 32'b0;
end else if (load_cond) begin //生成门控时钟
test_ff <= test_data;
end
end
通过以上的verilog代码确保只有在load_cond有效时,test_ff才会翻转,EDA工具可以根据此自动推出门控时钟。如下面的电路所示:
在我们使用这种方式生成门控时钟时,最好做到:
- 寻找现有的信号做为en,没必要生成新的逻辑信号,这反而有可能造成更多的损失。
-减小en信号的逻辑规模,例如设计输入端数据位用于指示数据有变化。 - 需要门控的数据位数越多越划算,如果位数太少,反而会浪费面积。例如我看的一篇文章里建议至少在位宽大于3bit以上寄存器才有必要使用门控时钟。
2.2 操作数隔离:
如果在某一段时间内,数据通路的输出未被真正采样,那么它就是无用的,因此我们可以将它的输入置成个固定值,这样,数据通路部分没有翻转,功耗就会降低。我们将这种低功耗方法称为操作数隔离。
对于一些算术逻辑单元(ALU),如加法器、乘法器等,只有当这些算术逻辑单元的输入是有效数据时,它的输出才是有意义的。而算术逻辑单元中有许多的组合逻辑,组合电路的内部工作状态完全是由其输入信号决定,每当输入信号翻转时,都会引起组合电路输出信号的改变,这期间组合电路内部会有很多器件工作,从而产生许多的无效功耗。
因此我们可以使用操作数隔离的方法,降低组合电路模块输入信号的无效翻转,以此降低这部分电路的功耗。
举个例子:
上面的电路图中,Add_0是两个多bit的数据进行相加操作的加法器。每当输入数据发生改变时,加法器就会开始计算,并且改变输出值,输出至后续的选择器。但是当SEL_0≠1,或SEL_1≠0时,加法器Add_0的计算结果并不能到达寄存器reg_0的输入端口,也就是说此时对于reg_0来说,Add_0的计算结果是它所不关心的,我们需要的是只是SEL_0=1且SEL_1=0时计算的结果,而非其余情形下的中间值,这些中间值对于我们来说就是造成了多余的功耗。因此,处于节省功耗的考虑,我们可以使用操作数分离的方法,使加法器Add_0不工作,如下图所示:
可知,基于SEL_0和SEL_Q信号来对输入到加法器的数据进行线与操作,当当SEL_0≠1,或SEL_1≠0时,输入到加法器的信号就会是固定的0,不会引起后续加法器和选择器的输入输出翻转,进而降低了功耗。
再举个栗子~上面是两个32bit的数据进行相加操作的加法器,64bit数据中不同的bit可以通过不同的组合逻辑到达,或者通过不同的路径到达。每当输入数据的某一bit发生改变时,加法器就会开始计算,并且改变输出值。如果A和B两个输入数据不能同时到达加法器的输入端口,就会出现许多中间值,但是我们只需要A和B都有效时计算的结果,因此为了节省功耗,我们可以使用寄存器锁存输入,然后直接通过寄存器输出确保它们同时达到加法器的输入端:
通过上面的例子可以发现,操作数隔离通过避免了后续电路的无效翻转来降低功耗,但是代价就是面积的增加,因此需要在设计时权衡考虑。
2.3 资源共享:
对于设计较多算术运算的逻辑,我们要尽量共用相同的逻辑,避免重复生成相同功能的电路,例如下图:
我们只使用一个比较器“==”,和一个算术比较器“>"就可以实现相同的功能。
通过资源共享我们可以节省大量的面积以及功耗。
2.4 状态机编码:
使用格雷码而非二进制来编码有限状态机,由于二进制编码状态机在状态转换时,很有可能有多个触发器发生翻转,例如,从状态A(3‘b011)到状态B(3’b100),有3bit数据发生了翻转。而对于格雷码来说,状态机按顺序跳变的情况下,只会有一位数据翻转。这样就可以降低这部分的功耗消耗。
而且由于格雷码的特点,也会降低出现毛刺的风险。
如上图所示,使用格雷码的状态机,其状态编码翻转频率明显降低。在我们实际应用时,尽量将跳转最频繁的几个状态设为相邻编码(即汉明距离近的编码),这样状态机每次跳转只会有一位或者几位数据发生变化,就能够降低状态机的寄存器和组合逻辑部分的功耗。
2.5 比较器优化
当有较大位宽数据需要进行比较时,最好避免一次性比较所有位。由于比较器是组合逻辑,如果比较器的输入发生变化时,其最终输出可能保持不变。然而,可能仍然存在许多中间计算过程导致了功耗。这部分的功耗完全是浪费的,因为这些转换对最终功能没有贡献。因此,我们需要避免出现此类的功耗浪费。对于比较器来说,从统计角度来看,仅比较MSB就能够在50%的情况下给出结果。因此只有当MSB比较不出大小时,较低的比特才应该进行比较。下图显示了两个32位数据的比较器:
第一幅图是一个典型的比较器,第二幅图是一个改进的比较器,在这种比较器中,首先进行MSB比较。如果仅比较MSB就能够得到比较结果,则阻止其他位参与比较。只有当MSB无法得到比较结果时,才允许较低的比特进入异或门进行比较。
应该注意的是,只有在MSB已经参与比较之后,总线的较低bit位才应该到达异或门。否则,如果较低的bit位数据先达到相应的XOR,它们无论如何都会造成功耗的浪费,并且预计算MSB对节约功耗上没有任何好处。同样的概念可以进一步递归应用在比较较低的31位数据中。
此外,使用这种方法也会给面积和时序带来一定的影响,例如:
•实施此类优先级比较器需要更多的资源,例如上图中的或门。
•为路径的时序带来影响:
-对于低位,现在引入了一个额外的或门。如果这些数据已经位于关键路径上,它们将有大的时序违例风险。
-对于MSB,异或门上有一个额外的负载,这将使该路径上的时序恶化。如果这个比特在关键路径上,它将进一步恶化时序。
2.6 行波计数器:
行波计数器的特点是:使用级联寄存器,使用上一级触发器的输出驱动下一级触发器的时钟输入端。
行波计数器的优点在于其使用的逻辑资源相比普通的同步计数器更少,并且能够大量减少功耗。
不过由于行波计数器在各个阶段都会创造一个新的时钟,所以会给静态时序分析STA和综合工具带来麻烦,增加后端的工作量。并且行波计数器的使用也会为扫描链的插入增加难度。因此,在使用行波计数器时,需要结合设计需求多加考虑。
2.7 独热码多路选择器:
在verilog中,有很多方式实现多路选择器,一般来说,我们使用的选择信号通常是多比特的二进制编码,多位数据就设计到一个多位不能同时变化的问题,进行出现伪转换,导致选择器输入输出错误翻转进而造成功耗。
因此,如果我们使用独热码编码的选择信号,输出就会更快更加稳定,且会在初期就将没有选择到的总线掩藏掉,进而节约了功耗。
2.8 算数逻辑单元优化
芯片中的算数逻辑单元,比如说乘法器或除法器,都有着不小的面积,而更多的逻辑单元也会导致更多的功耗,因此如果能使用其他的逻辑代替乘法器和除法器,可以大大节约面积与功耗。比如:
对于某些特殊的乘法,比如说某一个乘数是2的n次方时,我们就可以使用右移代替乘法,
对于某些特殊的除法,比如说除数是2的n次方时,我们就可以使用截位来代替除法。
在进行这类型的优化时,需要和算法人员进行讨论,是否能在不影响芯片性能的基础上进行优化。
3. 总结
以上就是我们设计人员可以在平时的代码设计之中可以考虑的一些低功耗方法了,对于其中的许多低功耗方法,会带来更多的资源和时序压力,因此,芯片设计实际上是在面积功耗和性能上寻求平衡,在满足需求上的基础上寻求最优解。我们需要根据实际情况采用合适的设计方法~
如果你喜欢这篇文章的话,请关注我的公众号-熊熊的ic车间,里面还有ic设计和ic验证的学习资料和书籍等着你呢~欢迎您的关注!