[FPGA 学习记录] 分频器---奇分频

分频器---奇分频


那么在上一章节当中,我们对分频器的相关理论知识做了一个系统性的讲解,那么重点讲解了我们的偶数分频,并且通过实验工程使用分频和降频两种方法,设计并实现了将系统时钟进行六分频的偶数分频电路,而且通过了上板验证。那么既然有偶数分频就一定存在奇数分频,那么本章节的主要内容就针对我们的奇数分频。

在分频功能的实现方式上,我们的奇数分频和偶数分频有很大的差别。那么偶数分频使用计数器计数就可以实现,而我们的奇数分频相对于偶数分频要更复杂一些。

那么在上一章节当中已经对分频器的相关理论知识做了系统性的讲解,那么这儿就不再进行理论知识的讲解,我们直接开始实战演练。

1 实战演练

1.2 设计规划

1.2.1 实验目标

在实战演练部分我们使用实验工程,采用与六分频相同的方法就是:降频和分频的方法,设计并实现一个对系统时钟进行五分频的奇数分频电路。

1.2.2 硬件资源

同样的使用我们的扩展 I/O 口,输出我们生成的分频时钟,使用逻辑分析仪对它进行测量,来验证我们的实验工程。

1.3 程序设计

实验目标和验证方法了解了之后,我们开始程序的设计。

首先是搭建文件体系

20231101022948_Gp4hPUqBb5

然后打开 doc 文件夹,新建一个 Visio 文件,用来绘制模块框图和我们的波形图

20231101023147_eMu80StaQJ

1.3.1 模块框图

那么首先开始模块框图的设计。那么模块框图的设计可以参照我们的六分频。那么输入信号和输出信号与我们六分频是一样的,那么输入信号只有时钟信号和我们的复位信号,那么输出信号是连接到我们开发板的扩展 I/O 口,方便我们的测量

20231101023504_suY5xVRXbg

模块框图绘制完成,接下来开始波形图的绘制。

1.3.2 波形绘制

我们刚才已经提到了:我们将采用与六分频相同的方法来实现我们的分频器。第一种方法就是分频的方法,第二种方法是降频的方法。首先是分频的方法。

我们先来绘制输入信号的波形

20231101023743_X3dUC9IHEY

那么输入信号的波形绘制完成。

我们先来看一下我们六分频它的波形是怎么绘制的。那么使用分频的方法来实现的六分频,它的波形是这样的

image-20231101025236931

计数器初值为 0 从 0 开始计数,每个时钟周期自加一,计数到最大值是 2 计数到最大值波形进行反转一次,就生成了我们输出的时钟信号。

但是我们的五分频不太适合这种方法,因为我们不可能计数到 2.5 对它进行一个反转;所以说,我们这儿的计数值最大值应该计数到 4,就是 0~4 计数五次,刚好对应我们的五分频;那么计数器它的初值同样为 0 那么在时钟的上升沿进行计数,那么下一个就是 1 那么后面就是 2、3 那么它计数到最大值是 4 那么 0~4 是 5 次计数,刚好与五分频对应

20231101025956_7cZNzl3xJS

那么计数器的波形绘制完成,那么接下来开始我们输出波形的绘制。那么输出信号的波形如何绘制呢?

我们先来尝试一下。首先给它一个初值为 0 当我们的计数器处于 0~2 的计数范围时让它保持低电平,那么计数到 2 时把它拉高为高电平,然后让它保持这个高电平;当计数到 4 的时候再把高电平拉低,然后在下一个循环 0~2 的计数范围内还是让它保持低电平。这样我们尝试着绘制了一下输出信号的波形

20231101030434_CdmXjuk4Gt

那么这样看似是分频,但是它的占空比并不是 50%。

那么通过观察我们发现,输出信号我们采用的是上升沿采样。那如果我们使用下降沿采样呢?我们来试一下:初值仍为 0 使用下降沿采样,那么计数到 2 时,然后在下降沿将它变为高电平,然后让它保持高电平;当我们计数值为 4 且在时钟的下降沿,把它拉低;这儿添加一条参考线

20231101030956_R24aP6SWUH

那么这儿我们采用下降沿采样的方式,尝试着绘制了我们的输出信号的波形。

但是通过观察我们发现:它的占空比也不是 50% 其实它可以看作是第一次绘制波形,向左平移了半个时钟周期。这也不是我们想要的输出信号的波形。那么输出信号的波形应该是什么样子呢?

它应该是这个样子

image-20231101031720000

