Verilog各类分频器设计详解
Verilog各类分频器设计详解
分频器是时序电路的基本器件,它的功能是对系统时钟或其他时钟进行分频产生所需要的时钟信号。分频有两种方式:一是通过HDL语言建模产生所需要的时钟信号,二是利用开发工具的PLL进行分频。前者分频灵活,需编写代码实现;后者使用场景受限,因为有的低端FPGA没有PLL,但PLL的分频效果更好,而且在进行小数分频时也比较容易实现。本文首先尝试用HDL语言建模方式设计各种类型的分频器,最后给大家简单介绍一下PLL的使用。如有不足之处还望大家批评指正。
偶数分频器
我们先从最简单的偶数分频器切入,慢慢分析各种分频器的实现。
若要实现二分频,则只需要在原时钟的上升沿进行输出时钟状态的翻转即可,如下图所示
若实现四分频呢?则需要一个计数器,每次在原时钟的上升沿计数,当计数器记到2个上升沿时输出时钟状态进行翻转,如下图所示
现在对一般情况进行分析,对时钟进行N分频,N为偶数;则计数器每次在原时钟的上升沿计数,计数器的范围为0~(N-1),我们可以在0—(N-1)这N个数中分出两个范围选择输出时钟的状态,如当cnt在0—M范围时输出时钟为低电平,当cnt在(M+1)—(N-1)范围时输出时钟为高电平,则我们可以动态调整输出时钟的占空比,输出时钟的占空比为(N-M-1)/N;
以8分频为例,则N=8,取M=3,则此时输出的时钟应当是50%占空比,如下图所示
若取M=1,则此时输出时钟的占空比应当是75%,如下图所示
偶数分频器代码
1 | // 偶数分频器示例,可调占空比 |
奇数分频器
我们先以三分频模块切入,分析奇数分频器的思想:整体思路是产生两路上升沿和下降沿触发信号,然后对这两路信号进行操作得到最终分频时钟。
当分频系数N为奇数时,使用一个计数器在0~(N-1)循环进行计数,控制输出(N-1)/2个高电平,(N+1)/2个低电平,称为clk_1
;然后将此clk_1
电平信号延迟半个周期称为clk_2
,最后输出clk_out = clk_1 | clk_2
,即为占空比为50%的奇数分频器;另一种思路是产生输出(N+1)/2个高电平,(N-1)/2个低电平的clk_1
,输出的分频时钟为clk_out = clk_1 & clk_2
。
以clk_out = clk_1 & clk_2
为例,将clk_1
延时半个时钟周期的方法有两种,法1是直接使用下降沿的锁存器对clk_1
锁存得到clk_2
,法2得到clk_2
的原理与clk_1
相同,不过是在下降沿检测。
若采用法一,则我们用一个下降沿触发的D触发器锁存clk_1
的结果clk_2
,然后将clk_1
与clk_2
信号做逻辑“或”就得到了占空比50%的分频时钟信号clk_out
。
结合上面的偶数分频器,如果N为偶数,则clk_1
就是我们所需要的分频结果,如果N为奇数,则clk_1 & clk_2
就是我们所需的分频结果,所以我们可以把偶数分频和奇数分频结合,实现N分频器设计,其中N为正整数;通过N[0]选择输出,N[0]=1为奇数分频,N[0]=0为偶数分频。
5分频结果如下:
6分频结果如下:
正整数分频器代码
1 | module clk_div_integer #( |
半整数分频器
1、占空比非50%
网上比较多的分频思路是:半整数分频多出来的那半个周期为高电平,其余为低电平。以5.5分频为例,以原时钟的半周期为单位,可以分频输出1高10低。原理是用计数器循环记数0~10即11个周期,控制输出clk_1
前6周期高电平,后5周期低电平,然后再使用该计数器得到一下降沿触发的5低6高的输出clk_2
,最后输出clk_out = clk_1 & clk_2
。波形图如下图所示
现在我们虽然得到了5.5分频后的信号,但占空比不是很理想,是否可以在其基础上进行改进来实现占空比近似50%的分频信号呢?
2、占空比近似50%
由上面的波形图我们可以看到,如果clkx
和clky
在cnt
的其它状态(稍微偏大的值)进行状态翻转的话,可能输出的clk_out = clk_1 & clk_2
就能达到近似50%的占空比。在尝试后发现,clkx
在cnt
等于N+M
和2N
时进行状态翻转,clky
在cnt
等于N
和M
时进行状态翻转,此时输出的clk_out = clk_1 & clk_2
近似50%占空比。其中N为不超过分频系数的最大整数,如5.5分频时N=5
,当N为奇数时M=(N-1)/2
,当N为偶数时M=(N+1)/2
占空比近似50%的4.5分频的波形图如下所示
半整数分频器代码
1 |
|
小数分频器
以8.7分频为例来分析小数分频器的设计。因为无法用计数器表示0.7这种数字,所以我们用一个等效的概念来实现8.7分频,原时钟87个周期的总时间等于分频后的时钟10个周期的总时间。
因为8.7分频在8分频和9分频之间,所以我们用8分频和9分频来组合生成8.7分频的时钟。可以列方程组,设8分频共x个周期,9分频共y个周期,则
- x+y=10 (1)
- 8*x + 9 *y = 87 (2)
解得x=3,y=7。
即通过3次8分频和7次9分频可得到8.7分频。但如果是按序先输出3个8分频再输出7次9分频的时钟用处不大,我们还得乱序使其均匀输出,不然会造成时钟频率均匀性不好,相位抖动大的问题。
此处我们介绍脉冲删除小数分频,该方法相对比较简单。什么意思呢?就是说我在87个输入时钟里删掉77个时钟周期,这样不就输出了10个时钟周期了吗?也就实现了8.7分频,那么该怎么删呢?查阅论文后得到结论:
- 设置寄存器cnt位宽自定,初始值为0;
- 在
clk
的上升沿cnt=cnt+分母
,并判断cnt
是否大于分子,若大于分子则在下一周期减去分子; cnt
小于分子时,输出脉冲信号为0,cnt
大于分子时,输出脉冲信号为1;
说起来比较乱,我们以7/3分频为例来看
时钟序号 | cnt | 输出脉冲 |
---|---|---|
0 | 3 | 0 |
1 | 6 | 0 |
2 | 9 | 1 |
3 | (12->)5 | 0 |
4 | 8 | 1 |
5 | (11->)4 | 0 |
6 | (7->)0 | 1 |
从表中可以看到每7个周期输出3个脉冲,刚好满足分频要求。
小数分频器代码
1 |
|
8.7分频结果如下
3.25分频结果如下
PLL分频
这里就给大家简单介绍一下如何在Vivado中对PLL进行例化。
首先打开vivado
,新建一个RTL项目,点击Flow Navigator
窗口中的IP Catalog
,在search
处搜索自己想要的IP核的名字,例如输入clock
就会找到Clocking Wizard
这个IP核,如下图所示
双击Clocking Wizard 这个IP核,就能弹出配置窗口;我们简单配置一些信息,输入时钟为100MHz,分频输出两个时钟,一个是30MHz,一个是18MHz,查看分频效果;
点击Generate,生成IP核,然后在source窗口就会出现一个文件
下面对这个生成的IP进行例化,测试分频效果
将这个例化模块的例程添加到自己的顶层仿真代码中,就可以查看分频结果了。
本次的博客就讲到这里,其中PLL的使用讲的很浅显,大家要想深入学习PLL的IP使用的话可以学习官方文档。若文章中存在任何错误或不足欢迎大家指正,欢迎大家在博客下方留言交流。