Hugo ʕ•ᴥ•ʔ Bear Blog

💡 特别鸣谢野火 FPGA 的教学与帮助!
本节相关的教程 link 📌

- 第 18 章 分频器:
  - 18.0 章节导读:
  - 18.1 理论学习:
  - 18.2 实战演练一:
    - 18.2.1 实验目标:
    - 18.2.2 硬件资源:
    - 18.2.3 程序设计:
      - 1. 模块框图:
      - 2. 波形图绘制:
        - 方法 1 实现:仅实现分频功能:
        - 方法 2 实现:实用的降频方法:
      - 3. 代码编写:
        - 方法 1 实现:仅实现分频功能:
        - 方法 2 实现:实用的降频方法:
      - 4. 仿真验证:
        - 仿真文件编写:
        - 仿真波形分析:
          - 方法 1 实现:仅实现分频功能:
          - 方法 2 实现:实用的降频方法:
      - 18.2.4 上板验证:
        - 1.  引脚约束:
        - 2.  结果验证:
  - 18.3 实战演练二:
    - 18.3.1 实验目标:
    - 18.3.2 硬件资源:
    - 18.3.3 程序设计:
      - 1. 模块框图:
      - 2. 波形图绘制:
        - 方法 1 实现:仅实现分频功能:
        - 方法 2 实现:实用的降频方法:
      - 3. 代码编写:
        - 方法 1 实现:仅实现分频功能:
        - 方法 2 实现:实用的降频方法:
      - 4. 仿真验证:
        - 仿真文件编写:
        - 仿真波形分析:
          - 方法 1 实现:仅实现分频功能:
          - 方法 2 实现:实用的降频方法:
    - 18.3.4 上板验证:
      - 1. 引脚约束:
      - 2. 结果验证:
  - 18.4 章末总结:
  - 18.5 拓展训练:

18.0 章节导读


时钟对于 FPGA 是非常重要的,但板载晶振提供的时钟信号频率是固定的,不一定满足工程需求,所以使用分频或倍频产生需要的时钟是很有必要的。

18.1 理论学习


数字电路中时钟占有很重要的地位,时间的计算都要以时钟作为基本的单元。一般来说我们使用的开发板上面只有一个晶振,即只有一种频率的时钟。但在数字系统设计中, 经常需要对基准时钟进行不同倍数的分频而得到各模块所需的时钟频率,若是想得到比固定的时钟频率更慢的时钟,可以将该固定时钟进行分频,若是想得到比固定时钟频率更快的时钟,则可以在固定时钟频率的基础上进行倍频。无论分频和倍频,我们都有两种方式可以选择,一种是器件厂商提供的锁相环(PLL,后面章节会讲解),另一种是自己动手来用 Verilog 代码描述。

而我们用 Verilog 代码描述的往往是分频电路,即分频器。分频器是数字系统设计中最常见的基本电路之一。所谓“分频”,就是把输入信号的频率变成成倍数地低于输入频率的输出信号。它的原理是:把输入的信号作为计数脉冲,由于计数器的输出端口是按一定规律输出脉冲的,所以对不同的端口输出的信号脉冲,就可以看作是对输入信号的“分频”。至于分频频率是怎样的,由选用的计数器所决定。如果是十进制的计数器那就是十分频,如果是二进制的计数器那就是二分频,还有四进制、八进制、十六进制等等以此类推。 分频器是和计数器非常类似的功能,有时候甚至可以说就是一个东西,我们可以回想上一节中计数器的内容,可以观察我们的仿真波形,对比下时钟信号 sys_clk 和 led_out 信号的关系,你会发现 led_out 信号实际上就是对时钟信号 sys_clk 进行了分频。

18.2 实战演练一


18.2.1 实验目标


分频器分为偶数分频器和奇数分频器,我们在上一章计数器的例子中其实实现的就是偶数分频,这里我们也先偶数分频器开始介绍。本例我们将实现对系统时钟进行 6 分频的偶数分频电路的设计。

18.2.2 硬件资源


使用板卡引出 I/O 口 E9 进行时钟输出。

18.2.3 程序设计


1. 模块框图