初值还是低电平,那么在 ❶ 这个位置把它拉高,然后保持一段时间的高电平;到 ❷ 这个位置给它拉低,然后保持它的低电平;到 ❸ 这个位置给它拉高。那么这个波形才是我们想要的输出信号的一个波形,那么这个波形应该怎么得到呢?

通过观察我们发现:如果说我们前面绘制的两路输出信号的波形,在组合逻辑下进行一个或运算就可以得到这个波形。0 与 0 或运算结果是 0,那么 0 与 1 或运算还是 1。刚好能够实现一个占空比为 50% 的五分频信号。

那 ❶ 到 ❸ 是它的一个时钟周期,刚好对应我们五个系统时钟的周期,那么这个波形就实现了我们系统时钟的五分频。

我们来把它整理一下

20231101034255_M0bjSdShx5

那么刚刚绘制的这两路波形,就可以作为中间变量。

1.4 代码编写

然后编写我们的代码

DCwEUVcWjY

然后参照我们的波形图来进行代码的编写。模块开始、模块名称,然后是端口列表,然后是我们的模块结束

QSKMbbWmGs

然后输入时钟信号,然后是我们的复位信号,然后是输出信号

lbbkPUe6w6

那么端口列表编写完成,下面声明我们的变量。

第一个变量是我们的计数器 cnt 那么 0~4 计数需要 3 个位宽就是 [2:0];然后是我们的 clk_1、我们的 clk_2

5heoulTfsk

那么接下来开始变量的赋值,我们同样使用 always 语句,我们使用异步复位;当我们的复位信号有效时也就是低电平时,我们的计数器给它一个初值 0;然后当它计数到最大值 4 让它归零;当我们的复位信号无效时,就是复位信号为高电平无效时,我们的计数器并没有计数到最大值,没有计数到最大值就是 0、1、2、3 的时候,让它进行自加一

acm87TrWae

那么下面开始 clk_1 变量的赋值,同样的我们使用 always 语句,使用的是异步复位;然后在复位信号有效时,给它一个初值是低电平;然后当计数器计数到 2 时把它拉高;当计数器计数到最大值 4 的时候,再把它拉低;当我们的复位信号无效时为高电平,那么计数器不是 2 也不是 4 的时候,让它保持原来的电平

KLcPEdIMOr

那么这样,变量 clk_1 已经赋值完成。下面开始变量 clk_2 的赋值。我们的变量 clk_2 同样使用 always 语句进行赋值,但是这儿有一点要注意:我们的 clk_2 它使用的是时钟信号的下降沿,那么敏感列表要做一下修改;当我们的复位信号有效时,它的初值为 0;在我们时钟信号的下降沿,计数器计数到 2 时把低电平拉高;在时钟的下降沿,计数器计数到最大值 4 把高电平拉低;其他时刻保持电平

i2c35BblHx

那么这样,两个变量 clk_1clk_2 它们的代码编写完成。

下面开始输出信号代码的编写。我们的输出信号使用组合逻辑进行赋值,这样就不会延迟一个时钟周期。我们使用 assign 语句。我们的输出信号是由两个信号 clk_1clk_2 它们取或运算得到的

t52inEhg0D

代码编写完成,我们保存

divider_five.v

module divider_five
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    
    output  reg         clk_out
);

