门控时钟
通常情况下,时钟树由大量的缓冲器和反相器组成。而时钟信号为设计中翻转率最高的信号,时钟树的功耗可高达整个设计功耗 30%。加入门控时钟(clock gating)电路,可减少时钟树的开关行为,能节省开关功耗。同时,时钟引脚开关行为的减少,寄存器的内部功耗也会减少。所以,采用门控时钟,可以有效地降低功耗。
实现原理
通俗来讲,当模块或触发器不工作时,将时钟关闭而不影响正常功能的逻辑,可以称之为门控时钟逻辑。此时时钟并不是一直存在的,所以可以形象的称之为门控时钟。
实现门控时钟的方法主要有以下 3 种。
1、使用与逻辑
最简单的方法,是直接将时钟使能控制(门控)信号与时钟做"与"逻辑。
例如对一块 ram 的时钟进行该操作,代码如下:
实例
module clkgate_basic
(
input clk ,
input clken ,
input rstn ,
input wr_en ,
input [3:0] addr ,
input [7:0] data ,
output [7:0] q
);
//clk gate
wire clk_gate = clk & clken ;
ram #(4, 8)
u1_ram16x8
(
.CLK (clk_gate),
.A (addr),
.D (data),
.EN (clken),
.WR (wr_en),
.Q (q));
endmodule
ram 模型如下:
实例
#( parameter AW = 2 ,
parameter DW = 3 )
(
input CLK ,
input [AW-1:0] A ,
input [DW-1:0] D ,
input EN ,
input WR , //1 for write and 0 for read
output reg [DW-1:0] Q
);
parameter MASK = 3 ;
reg [DW-1:0] mem [0:(1<<AW)-1] ;
always @(posedge CLK) begin
if (EN && WR) begin
mem[A] <= D ;
end
else if (EN && !WR) begin
Q <= mem[A] ;
end
end
endmodule
testbench 代码如下:
实例
module test ;
//signals declaration
reg rstn ;
reg clk ;
reg clken ;
reg wr_en ;
reg [3:0] addr ;
reg [7:0] data ;
wire [7:0] q ;
initial begin
rstn = 0 ;
#7 rstn = 0 ;
end
always begin
#50 clk = 0 ;
#50 clk = 1 ;
end
//data logic
initial begin
clken = 0 ;
wr_en = 0 ;
addr = 4'h3 ;
data = 8'h31 ;
# 53 ;
//(1) normal write and read
clken = 1 ;
wr_en = 1 ;
repeat(9) begin
@(negedge clk) ;
data = data + 1 ;
addr = addr + 1 ;
end
@(negedge clk) ;
clken = 0 ;
wr_en = 0 ;
//read
#211;
addr = 4'h3 ;
clken = 1 ;
repeat(9) begin
@(negedge clk) ;
addr = addr + 1 ;
end
@(negedge clk) ;
//end
clken = 0 ;
end // initial begin
clkgate_basic u_ram_clkgate
(
.clk (clk),
.clken (clken),
.rstn (rstn),
.wr_en (wr_en),
.addr (addr),
.data (data),
.q (q)
);
//simulation finish
always begin
#100;
if ($time >= 10000) begin
#1 ;
$finish ;
end
end
endmodule
抓取 ram 端口信号,测试结果如下。
如图可知在读、写操作中间,ram 时钟有一段一直为 0 的时刻。时钟在 ram 没有工作的时候不会翻转,实际中也会减少很多的功耗。
该方法缺点也非常明显。由于时序或抖动的原因,时钟使能信号与时钟进行"与"逻辑后,容易产生毛刺,会对数字电路产生严重影响。
在 testbench 中对时钟使能信号进行非理想的模拟,加入如下仿真代码:
实例
#985;
addr = 4'h3 ;
clken = 1 ;
repeat(9) begin
@(negedge clk) ;
addr = addr + 1 ;
end
@(negedge clk) ;
clken = 0 ;
#20 clken = 1 ;
#21 clken = 0 ;
#31 clken = 1 ;
#13 clken = 0 ;
读 ram 的仿真结果如下。
由图可知,因为信号 clken 的异步或抖动问题,导致输入到 ram 的时钟已经出现了毛刺。地址为 0x3 的数据被遗漏(和使能信号时序有关),地址为 0xC 的数据读了 2 次。显然该门控时钟的逻辑设计非常的危险。
为解决此类问题,需要使用 latch 结构来消除毛刺。
2、使用 latch
在 《Verilog 教程》章节《6.5 Verilog 避免 Latch》中讲到,数字设计中应当避免 Latch 的产生,但 clock gating 是个例外。所以在进行时序分析时,不用关心 clock gating 部分产生的 Latch。
使用 latch 消除门控时钟毛刺的电路图如下所示。
在时钟下降沿对时钟使能信号进行锁存,并保持一个时钟周期内不变。锁存后的信号再与时钟进行"与"逻辑操作,可将门控时钟中的毛刺消除掉。
将仿真例程钟直接"与"操作的门控时钟逻辑部分,改写为使用 latch 逻辑,修改如下:
实例
reg en_latch ;
always @(*) begin
if (!clk) begin
en_latch = clken ;
end
end
wire clk_gate = clk & en_latch ;
仿真结果如下。
虽然地址为 0x3 的数据仍然被遗漏(和使能信号时序有关),但 ram 的时钟已经是正常时钟,不再出现毛刺(标黄的 CLK 信号)。
3、使用标准单元库
虽然使用 latch 可以解决门控时钟毛刺的出现,但是时序也需要严格的约束。
FPGA 或 IC 设计时,综合库中往往会有集成门控逻辑单元。此类门控逻辑单元经过了大量的更新迭代和验证,使用起来更加的方便、安全。
因此一般情况下,门控时钟的设计也都会直接调用专用的集成门控逻辑单元。调用方式和基本的与门、缓冲器等基本单元类似,直接例化即可。
使用方式
合理的使用门控时钟逻辑,对电路时钟进行控制,也会有效的减少功耗。
1、手动 gating
增加时钟使能信号,人为的控制模块工作时钟的有无。
模块工作时,将使能信号有效,时钟打开;模块空闲时,将使能信号无效,时钟关闭,节省功耗。
上一节的仿真设计,就可以看做是手动 gating 的例子。ram 读写时,使能信号为高,ram 时钟打开,ram 开始工作;使能信号为低时,ram 不工作,此时无输入时钟。
2、自动 gating
模块在工作时,可以自动检测自己的工作状态,并输出一个 busy(忙碌) 信号。外部可以通过该指示信号,对模块内部的部分逻辑进行时钟门控,来减少时钟的翻转,达到自动 gating 的控制。
与手动 gating 不同的是,这些模块在工作时,会有短暂的空闲状态。自动 gating 就是要在这短暂的空闲状态时间内关闭掉无用的时钟,而不是像手动 gating 直接关闭掉整个模块的时钟,否则该模块就不能再正常工作。
例如 uart 在完成一次传输数据时会有一段空闲的状态,此时可将一些 fifo 逻辑、波特率产生逻辑等模块的时钟自动关闭。
例如在包含 cpu 的设计中,可加入检测 bus 总线空闲状态的逻辑,自动控制开关 bus 总线的时钟,以此降低功耗。
限于篇幅,这里先不再举例仿真。
3、自动插入 gating
当 RTL 设计完成之后进行逻辑综合时,编译器也会对代码的逻辑进行自动优化,这就包括将一些触发器的时钟端进行 gating。例如一个带使能端的同步 D 触发器的 RTL 描述如下:
实例
always @(posedge CLK) begin
if (EN) begin
Q = D ;
end
end
其 RTL 前级仿真如下图所示:
综合后的仿真波形往往会如下图所示:
对比可知,综合后已经没有了 EN 信号,时钟(CP 端)经过 clock gating 后也不是一直存在。既保证了逻辑的正确性,又减少了时钟翻转,降低了功耗。
点我分享笔记