因为我们设计的是 6 分频电路,所以模块取名为 divider_six,然后是端口信号的设计,首先必不可少的是时钟 sys_clk 和复位信号 sys_rst_n,分频器和计数器一样一般都只作用于 FPGA 内部的信号,这里我们没有其他的外部输入信号,其输出也往往提供给 FPGA 内部信号使用,这里我们将分频模设计为一个独立的模块。根据上面的分析设计出的 Visio 框图如图 18-3 所示。

2. 波形图绘制


方法 1 实现:仅实现分频功能

首先绘制两个输入信号的波形 sys_clk 时钟信号和 sys_rst_n 复位信号,既然需要分频那肯定需要一个计数器,所以我们定义一个名为 cnt 的计数器,根据上一章的分析得知对于计数器我们要精确的控制它何时计数、何时清零,这里计数的开始我们没有特殊要求, 即只要时钟正常工作且复位被释放我们就可以立刻进行计数。那计数器计数到多少清零呢?我们需要对输入的系统时钟时钟进行 6 分频,那需计数器计数 0~5 这 6 个数吗?当然不需要,和上一章的思考过程是一样的,我们只需要让计数器从 0 计数到 2,即计 3 个数就可以了,然后每当计数器计数到 2 的时候就让 clk_out 输出信号取反即可,如图 18-4 所示产生的 clk_out 输出信号就是对 sys_clk 时钟信号的 6 分频。

方法 2 实现:实用的降频方法

方法一中的 clk_out 输出信号是我们想要的分频后的信号,然后很多同学就直接把这个信号当作新的低频时钟来使用,并实现了自己想要的功能。大家肯定会觉得能够实现功能就一切 OK 了,而往往忽略了一些隐患的存在.

如果你对 FPGA 的了解多一些就会理解其实这是不严谨的做法,这种做法所衍生的潜在问题在低速系统中不易察觉,而在高速系统中就很容易出现问题。因为我们通过这种方式分频得到的时钟虽然表面上是对系统时钟进行了分频产生了一个新的低频时钟,但实际上和真正的时钟信号还是有很大区别的。因为在 FPGA 中凡是时钟信号都要连接到全局时钟网络上,全局时钟网络也称为全局时钟树, 是 FPGA 厂商专为时钟路径而特殊设计的,它能够使时钟信号到达每个寄存器的时间都尽可能相同,以保证更低的时钟偏斜(Skew)和抖动(Jitter)。而我们用这种分频的方式产生的 clk_out 信号并没有连接到全局时钟网络上,但 sys_clk 则是由外部晶振直接通过管脚连接到了 FPGA 的专用时钟管脚上,自然就会连接到全局时钟网络上,所以在 sys_clk 时钟工作下的信号要比在 clk_out 时钟工作下的信号更容易在高速系统中保持稳定,既然发现了问题那我们该怎么办呢? 这时可不要忘记了上一章中刚学到的 flag 标志信号,这里我们就可以用上了,我们可以产生一个用于标记 6 分频的 clk_flag 标志信号,这样每两 clk_flag 脉冲之间的频率就是对 sys_clk 时钟信号的 6 分频,但是计数器计数的个数我们需增加一些, 如图 18-5 所示需要从 0~5 共 6 个数,否则不能实现 6 分频的功能。和方法 1 对比可以发现,相当于把 clk_out 的上升沿信号变成了 clk_flag 的脉冲电平信号(和上一章方法 2 中的 cnt_flag 是一样的道理),为后级模块实现相同的降频效果。虽然这样会多使用一些寄存器资源,不过不用担心我们的系统是完全可以承担的起的,而得到的好处却远远大于这点资源的使用,能让系统更加稳定。

这样在后级模块中需要使用低频时钟的情况,我们就可以不用 clk_out 这种信号作为时钟了,而是继续使用 sys_clk 系统时钟来作为时钟,但让其执行语句的条件以 clk_flag 信号为高电平的时候有效。

3. 代码编写


方法 1 实现:仅实现分频功能

方法 2 实现:实用的降频方法

4. 仿真验证


仿真文件编写

仿真波形分析

方法 1 实现:仅实现分频功能