reg [2:0]   cnt;
reg         clk_1;
reg         clk_2;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt <= 1'b0;
    else if (cnt == 3'd4)
        cnt <= 1'b0;
    else
        cnt <= cnt + 3'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_1 <= 1'b0;
    else if (cnt == 3'd2)
        clk_1 <= 1'b1;
    else if (cnt == 3'd4)
        clk_1 <= 1'b0;
    else
        clk_1 <= clk_1;

always@(negedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_2 <= 1'b0;
    else if (cnt == 3'd2)
        clk_2 <= 1'b1;
    else if (cnt == 3'd4)
        clk_2 <= 1'b0;
    else
        clk_2 <= clk_2;

assign clk_out = clk_1 | clk_2;

endmodule

1.5 代码编译

下面对代码进行编译,检验我们的语法错误。

我们回到桌面,建立实验工程

YLrt6leGM7

然后添加我们的代码,然后进行一次全编译;出现了报错信息,更正代码后重新编译,编译完成,点击 OK

20231101041414_poOrajbhZY

divider_five.v

module divider_five
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    
    output  wire        clk_out
);

reg [2:0]   cnt;
reg         clk_1;
reg         clk_2;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt <= 1'b0;
    else if (cnt == 3'd4)
        cnt <= 1'b0;
    else
        cnt <= cnt + 3'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_1 <= 1'b0;
    else if (cnt == 3'd2)
        clk_1 <= 1'b1;
    else if (cnt == 3'd4)
        clk_1 <= 1'b0;
    else
        clk_1 <= clk_1;

always@(negedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_2 <= 1'b0;
    else if (cnt == 3'd2)
        clk_2 <= 1'b1;
    else if (cnt == 3'd4)
        clk_2 <= 1'b0;
    else
        clk_2 <= clk_2;

assign clk_out = clk_1 | clk_2;

endmodule

查看一下 RTL 视图

image-20231101041835146

这个就是我们的 RTL 代码综合出来的 RTL 视图。那么这个 RTL 视图已经比较复杂了,但是如果仔细分析还是可以进行分析的;如果说我们的系统再大一些就很难分析了,更复杂的系统我们如果再对其内部继续和之前一样的面面俱到的分析,意义不是很大;因为我们使用 Verilog 硬件描述语言来描述硬件的行为,目的就是要跳出这种最底层的复杂设计,只是关心功能的实现;那么所以说后面我们将会把重点放在对行为和层次化结构的实现上,但有时候在进行局部优化时我们还会进行局部的分析,不会采用这种低效率的全局分析;所以这儿 RTL 视图不再进行分析了,我们只是看一下。

那编译通过之后下面开始仿真验证。

1.6 逻辑仿真

那么仿真文件我们可以直接使用六分频的仿真文件,但是这儿需要进行一些修改:首先是改一下名称,然后打开它

20231101042038_Xzwy6czVvW

改一下模块内部的名称,还有实例化的名称和模块的名称,那么输出信号名称也需要做一些修改,因为我们这儿的输出端口是 clk_out;选中它,使用快捷键 Ctrl+H 在这儿输入 clk_out 全部替换

20231101042331_Iz7JFUW6lG

那么修改完成之后保存

tb_divider_five.v

`timescale 1ns/1ns

module tb_divider_five();

reg     sys_clk;
reg     sys_rst_n;

wire    clk_out;

initial
    begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end

always #10 sys_clk = ~sys_clk;

divider_five divider_five_inst
(
    .sys_clk  (sys_clk),
    .sys_rst_n(sys_rst_n),
    
    .clk_out  (clk_out)
);

endmodule

回到我们的实验工程,加载我们的仿真文件,然后进行仿真设置

20231101042547_iQAYGq83cZ

然后点击 RTL Simulation 这个位置开始仿真

20231101043012_ySzZlaGAeE

那么仿真编译完成,打开 sim 窗口添加我们的模块波形;全选、分组、消除前缀,然后选择 Restart 那么这儿的时间参数设置为 500ns 运行一次、全局视图;我们调整一下波形的位置,方便我们的查看

20231101043315_4j6sJotZn7

我们参照着绘制的波形图来看一下我们的仿真波形图。

首先是计数器:那么计数器初值为 0 每个时钟周期加一 1、2、3、4 每个时钟周期加一;计数到最大值归零,然后开始下一个循环的计数

image-20231101043947434

那么计数器是没有问题的。

然后看一下变量 clk_1,在计数到 2 时拉高;然后计数到 4 的时候拉低,对应时钟的上升沿,没有问题

image-20231101044529761

看一下变量 clk_2,初值为 0;那么在时钟的下降沿,计数器计数到 2 时拉高;然后在时钟的下降沿,计数器计数到 4 的时候拉低

image-20231101044943012

然后看一下输出信号。首先使用两个参考线看一下它的频率,它的频率是 10MHz 刚好是 50MHz 的五分频;输出时钟信号是由两个时钟变量取或运算得到的,而且使用的是组合逻辑

image-20231101045554969

仿真波形与我们绘制的波形图是一致的,仿真验证通过。

1.7 上板验证

我们回到我们的实验工程,开始绑定我们的管脚。我们将我们的输出信号与扩展 I/O 口 F15 端口相绑定;我们的时钟输入端口是 E1;复位信号输入端口是 M15

image-20231101045844072

那么引脚绑定完成之后回到实验工程,进行一次全编译,编译完成,点击 OK

20231101045930_5K3OTEhzfP

那么如下图所示连接下载器、电源,下载器的另一端连接我们的电脑,给开发板上电

上板验证前的硬件连线

回到我们的实验工程,点击 Programmer 这个位置,打开我们的下载界面;添加我们的 SOF 文件,点击开始,进行程序的下载,那么程序下载成功

20231101051004_1uagakxthJ

将逻辑分析仪的通道0——CH0和征途Pro开发板上扩展IO口的F15引脚相连接,存储深度设置为 200KSa、采样率设置为 100MHz,执行单次采样得到的结果是

image-20231101051337469

输出的时钟分频信号的时钟频率是 10MHz 刚好是系统时钟的五分频。那么这样上板验证正确,验证通过。

1.8 使用降频的方法实现五分频

回到我们的 Visio 文件。我们可以参照一下六分频的波形,使用降频的方法实验六分频它的波形如图所示

image-20231101051950701

它计数的最大值是 5,如果使用降频的方法实现五分频,这儿计数的最大值应该是 4 所以说我们只需要简单的修改就可以使用

20231101052224_lBXw6mzUvs

那么这样波形图绘制完成,我们参照波形图修改一下我们的代码。

我们的输出信号重新命名,这儿使用 reg 型;输出信号初值为低电平,也就是 0;然后当计数器计数到最大值减一的时候,拉高一个时钟周期的高电平;那么其他时刻让它保持低电平

MrqlvgG3tk

这样代码修改完成,保存

divider_five.v

module divider_five
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    
    // output  wire        clk_out
    output  reg         clk_flag
);

reg [2:0]   cnt;
/* reg         clk_1;
reg         clk_2;
 */

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt <= 1'b0;
    else if (cnt == 3'd4)
        cnt <= 1'b0;
    else
        cnt <= cnt + 3'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_flag <= 1'b0;
    else if (cnt == 3'd3)
        clk_flag <= 1'b1;
    else
        clk_flag <= 1'b0;

/* always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_1 <= 1'b0;
    else if (cnt == 3'd2)
        clk_1 <= 1'b1;
    else if (cnt == 3'd4)
        clk_1 <= 1'b0;
    else
        clk_1 <= clk_1;

always@(negedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        clk_2 <= 1'b0;
    else if (cnt == 3'd2)
        clk_2 <= 1'b1;
    else if (cnt == 3'd4)
        clk_2 <= 1'b0;
    else
        clk_2 <= clk_2;

assign clk_out = clk_1 | clk_2;
 */
endmodule

回到我们的实验工程,重新进行编译;那么编译完成,点击 OK

20231101052942_n4wlM4167I

然后看一下 RTL 视图

image-20231101053028073

那么这儿生成的 RTL 视图与我们六分频采用降频的方式生成的 RTL 视图,是一样的。

下面进行代码的仿真验证。修改一下仿真代码

titFo61vbX

然后保存

tb_divider_five.v

`timescale 1ns/1ns

module tb_divider_five();

reg     sys_clk;
reg     sys_rst_n;

wire    clk_flag;

initial
    begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end

always #10 sys_clk = ~sys_clk;

divider_five divider_five_inst
(
    .sys_clk  (sys_clk),
    .sys_rst_n(sys_rst_n),
    
    .clk_flag  (clk_flag)
);

endmodule

回到我们的 ModelSim 全选,然后删除所有的波形;回到我们的 Library 对修改的两个代码进行重编译,那么这儿显示重编译完成,我们回到 sim 重新添加我们的模块波形;然后我们点击一下 Restart 清除所有的波形;然后运行 500ns

20231101053538_L3UD7jSgiI

下面参照我们绘制的波形图,来看一下我们的仿真波形图

image-20231101054540551

那么计数器部分,初值为 0 最大值为 4 计到最大值归零,开始下一循环计数;那么时钟频率也是 10MHz 刚好是系统时钟的五分频。

仿真验证通过,回到我们的实验工程,然后重新绑定引脚,因为输出信号的名称已经改变了。

选择我们的 clk_out 使用键盘上的 delete 进行删除,点击 OK、Yes 然后这儿重新绑定 F15 端口

20231101054750_fycyw47mYJ

然后重新进行一个全编译,编译完成之后点击 OK

20231101054838_FJHQW8CI8l

下载我们的程序

20231101054929_PUvkCftO25

它的频率也是 10MHz

image-20231101055042490

那么上板验证完成,验证通过。

那么以上就是本章节的全部内容。经过本章节与上一章节的讲解,我们了解了时序逻辑电路当中最常用的偶数分频和奇数分频的实现方法,而且详细讲解了,仅实现分频功能的分频器和实用的降频方法。希望大家能够理解这两种方法的差别和产生这种用法的意义,那么在后面的章节我们还会讲到,通过 PLL(锁相环)实现时钟的任意分频、倍频、相位移动,它的功能是非常强大的。


参考资料:

12. 分频器
18-第十五讲-分频器—奇分频

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值