二、RTL级低功耗设计(续)
前面一篇博文我记录了操作数隔离等低功耗设计,这里就主要介绍一下使用门控时钟进行低功耗设计。
(4)门控时钟
门控时钟在我的第一篇博客中有简单的描述,这里就进行比较详细的描述吧。我们主要学习门控时钟电路是什么、什么使用门控时钟、综合库里的门控时钟、如何使用门控时钟、对门控时钟的一些处理、手动插入门控时钟。我们重点介绍如何使用门控时钟和门控时钟的处理。
①门控时钟概述
门控时钟有两种方案:一种直接针对寄存器的时钟进行门控,一种对模块级别的时钟进行门控。相比之下,直接对寄存器的时钟进行门控更为灵活。因为在很多时候,我们不能保证刚好将不需要门控的寄存器与需要门控的寄存器分配在不同的模块。因此我们主要介绍寄存器级的门控时钟。
=============================================================================
下图是门控时钟的一个简单电路图:
上述电路图中,将控制信号(EN)直接与时钟信号(CLK)进行与操作,以完成门控。门控后的时钟信号GCLK送到寄存器阵列中。这样,当EN为0时,该时钟被关掉。相应的波形如下所示:
可以看出,如果EN信号不加控制,会导致门控时钟信号出现毛刺。时钟上的信号出现毛刺是非常危险的。所以在进行门控时,为了使门控时钟不产生毛刺,使能信号必须满足条件:它是寄存器的输出,该寄存器的时钟信号与要门控的时钟信号是相同的。由于上述原因,虽然采用这种门控方式最直接,但在实际中很少采用。
==============================================================================
为了解决这种问题,引入基于锁存器的门控时钟方案,如下图所示:
对应的时序图如下所示:
可以看到,这种方式消除了EN与CLK组合产生的毛刺对门控时钟的影响。该方法的原理在于:锁存器在CLK为低时透明。这样,EN 信号上的毛刺仅出现在CLK的低电平处,EN1与CLK进行与操作,可以将这部分毛刺消除掉。这样,GCLK上就没有毛刺了。
不过需要注意的是,如果在电路中,锁存器与与门相隔很远,到达锁存器的时钟与到达与门的时钟有较大的延迟差别,则仍会出现毛刺,下面就来分析一下:
上述的右上图中,B点的时钟比A时钟迟到,并且Skew > delay,这种情况下,产生了毛刺。为了消除毛刺,要控制Clock Skew,使它满足Skew >Latch delay(也就是锁存器的clk-q的延时)。上述的右下图中,B点的时钟比A时钟早到,并且|Skew| > ENsetup 一 (D->Q),这种情况下,也产生了毛刺。为了消除毛刺,要控制Clock Skew,使它满足|Skew|< ENsetup一(D->Q)。
常见的是第一种毛刺,不过我们可以将这个逻辑做成一个单元,这样就基本上能消除上面的那两种毛刺了,即:
============================================================================
通常情况下,时钟树由大量的缓冲器和反相器组成,时钟信号为设计中翻转率最高的信号,时钟树的功耗可能高达整个设计功耗30%。加入门控时钟电路后,由于减少了时钟树的开关行为,节省了开关功耗。同时,由于减少了时钟引脚的开关行为,寄存器的内部功耗也减少了。采用门控时钟,可以非常有效地降低设计的功耗,一般情况下能够节省20%~60%的功耗。
此外,由于门控时钟不需要用到MUX单元,加入门控时钟电路后,设计的面积也减少了。门控时钟电路的扇出越大,减低功耗和面积的效能越好。当然,扇出太大了,又会产生时序等的问题。
门控时钟电路非常容易实现,用工具自动插入门控时钟,不需要修改RTL代码,门控时钟与工艺无关。
这些优点本来应该放在总结处说的,这里提前进行叙述是为了能够给大家一个印象。其中低功耗的优点是通篇进行讲解的,然后降低面积和实现的问题,我们会在后面的具体实现进行讲解。
②综合库中的门控时钟模型
前面我们说了,门控时钟可以以三种方式实现:一个与门(即不带锁存的门控时钟)、分散的锁存器+与门、集成的锁存器+与门。在综合库中,与门、锁存器是基本逻辑单元,因此可以构成门控时钟。此外,综合库中还专门提供了集成的门控单元。一般情况下,我们使用的是集成的门控单元,因为这个门控单元是对Skew作了控制,不存在前面描述的毛刺问题。
一个示例的 综合库中的时钟门控单元描述如下所示:
(该综合库模型中,E为门控信号;CK为时钟信号;ENL是锁存器输出;ECK为对输出门控后的时钟信号;statetable描述了该门控单元中内部锁存器的功能。该单元的其他内容描述就不具体描述了,我在Tcl与Design Compiler这个分类的博客里面有对综合库进行具体的介绍。)
③门控时钟实现
我们要实现门控时钟,首先就得从RTL代码中进行设置。在RTL代码中将需要门控的寄存器写成“载入-使能”的形式,如下所示:
always @(posedge CLK)
if (EN)
Q <=D;
上述代码中,如果EN有效,则寄存器在时钟上升沿采样数据,否则保持原值。一般情况下,综合会得到下图右上角的电路,而插入门控时钟的电路为下图右下角的电路:
上图的典型综合结果中(即不使用门控时钟的情况),在每个受EN使能控制的寄存器之前加入了一个MUX,当EN信号有效时,寄存器锁存输入信号D;否则保持原值。这种方法也能减少寄存器上的翻转,因而节省翻转功耗。然而,这种“载入一使能”结构中,每个寄存器都有一个MUX,假设MUX面积为4,则8位寄存器需要增加的面积为32。面积越大,意味着芯片成本越高,而且整体的功耗也会增加。另外,这种方式不能消除时钟树上的功耗。
对于右下角的门控时钟形式的综合电路,假设一个门控逻辑的面积为10,一个门控时钟信号可以驱动8位寄存器,则在门控时钟电路中,对每8个寄存器需增加一个门控逻辑,增加的面积为10。由此可以看到,门控时钟的电路比普通综合结果的面积更小、功耗更低。
=============================================================================
鉴于门控时钟的优点,我们需要把普通的综合结果“转换”为门控时钟的结果,我们主要是通过DC的power compiler来自动实现的。我们主要通过命令来设置门控时钟的风格和通过命令“启动”插入门控时钟。综合工具根据我们所设置的时钟门控的风格,插入相应的门控逻辑。因此,门控时钟的实现主要有两步,一步是设置门控时钟的风格,通过命令set_clock_gating_stale 及其选项来实现;另一步就是在网表中加入门控时钟,通过命令insert_clock_gating来实现。下面我们就来介绍一下这两个设置,由于命令在不同版本的DC中有所不同,命令的具体选项就可能不一样,这里就只介绍一些常用或者说是可能用到的选项。
==============================================================================
在执行insert_clock_gating命令前,我们一般先使用set_clock_gating_style命令来指定要插入门控时钟电路的结构(或者说是插入门控时钟的风格)。下面我们就来介绍一下使用这个set_clock_gating_style命令可以进行插入哪些门控时钟电路结构。
·-sequential_cell 选项设置是否采用基于锁存器的风格。因为我们的门控时钟有三种形式(不适用锁存器的与门,基于锁存器+离散与门,集成的锁存器+与门),因此就要指定使用哪一种形式:
A:基于锁存器的离散门控单元是默认值,可以通过下面的命令来设置:
set_clock_gating_style -sequential_cell latch
B:不使用锁存器的门控单元,可以通过下面的命令来设置:
set_clock_gating_style -sequential_cell none
C:使用集成的门控单元则不需要使用这个-sequential_cell来设置了,因为-sequential_cell 选项设置是否采用基于锁存器的风格。使用集成的门控单元直接设置参数就可以了,例如可以通过下面的命令来设置使用集成的门控单元:
set_clock_gating_style “integrated”
一般推荐使用集成门控这种方式。
·-positive_edg_logic选项(简写为-positive或-pos)设置在RTL代码中用上升沿锁存的寄存器(也就是上升沿沿触发的寄存器)采用何种门控逻辑。
-negative_edg_logic选项(简写为-negative或-neg)设置在RTL代码中用下降沿锁存的寄存器(也就是下降沿触发的寄存器)采用何种门控逻辑。
例如下面的命令:
set_clock_gating_style -sequential_cell none -pos “or”
该命令设置了不适用锁存器的风格,然后对于上升沿触发的寄存器,其门控单元使用或门逻辑构成。
set_clock_gating_style -neg “integrated”
该命令置在RTL代码中用下降沿锁存的寄存器(也就是下降沿触发的寄存器)使用集成门控时钟单元。
set_clock_gating_style -positive “integrated” -negative “integrated”
该命令设置RTL代码中,无论你的寄存器是上升沿触发还是下降沿触发,控制该寄存器的时钟单元都是使用集成门控时钟单元。
·-minimum_bitwidth 选项用于设置进行时钟门控的寄存器阵列的最小宽度。对于宽度小于该设置的寄存器阵列,不进行时钟门控;然而当电路由有公共使能时,会对电路进行分解进行集体门控。例如下面的命令作用与下面的电路:
set_clock_gating_style -minimum_bitwidth 4
上述命令意味着一个门控时钟至少要触发4个寄存器。左图中有3个寄存器组,每组只有3个寄存器,不能满足至少要有4个寄存器的要求。因此,对于每个组的寄存器,不能用门控时钟。然而,所有的3个寄存器组,都有1个公共的使能信号”a”,我们可以把它分解出来作为控制时钟的门控信号。这样一来,信号“a”控制9个寄存器,它满足最少要触发4个寄存器的要求。因此将上面的命令约束上面左边的电路时,综合得到结果就会成为右边有门控时钟的电路。
·-num_stages选项用于设置一个多级门控的级数。在有些设计中,顶层的门控信号会分解成不同的子门控信号。在缺省情况下,仅对跟寄存器阵列相连的门控制信号生成门控逻辑。例如对于下面的电路图:
在这个例子中,全局门控信号EN分别跟a,b,c信号组合,然后驱动不同的寄存器阵列。缺省情形下(set_clock_gating_style命令的默认设置为“num_stages”等于“1",缺省时也为1),跟寄存器阵列相连的门控信号 由门控单元给出。
由于所有的3个寄存器组都有1个公共使能“a",它可以被分解出来产生1个额外(级)的门控时钟单元。在set_clock_gating_style命令加选项“-num_stages 2",就可以产生下图所示的两级门控时钟:
使用多级门控时钟,时钟综合器可以尽量地摆放门控时钟单元,使它靠近时钟源,从而最大限度地降低时钟树的功耗。
·-control_point与-control_signal选项跟DFT有关,用于设置该门控单元在DFT时是否可控,DFT控制信号是scan-enable还是test-mode,以及DFT控制信号与EN信号的组合逻辑是放在门逻辑中的锁存器之前还是之后。通常,将DFT控制信号与EN信号进行或操作,这样在DFT时,可以控制该门控逻辑。例如下面的命令约束:
set_clock_gating_style -control_point before -control_signal test_mode
设置得到下面的电路结构:
上图给出了在门控逻辑中插入控制点的示例。在这个例子中,DFT控制信号为test_mode,控制点位于锁存器之前。
·-observation_point选项跟DFT有关,用于设置是否要插入观测逻辑,以便在DFT时能看到门控逻辑内部的信号。
例如下面的约束命令:
set_clock_gating_style -observation_point true
则设置插入观测,逻辑,如下图所示:
除了上述选项外,该命令还有一些其他的选项设置,比如-setup选项设置建立时间约束。-hold选项设置保持时间约束。-observation_logic_depth选项用于设置观察电路中异或门的数目。-max_fanout选项设置一个门控单元所驱动的最大负载数目,定义CG单元最大扇出的一个目的是减少CG后面的时钟延迟,门控时钟单元的扇出越大,它到达寄存器的延迟越长;此外,还有用来约束重新平衡(后面会有对重新平衡进行介绍)。"set_clock_gating_style"命令有很多选项,我们可以在Power Compiler用"man set_clock_gating_style"命令来查看其详细的使用方法。
==============================================================================
设置了门控时钟的加入风格之后,我们就可以设置在门级网表电路中加入门控时钟。在Power Compile:里,用insert_clock_gating命令可在GTECH网表上加入门控时钟。这个命令可以单独使用,也可以配合一些选项,设置一些功能,我们下面主要介绍一下-global选项。
我们来看一下下面这段代码:
always @ (posedge clk)begin
if (a && b) q=d;
end
当有多个模块都有这段代码时,单单利用insert_clock_gating命令就会得到下面的带门控时钟的电路:
上述电路中,有两个模块都有门控时钟,都是同一个控制信号。那么我们就可以使用insert_clock_gating -global选项,让门控时钟可以穿越层次结构,插入到设计中。这样一来,既可以省门控时钟,又可以省面积。使用该选项后,综合得到的带门控时钟的电路如下所示:
因此使用insert_clock_gating加选项“-global",可以使门控时钟穿越层次结构。如果不用选项“-global",在每个模块里有一个门控时钟单元。
实现门控时钟的方法就如前面所示,主要是设置门控时钟的风格和加入门控时钟这两个命令以及他们的一些选项。
④门控时钟的处理
我们在门级网表中加入门控时钟之后,有时候需要对门控时钟进行修修改改,比如说删除一些门控时钟之类的。下面我们就来介绍一下常见的一些门控时钟处理。
·重新连接门控时钟
如下图所示:
上面的左边图中,寄存器A由CG1触发(也就是原来由上面的门控单元CG1进行控制)。由于寄存器A距离门控时钟单元CG2更接近,我们更想让寄存器A与门控单元CG2进行连接来减少连线的长度,因此我们需要进行重新连接。重新连接后,寄存器A由CG2触发,如上面右边的图所示。上面重新连接所使用的命令如下所示:
rewire_clock_gating -gating_cell CG2 -gated_objects {reg_A}
此外,我们可以使用rewire_clock_gating的-proximity选项,使用这个选项后,Power Compiler会自动重新连接寄存器,使时钟门控单元CG到寄存器的连线最短:
rewire_clock_gating -proximity
·重新平衡门控时钟的扇出
如下图所示:
左图是原来的设计。当我们对电路进行优化时(比如使用compiler_utral -retiming 或者 optimize_registers命令),设计中的寄存器可能被移动或删除,如中图所示。
寄存器优化后,门控时钟的扇出不平衡。而门控时钟有最小和最大扇出的约束,对于每一个单独的CG单元(如中图所示)最小扇出的条件不能满足。Power Compiler就需要相关的命令重新平衡门控时钟的扇出,使用的命令如下所示:
rewire_clock_gating -balance_fanout
使用上述命令后,Power Compiler将CG单元合并,以满足最小/最大扇出的约束。重新平衡后的设计如右图所示。
·合并门控时钟
如果两个或以上的门控时钟单元的输人逻辑相等,它们可以被合并。合并只能在一个层次内部进行。合并后,冗余的逻辑被删除。如下图所示:
合并的命令为"merge_clock_gating_cells"
·删除门控时钟
有时候,我们需要删除某些门控时钟,这个时候我可以使用remove_clock_gating命令。该命令即一些选项如下所示:
remove_clock_gating
[-gating_cells CG_cells_list]
[-gated_registers gated_register_list]
[-all] [hier]
如下图所示:
上图上半部分是使用了-gated_registers这个选项,将原来的门控单元删除,换成“使能-载入”模式;上图的下半部分是使用了-gating_cells这个选项,原来的一个门控时钟删除。
因此我们可以通过指定门控时钟单元或通过指定寄存器删除门控时钟。如果在使用删除门控时钟命令时用了开关选项“-all",当前设计中的所有门控时钟都会被删除。
⑤手动插入门控时钟
上面介绍的是使用EDA工具,配合代码自动生成门控时钟。我们也可以以手工的方式设计门控时钟,下面是一个手动设计门控时钟的的代码例子:
assign Gated_Clock = Clock&Enable ;
always@(posedge Gated_Clock or negedge Reset)begin
if(!Reset)
Data_ Out<=8’b0;
else
Data Out<=Data Out+8'b1
end
对于手工门控时钟,Power Compiler将不插入时钟门控单元,也不能对它进行操作(比如重新平衡之类的)。
手工门控时钟可以被取代,取代的脚本如下:
······
create_clock -period 5 [get_ports clk]
set_clock_gating_style ...
replace_clock -gating_cells
······
取代前后的电路如下所示:
取代手工门控时钟的好处在于:取代后,它可以避免产生潜在的毛刺(glitch),也可以允许在它上使用其他的CG命令。例如我们可以使用remove_clock_gating命令去掉门控时钟或使用rewire_clock_gating命令重新连接门控时钟。
门控时钟的低功耗设计到这里就介绍完了,关于门控的STA、DFT和CTS的问题我们就不进行介绍了。
(5)其他
有的时候,我们不经意间就会引入多余的翻转,多余翻转就引起的多余的功耗。下面是多余翻转的例子(类似于操作数隔离):
上图中,当load_out无效时,如果laod_op有效,那么数据就会进行操作,也就是存在相应的翻转。因为输出是无效的,所以这些翻转是多余的,消耗了功耗,但却没有输出。因此我们就要注意,当load_out无效时,load_op也要设置为无效,这样就可以节省部分功耗。
对于上面的多余翻转,可以进行相应的压缩来节省功耗,如下所示:
sel_in同时控制了数据输入和多路选择器的输出。SEL=0时,读入A和B,选通操作1;SEL=1时,读入C和D,选通操作2;这时候就可以减少另一半操作引起的翻转,从而减少了功耗。
对于这种数据输入,然后进行操作选择性输出的,可以进行相应的门控来减少多余的翻转,从而减少功耗。
结合综合进行RTL级的低功耗就如上所述了,重点是门控时钟的使用和操作数隔离技术,这是我们前端设计人员需要懂的。