打开 ModelSim 执行仿真,仿真出来的波形如图 18-8 所示,我们让仿真运行了 500ns,可以发现 clk_out 信号产生了几个周期的完整波形,我们在 clk_out 相邻两个上升沿的位置处分别放置参考线并添加频率显示,我们可以看到显示的频率为 8.333MHz,而我们系统时钟 sys_clk 是 50MHz 的,大约为 6 分频的关系,从而验证了我们设计的是正确的。

方法 2 实现:实用的降频方法

打开 ModelSim 执行仿真,仿真出来的波形如图 18-9 所示,我们也让仿真运行了 500ns,可以发现 clk_flag 脉冲标志信号每当计数器计数到 5 时产生一个时钟周期的脉冲, 我们在 clk_flag 相邻两个下升沿的位置(也是下一级模块可以采集到 clk_flag 为高电平的位置)处分别放置参考线并添加频率显示,我们可以看到显示的频率为 8.333MHz,也是对 sys_clk 系统时钟的 6 分频,和我们绘制的波形图一致。

18.2.4 上板验证


1. 引脚约束


仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 18-2 所示。引脚配置如图 18-10 所示。

2. 结果验证


程序下载完毕后,如图 18-13 所示,使用示波器对输出 I/O 口 E9 进行频率测量,分频时钟信号频率为 8.333MHz,刚好为输入 50MHz 时钟信号的 6 分频,达到预期效果。

18.3 实战演练二


18.3.1 实验目标


有偶数分频就有奇数分频,仅实现分频功能来讲其中的差别和实现方式还是很大的, 奇数分频相对于偶数分频要复杂一些,并不是简单的用计数器计数就可以实现的。

实现将一个系统时钟进行 5 分频的奇数分频的功能。可以用于将高频的时钟降低为低频的时钟工作使用。

18.3.2 硬件资源


使用板卡引出 I/O 口 E9 进行时钟输出。

18.3.3 程序设计


1. 模块框图


这里我们设计一个 5 分频的奇数分频器,模块我们取名为 divider_five。奇数分频和偶数分频在模块的设计和分析上其实是一样的,只是其内部逻辑的实现上有所不同,所以模块的输入仍然是有时钟和复位信号,输出为对输入时钟分频后的结果 clk_out。根据上面的分析设计出的 Visio 框图如图 18-16 所示。

2. 波形图绘制


方法 1 实现:仅实现分频功能

奇数分频仅实现分频功能的实现方式不像偶数分频仅实现分频功能那样直接计数就可以了,而是需要我们先思考探索一下。在波形图的设计上首先画出时钟和复位两个输入信号,然后我们可以简单的画出 5 分频的波形大概是什么样子,如果我们依然采用偶数分频的方法,可以发现计数器计数变化的位置总是对应系统时钟 sys_clk 的上升沿,所以分频后 clk_out 信号变化的位置也是对应系统时钟 sys_clk 的上升沿,这样我们最终得到的波形如图 18-17 所示,虽然也实现了奇数分频,但占空比却不是 50%,同理如图 18-18 所示,使用下降沿的效果也是一样,也就是说如果我们像之前一样只用上升沿或下降沿计数的话显然是无法实现奇数分频效果的。那怎么办呢?

于是我们先画出 5 分频的效果波形图再来分析,看看能发现什么规律。如图 18-19 所示,我们发现要实现 5 分频需要在系统时钟 sysclk 的上升沿和下降沿都工作,我们之前的例子从没有遇到在一个模块中既使用上升沿又下降沿的情况。我们尝试着把图 18-17、图 18-18、图 18-19 的 clk_out 输出信号的波形放到一起来寻找规律,变成图 18-20 所示的波形,clk1 波形的变化都是在系统时钟 sys_clk 上升沿时进行,clk2 波形的变化都是在系统时钟 sys_clk 下降沿时进行,clk_out 输出信号是我们想要的 5 分频的,红色加粗的虚线是 5 分频的变化位置,clk1 和 clk2 我们经过之前的练习可以很容易的根据 cnt 计数器的计数来产生,那 clk_out 输出信号该如何产生呢?我们仔细观察,发现 clk1 和 clk2 相与的结果就是 clkout 的波形,真是太好了!可能有同学并不能立刻想到这种方法,但是如果比较熟练或者善于观察还是很容易发现规律的。那么奇数分频的波形设计我们就算完成了,这里 clk1 和 clk2 都是低电平 2 个时钟周期,高电平 3 个时钟周期,大家可以尝试下如果低电平是三个时钟周期,高电平时 2 个时钟周期,我们的波形该怎么画。

