概述:
我们都知道,时钟和复位在IC设计中是非常重要的。有时候需要对时钟进行一些逻辑变化后再作时钟使用(注意时钟最好不要作为除时钟之外的任何信号使用),这些逻辑有的可以做低功耗,有的可以做DFT中的OCC。像是拔牙操作一类的分频我们今天不讨论。今天讨论的任意分频器在许多的数字IC设计与FPGA面试笔试题中都有考察,所以在实(tou)现(xue)了以后做本次学习记录,供大家参考。
1. 先入为主
我们来宏观的看本次分频器的内容,会比较容易理解。关键功能点的电路如图1所示:
电路图结合波形(时序)图来看,我们以三分频为例,如图2
可以看到:
clk=1 select max_clk(D1)
clk=0 select min_clk(D0)
光一个奇数分频可能不够,我们再来一个偶数分频——二分频,如图3
mux出来的信号clk_out是一个三分频时钟。那么,这个minimize(min_clk) 和 maxmize(max_clk) 又是怎么确定的呢?任意分频是怎么实现的呢?我们继续往下看。
2. minimize & maxmize
首先来谈下相对比较难的,奇分频。把上面图2的时序图拿来看
从图中可以发现min/max clk有两点不一样:
1. 占空比
2. 变化的时钟沿
来看它们的区别:
三分频的 高电平持续时间:低电平持续时间 分为2:1和1:2。可以看到max_clk 的占空比为2:1而min_clk的占空比是1:2。max/min clk只需要控制它们岔开半个周期(因为奇数分频会在源时钟的上升沿和下降沿交替变化)即可。
奇分频看完,偶分频就简单了,以二分频为例,高电平持续时间:低电平持续时间 只能是1:1。此时max/min clk 除了相位不同外,其他都相同,如图3。
总结一下,任意分频时要区分奇分频和偶分频。
输入一个数N为奇数时,要控制max_clk 高低电平占空比为
(N+1)/2 : (N-1)/2
如7分频,要控制高低电平占空比为4:3。min_clk则相反。
输入一个数N为偶数时,要控制max_clk与min_clk 的高低电平占空比同时为 N/2 : N/2。
如8分频,要控制高低电平占空比为4:4
我们现在知道min_clk和max_clk是要控制成什么样子才能做分频了。但是如何控制呢?我们来看下面。
3. 控制min_clk与max_clk
控制min_clk与max_clk是通过计数器来控制的。而min与max需要在奇数偶数上做区分。这个感觉自己讲的不是很明白,直接上代码。(话说代码段没有verilog是什么鬼)
module fre
(
input [5:0] N, //分频系数
input clk,
input rst_n,
output clk_out
);
reg min_clk;
reg max_clk;
reg min_clk_pre; // min_clk前一拍,为什么有它波形中可以得到解释
reg [5:0]num; // 分频系数寄存
reg [5:0]cnt; // 计数器
reg latch_en; // 1分频时用ckgen直接通clk
// CLOCK GETING CELL
always@(*)
if(~clk)
latch_en = (N == 1);
// 存N进num
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
num <= 6'd0;
else
num <= N;
end
// cnt计数
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 6'd0;
else if(cnt == num -1'b1)
cnt <= 6'd0;
else if(N == 1)
cnt <= 6'd0;
else
cnt <= cnt + 1'b1;
end
wire [5:0]min,max; // min_clk 和 max_clk 的变化由这两个数控制
assign min = (num >> 1) - 1'b1; // n/2-1
assign max = min[4:0] + num[0]; // n/2-1 + (num == 奇数? 1'b1 : 1'b0)
//min_clk_pre logic
always@(negedge clk or negedge rst_n) begin
if(!rst_n)
min_clk_pre <= 1'd0;
else
min_clk_pre <= (cnt <= min);
end
//min_clk logic
always@(negedge clk or negedge rst_n) begin
if(!rst_n)
min_clk <= 1'b0;
else
min_clk <= min_clk_pre;
end
//max_clk logic
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
max_clk <= 1'b0;
else
max_clk <= (cnt <= max);
end
assign clk_out = (N != 1) ? (clk ? max_clk : min_clk) : clk; // clk_out mux
endmodule
我们和仿真的波形一起来看,以三分频为例,如图4:
当看完代码(注释)和波形图之后,你应该理解了怎么控制min_clk 和 max_clk了。此时功能是实现了,但是时序呢?我们做的这个东西是要做为时钟使用的,所以我们一定要严谨严谨再严谨。我们来看时序。(只为笔试撕代码的小伙伴可以到此结束)
4. 时序问题
可能你并不知道时序有什么问题,或者说时序是什么。首先你需要明白setup time 和 hold time的概念。这是很基础的概念,所以如果你不明白,快去看看其他大佬的文章。然后我们来看这个分频器的时序问题,如图5
可以看到,如此紧凑的变化频繁的组合逻辑,是很容易产生毛刺的(图中红条部分),时钟一旦有了毛刺,那么这个电路的后果将不可想象。所以我们在做时序分析时,我们的工具也给了我们解决办法:
set_clock_gating_check
这是一种对时钟做逻辑时都可以用到的约束,它的option有以下两组:
-low -high
-setup -hold
-setup -hold 好理解,-low -high是它的两种情况。我们来介绍它的两种情况:
4.1 与情况(-high)
可以看到。当EN = 1时,CLK才可以通过与门,但是为了完整的把CLK的高电平时间全部通过(而不是只通过其中一部分)我们需要EN信号在CLK上升沿到来之前一段时间内(setup time)就做好准备,同样的,EN信号为了使CLK高电平完整的通过,需要在CLK拉低时,再保持一段时间即hold time。
4.2 或情况(-low)
或情况同理,不做赘述,图供大家理解。
4.3 回到分频器
我们了解了set_clock_gating_check以及它的option后。我们来看怎么做,能让我们分频出来的时钟安安稳稳,没有毛刺。以二分频为例,如图6
可以看到,我们小小的分频器两种情况都有,在图中已经标出。大家对号入座是可以看到上述两种情况在我们的分频器中的位置的。
所以,答案也就有了,我们要在约束中加入:
set_clock_gating_check -setup 0.05 -hold 0.05 -high [get_pins max_clk] set_clock_gating_check -setup 0.05 -hold 0.05 -low [get_pins min_clk]
至此,我们的分频器出来的信号就是一个稳定的时钟了。
5. 总结
实现功能固然重要,但是如果只沉浸在实现功能的乐趣中而不去管它的时序,那么对于一个芯片,一个公司来说,那将会是非常危险的事情。FPGA相对来说成本较低,但是如果是ASIC 乃至SOC的话,流片一次的成本是非常高的,所以做IC最重要的应该是慎重和严谨。稍有不慎,公司的资金和长时间的努力将付之一炬。第一次写博客,不足之处也希望大家指出,我也在走往严谨的路上!
(需要testbench请私信)