前言
??在许多设计中,常常伴随着对时钟的各种需求,如需要进行偶数倍分频,奇数倍分频;对于时钟的处理也很重要,如何防止时钟截断,时钟毛刺,减少累计时钟偏移;在低功耗设计中,如何降低时钟网络和其中寄存器的功耗; ??进而衍生出各种技术,如行波计数器、计数分频器、门控时钟、锁存器门控时钟等待。 ??在数字设计中,产生时钟信号的方法主要有两种,一种是通过PLL锁相环对时钟源进行分频或倍频,另一种是在设计的模块中用硬件描述语言描述分频逻辑。 ??在本文中将对这些技术进行简要的介绍,并给出若干设计实例。
一、开源资料下载链接
??https://hihii11.github.io/verilog_clockmanager.html
二、行波计数器
2.1 行波计数器介绍
??图2.1给出了一个三级行波计数器,每个D触发器的反相输出端~Q与输入端D相连。正向输出端作为下一级触发器的时钟信号。 ????????????????图2.1 三级行波计数器
这样设计的行波计数器相对于其他分频器来说,其具有以下特点。 1.每级D触发器可对源时钟进行2n分频。 ??如上图2.1中, ????clk1 = clk_source / 2; ???? clk2 = clk_source / 4; ???? clk3 = clk_source / 8; ?? 其各级时钟波形如图2.2所示。 ????????????????图2.2 各级时钟波形 2.资源消耗少 ??由于行波计数器仅通过若干个触发器级联产生时钟,所以其没有附加的组合逻辑,故消耗资源较少。 3.功耗低 ??由于行波计数器消耗资源少,故在CLK发生反转时,所带动的处于活跃的组合逻辑部分也较少,因此由这部分逻辑产生的峰值功耗大大降低。 ??在低功耗设计中常用行波计数器。
行波计数器也存在以下缺点: 1.行波计数器有较严重的级联效应 ??由于行波计数器的级联结构,其每一级时钟都会产生一定的滞后。 如: ??clk1 相较于 clk_source 会产生由U1A所引入的触发器延时及布线延时(tU1A)。 ??clk2 相较于 clk1 会产生由U2A所引入的触发器延时(tU2A)。 ??clk3 同理。 ??由此一来,延迟会不断积累,如图2.3所示。 ????????????????图2.3 行波计数器的级联效应 ??一方面这种延迟可能会引入毛刺,另一方面在同步电路设计中,有可能会违背数据的建立保持时间。 ??因为行波计数器产生的时钟本质上是异步的,依赖其产生的时钟的模块,在进行数据交互时,属于跨时钟域。 2.会对STA工具和综合工具带来麻烦 ??行波计数器在各阶段创建时钟所引入的级联效应导致的问题,会增加STA工具和综合工具的工作量。
2.2 设计实例
1.模块代码
module travel_wave_counter#(
parameter integer LEVELs = 3
)
(
input wire CLK_IN,
input wire nRST,
output wire [LEVELs-1:0] CLK_OUT
);
genvar i;
generate
for(i = 0;i < LEVELs;i = i+1)
begin
if(i == 0)
begin
travel_wave_level travel_wave_level_inist0(
.CLK_IN(CLK_IN),.nRST(nRST),.DOUT(CLK_OUT[i]));
end
else
begin
travel_wave_level travel_wave_level_inist0(
.CLK_IN(CLK_OUT[i-1]),.nRST(nRST),.DOUT(CLK_OUT[i]));
end
end
endgenerate
endmodule
module travel_wave_level(
input wire CLK_IN,
input wire nRST,
output wire DOUT
);
reg dout;
assign DOUT = dout;
always@(posedge CLK_IN,negedge nRST)
begin
if(~nRST)
begin
dout <= 1'b0;
end
else
begin
dout <= ~dout;
end
end
endmodule
2.综合电路框图
2.3 总结
??行波计数器产生时钟属于异步,同步应当避免。 ??在低功耗设计等情况下,可以尝试使用行波计数器,但需要严格控制。
三、计数分频器
3.1 计数分频器介绍
??针对上述行波计数器的缺点,在同步电路设计中,可以采用计数器或状态机来产生分频时钟。 ??利用计数器产生分频器需要注意: ??1.应由寄存器直接产生时钟信号,永远不要对计数器或状态机输出进行译码。 ??原因是状态改变时信号会产生竞争冒险,从而导致逻辑错误,产生的时钟信号出现毛刺,会导致电路逻辑错误。 ??2.对主频进行分频时,应使用同步计数器或状态机。
3.2 设计实例
??典型的计数分频器设计:(注意本小节计数器均为偶数分频) 1.使用两个同步always逻辑块
module div_counter#(
parameter integer DIV_NUM = 2
)(
input wire CLK_IN,
input wire nRST,
output wire CLK_OUT
);
reg [15:0] clk_cnt;
reg clk_out;
assign CLK_OUT = clk_out;
always@(posedge CLK_IN)
begin
if(~nRST)
begin
clk_cnt <= 16'd0;
end
else
begin
if(clk_cnt != DIV_NUM - 1)
clk_cnt <= clk_cnt + 16'd1;
else
clk_cnt <= 16'd0;
end
end
always@(posedge CLK_IN)
begin
if(~nRST)
begin
clk_out <= 1'b0;
end
else
begin
if(clk_cnt < DIV_NUM/2)
clk_out <= 1'b1;
else
clk_out <= 1'b0;
end
end
endmodule
综合后电路: 2.一个always逻辑块,一个组合逻辑
module div_counter#(
parameter integer DIV_NUM = 2
)(
input wire CLK_IN,
input wire nRST,
output wire CLK_OUT
);
reg [15:0] clk_cnt;
reg clk_out;
assign CLK_OUT = (clk_cnt < DIV_NUM/2)?1'b1:1'b0;
always@(posedge CLK_IN)
begin
if(~nRST)
begin
clk_cnt <= 16'd0;
end
else
begin
if(clk_cnt != DIV_NUM - 1)
clk_cnt <= clk_cnt + 16'd1;
else
clk_cnt <= 16'd0;
end
end
endmodule
综合后电路: ??两种设计方式的区别在于,第一种设计时钟信号是通过时序逻辑驱动产生的,而第二种是直接通过组合逻辑产生。 ??第二种设计对于信号毛刺的抗干扰性较差,第一种可以很好地滤除毛刺。
3.3 总结
??对于行波计数器的缺点,可采取计数器的方法进行时钟分频。 ??完全同步的设计,时钟偏移现象相对行波计数器较轻。 ??消耗的资源较行波计数器较多,且功耗较大。
四、门控时钟设计
4.1 门控时钟介绍
??门控时钟是减少功耗的有力手段,在时钟被门控关闭后,该时钟网络和其中的寄存器都会停止翻转,因此功耗会显著减低。 ??但门控时钟也会带来一些问题: ??1.门控时钟属于非同步设计,会增加设计时间和验证工作量。 ??2.门控时钟会在时钟链路上增加额外的时钟偏移。 ??3.门控时钟对毛刺敏感,可能导致设计失败。
传统的门控时钟设计如图3.1所示: ????????????????图4.1 典型门控时钟电路 ??对于时钟源clk_source经过一个额外的与门U2A,当clk_en为高时,clk_source能过通过到达U1A触发器,当clk_en为低时clk_source无法到达触发器。 ??当clk_en为低时,触发器及其时钟网络中的所有组合逻辑停止翻转,out端保持不变。 ??由于clk_source经过了U2A与门,所有会产生一定的时钟偏移。
4.2伪门控时钟
??上述问题可以通过同步设计来解决。 ??使用MUX的同步设计方案: ????????????????图4.2 伪门控时钟电路 ??可以通过clk_en来控制触发器输入端口D的数据,若此时选择的数据为out端口,则触发器输出将保持不变。 ??但由于并未切断时钟树,所以触发器内部包括组合逻辑都处于活跃状态,功耗并未得到实质性降低。
4.3 基于锁存器的门控时钟
传统的门控时钟仅有一级与门进行门控控制,其会产生时钟截断情况: ????????????????图4.3 传统门控时钟的时钟截断现象 ??在6时刻,因为clk_en由高变低,所以其输出clk_out被截断,同理在8时刻,由于clk_en变高,clk_out同样被截断。 ??这样的截断会产生以下一些问题: ??1.时钟占空比不平衡。 ??2.产生的脉冲尖峰(干扰)会引起保持建立时间的问题。
??故在设计中可采用锁存器来避免时钟截断。
4.3 基于锁存器的门控时钟设计实例
基于锁存器门控时钟的verilog描述:
module gata_clock(
input wire CLK_IN,
input wire CLK_ENI,
output wire CLK_OUT
);
reg clk_eno;
assign CLK_OUT = clk_eno & CLK_IN;
always@(~CLK_IN)
begin
clk_eno <= CLK_ENI;
end
endmodule
时钟截断仿真: 由于在FPGA设计中,板上资源有时没有锁存器; 此时可以将锁存器换为触发器;
module gata_clock(
input wire CLK_IN,
input wire CLK_ENI,
output wire CLK_OUT
);
(*KEEP="true"*)reg clk_eno;
assign CLK_OUT = clk_eno & CLK_IN;
always@(posedge CLK_IN)
begin
clk_eno <= CLK_ENI;
end
endmodule
综合后电路:
4.4 总结
??合理使用门控时钟可以降低模块功耗。 ??在FPGA设计中,可以使用带触发器的门控时钟来消除时钟截断现象。
五、奇数分频器
5.1 奇数分频器简介
??对于偶数倍的分频,可以使用行波计数器或计数器等方式实现; ??但对于占空比要求为百分之50的奇数分频,上述几种设计就难以实现了; ??对于奇数倍的分频,可以采取以下设计方法: ??1.创建一个模N计数器,计数值为0-(N-1) ??2.创建两个T触发器,用于产生时钟产生逻辑LOGIC0,LOGIC1 ?? ??需要注意产生LOGIC0的T触发器需在输入时钟的上升沿采样 ???? 产生LOGIC1的T触发器在输入时钟的下降沿采样 ??3.创建两个T触发器使能信号TFF_EN0,TFF_EN1 ????TFF_EN0在第1步中,计数值为1的时候置位 ????TFF_EN1在第1步中,计数值为(N+1)/2时置位 ??4.创建产生时钟逻辑,CLK_OUT = LOGIC0 ^ LOGIC1
5.2 设计实例
奇数分频器设计代码:
module odd_clk_div#(
parameter integer DIV_NUM = 3
)(
input wire CLK_IN,
input wire nRST,
output wire TFF_EN0,
output wire TFF_EN1,
output wire LOGIC0,
output wire LOGIC1,
output wire [15:0] CLK_CNT,
output wire CLK_OUT
);
reg [15:0] clk_cnt;
wire tff_en0,tff_en1;
assign CLK_CNT = clk_cnt;
assign tff_en0 = (clk_cnt == 'd0)?1'b1:1'b0;
assign tff_en1 = (clk_cnt == (DIV_NUM+1)/2)?1'b1:1'b0;
assign TFF_EN0 = tff_en0;
assign TFF_EN1 = tff_en1;
always@(posedge CLK_IN)
begin
if(~nRST)
begin
clk_cnt <= 16'd0;
end
else
begin
if(clk_cnt != DIV_NUM - 1)
begin
clk_cnt <= clk_cnt + 16'd1;
end
else clk_cnt <= 16'd0;
end
end
reg clk_div0_lgic;
reg clk_div1_lgic;
assign CLK_OUT = clk_div0_lgic ^ clk_div1_lgic;
assign LOGIC0 = clk_div0_lgic;
assign LOGIC1 = clk_div1_lgic;
always@(posedge CLK_IN)
begin
if(~nRST)
begin
clk_div0_lgic <= 1'b0;
end
else
begin
if(tff_en0)
clk_div0_lgic <= ~clk_div0_lgic;
else
clk_div0_lgic <= clk_div0_lgic;
end
end
always@(negedge CLK_IN)
begin
if(~nRST)
begin
clk_div1_lgic <= 1'b0;
end
else
begin
if(tff_en1)
clk_div1_lgic <= ~clk_div1_lgic;
else
clk_div1_lgic <= clk_div1_lgic;
end
end
当DIV_NUM = 7时,仿真波形如下: 上述设计综合:
5.3 另一种设计思路
??本质上来说,这种奇数倍分频产生了两个2N倍的偶数分频信号,只不过利用两个分频信号的相位差进行逻辑异或得到N分频的信号; ??因此也可以采用以下的设计思路: ??1.首先创建两个模2N的计数器 ????第一个计数器在clk上升沿采样,初值为0 ????第二个计数器在clk下降沿采样,初值为(DIV_NUM+1)/2 - 1 ??2.根据两个计数器的值产生两个2N倍偶数分频信号 ??3.将产生的偶数分频信号进行异或产生时钟
module odd_clk_div#(
parameter integer DIV_NUM = 3
)(
input wire CLK_IN,
input wire nRST,
output wire LOGIC0,
output wire LOGIC1,
output wire [15:0] CLK_CNT0,
output wire [15:0] CLK_CNT1,
output wire CLK_OUT
);
reg [15:0] clk_cnt0;
reg [15:0] clk_cnt1;
wire logic0,logic1;
assign LOGIC0 = logic0;
assign LOGIC1 = logic1;
assign CLK_CNT0 = clk_cnt0;
assign CLK_CNT1 = clk_cnt1;
assign CLK_OUT = logic0 ^ logic1;
assign logic0 = (clk_cnt0 < DIV_NUM ) ? 1'b1:1'b0;
assign logic1 = (clk_cnt1 < DIV_NUM ) ? 1'b1:1'b0;
always@(posedge CLK_IN)
begin
if(~nRST)
begin
clk_cnt0 <= 16'd0;
end
else
begin
if(clk_cnt0 != 2*DIV_NUM - 1)
begin
clk_cnt0 <= clk_cnt0 + 16'd1;
end
else clk_cnt0 <= 16'd0;
end
end
always@(negedge CLK_IN)
begin
if(~nRST)
begin
clk_cnt1 <= (DIV_NUM+1)/2 - 1;
end
else
begin
if(clk_cnt1 != 2*DIV_NUM - 1)
begin
clk_cnt1 <= clk_cnt1 + 16'd1;
end
else clk_cnt1 <= 16'd0;
end
end
endmodule
仿真: 设计综合:
5.4 总结
??奇数分频可通过产生两个相位不同的2N倍分频,异或产生
文献参考
1.《硬件架构的艺术-数字电路的设计方法与技术》
|