方法 2 实现:实用的降频方法

奇数分频同样也会遇到和偶数分频相同的问题,就是我们也不能直接将奇数分频的信号作为下一级的时钟,所以我们也要使用 clk_flag 的方式实现。虽然奇数分频仅实现分频功能的实现方式和偶数分频仅实现分频功能的差异很大,但他们实用的降频方法都是相同的道理,如图 18-21 所示,在波形上除了 cnt 计数器计数的个数不同外,其他的都是一样的。

3. 代码编写


方法 1 实现:仅实现分频功能

根据上面 RTL 代码综合出的 RTL 视图如图 18-22 所示,该 RTL 视图已经比较复杂了,但是如果仔细分析还是可以进行分析的,如果系统再大一些就很难分析了,而更复杂的系统我们如果再对其内部继续和之前一样面面俱到的分析意义不是很大,因为我们使用 Verilog 硬件描述语言来描述硬件的行为就是要跳出这种对底层的复杂设计,而只关心其功能的实现,所以后面我们将主要把重点放在对行为和层次化结构的实现。但有时候在进行局部优化时我们还会进行局部的分析,而不是低效率的全局分析,但是无论怎样,通过前面的讲解大家一定要有一个硬件的思想。

方法 2 实现:实用的降频方法

根据上面 RTL 代码综合出的 RTL 视图如图 18-23 所示,该 RTL 视图的结构分析和上一章方法 2 的带标志信号的计数器的 RTL 视图分析相同,结构上也是少了最后一级寄存器,这里不再进行详细介绍。

4. 仿真验证


仿真文件编写

仿真方法 2 的 RTL 代码时,把 Testbench 中的“clk_out”全改为“clk_flag”即可。

仿真波形分析

方法 1 实现:仅实现分频功能

打开 ModelSim 执行仿真,仿真出来的波形如图 18-24 所示,我们让仿真运行了 500ns,可以发现 clk_out 信号产生了几个周期的完整波形,我们在 clk_out 相邻两个上升沿的位置处分别放置参考线并添加频率显示,我们可以看到显示的频率为 10MHz,而我们系统时钟 sys_clk 是 50MHz 的,大约为 5 分频的关系,从而验证了我们设计的是正确的。

方法 2 实现:实用的降频方法

打开 ModelSim 执行仿真,仿真出来的波形如图 18-25 所示,我们也让仿真运行了 500ns,和偶数的降频方法一样,可以发现 clk_flag 脉冲标志信号每当计数器计数到 4 时产生一个时钟周期的脉冲,我们在 clk_flag 相邻两个下升沿的位置(也是 下一级模块可以采集到 clk_flag 为高电平的位置)处分别放置参考线并添加频率显示,我们可以看到显示的频率为 10MHz,也是对 sys_clk 系统时钟的 5 分频,和我们绘制的波形图一致。

18.3.4 上板验证

1. 引脚约束


2. 结果验证


程序下载完毕后,如图 18-29 所示,使用示波器对输出 I/O 口 E9 进行频率测量,分频时钟信号频率为 10MHz,刚好为输入 50MHz 时钟信号的 5 分频,达到预期效果。

18.4 章末总结


本章主要讲解了时序逻辑电路中最常用的偶数分频和奇数分频的实现,并详细讲解了仅实现分频功能的分频器和实用的降频方法,希望大家能够理解其中的差别和产生这种用法的意义。学会一步步根据需求分析问题如何实现,要多尝试、敢尝试、多联系,学过的知识要能够用上。对于实用的降频方法除了本章讲解的以外,后面的 IP 核章节还会讲解到通过 PLL(Phase Locked Loop,即锁相环)的方法来实现对时钟的任意分频、倍频、相位移动,非常强大。

18.5 拓展训练


根据上一章中我们学习到的 parmaeter 参数化的方法将偶数分频和奇数分频都做成通用的任意分频模块,方便以后的调用。

#野火FPGA