Verilog手撕代码(6)分频器

2024-03-05 01:20
文章标签 代码 verilog 分频器

本文主要是介绍Verilog手撕代码(6)分频器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 分频概念
  • 偶数分频
    • 二分频
    • 任意偶数
    • 占空比问题
  • 奇分频
    • 非常规占空比的奇分频
  • 分频时钟的使用
  • 小数分频

分频概念

分频就是生成一个新时钟,该新时钟的频率是原有时钟频率的整数分之一倍,新周期是原有周期的整数倍。

再简单来说,让你手撕一个四分频电路,就是写代码生成一个周期是原来四倍的时钟,如果手撕一个三分频电路,就是写代码生成一个周期是原来三倍的时钟。
在这里插入图片描述
如图为四分频波形图,clk_out的频率是clk的1/4,但周期是clk的4倍。
分频主要分为偶数分频、奇数分频、小数分频。

偶数分频

二分频

二分频引入,在每个时钟上升沿来到时,翻转新时钟

always@(posedge clk or negedge rst_n)beginif(!rst_n)clk_2 <= 0;elseclk_2 <= ~clk_2;
end

得到的结果如下:
在这里插入图片描述

任意偶数

代码:

module div
#(parameter num = 8
)
(input clk,input rst_n,output reg clk_out
);reg [2:0]cnt;
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt <= 0;clk_out <= 0;endelse if(cnt == num/2 -1)begincnt <= 0;clk_out <= ~clk_out; end	elsecnt <= cnt + 1'b1;
endendmodule

Testbench

module div_tb();reg clk;reg rst_n;wire clk_out;div u0(.clk(clk),.rst_n(rst_n),.clk_out(clk_out));always #10 clk = ~clk;initial beginclk = 0;rst_n = 0;#15 rst_n = 1;#50000$stop;endendmodule

仿真结果:
在这里插入图片描述

每4各clk,clk_out翻转一次,即8个clk周期的为clk_out的一个周期。

在Testbench中使用defparam语句覆盖原始rtl中的num初始值,改为4分频。

defparam num = 4;

结果为:
在这里插入图片描述

占空比问题

上面写的任意偶数分频代码的占空比都是50%,实际上面试手撕代码不会让你50%占空比

方法:==进行计数,对于一个八分频,开始就把时钟设为高电平,我用cnt 计数到两个时钟上升沿后再把它拉低,计数到7后cnt 拉低 时钟拉高,这样就实现了两个周期高,六个周期低,占空比为2/8 即 25% 的八分频。 ==

奇分频

任意奇数分频代码:

module div_2
#(parameter N = 7
)
(input clk,input rst_n,output reg clk_out
);reg [2:0] cnt;
always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 3'd0;else cnt <= (cnt == (N-1))?3'd0:cnt + 1'd1;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)clk_out <= 1'b0;else if((cnt == N-1)|(cnt == (N-1)/2))clk_out <= ~clk_out;elseclk_out <= clk_out;
endendmodule

Testbench:

`timescale 1ns/1ns
module div_2_tb();reg clk;reg rst_n;wire clk_out;div_2 inst(.clk(clk),.rst_n(rst_n),.clk_out(clk_out));always #10 clk = ~clk;initial beginclk = 0;rst_n = 0;#15rst_n = 1;#3000$stop;end
endmodule

仿真结果:
在这里插入图片描述

在cnt = 4 和cnt = 6的时候对clk_out翻转,可以看到每经过7个clk,生成一个clk_out完整周期。但是计算clk_out的占空比可得为3/7,并不是规整的50%占空比。

实现50%占空代码:

module div_2
#(parameter N = 7
)
(input clk,input rst_n,output wire clk_out
);reg [2:0] cnt_pos;
reg [2:0] cnt_neg;
reg clk_pos,clk_neg;
always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt_pos <= 3'd0;else cnt_pos <= (cnt_pos == (N-1))?3'd0:cnt_pos + 1'd1;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)clk_pos <= 1'b0;else if((cnt_pos == N-1)|(cnt_pos == (N-1)/2))clk_pos <= ~clk_pos;elseclk_pos <= clk_pos;
endalways@(negedge clk or negedge rst_n)beginif(!rst_n)cnt_neg <= 3'd0;else cnt_neg  <= (cnt_neg  == (N-1))?3'd0:cnt_neg  + 1'd1;
endalways@(negedge clk or negedge rst_n)beginif(!rst_n)clk_neg <= 1'b0;else if((cnt_neg == N-1)|(cnt_neg == (N-1)/2))clk_neg <= ~clk_neg;elseclk_neg <= clk_neg;
endassign clk_out = clk_pos | clk_neg;endmodule

Testbench:

`timescale 1ns/1ns
module div_2_tb();reg clk;reg rst_n;wire clk_out;div_2 inst(.clk(clk),.rst_n(rst_n),.clk_out(clk_out));always #10 clk = ~clk;initial beginclk = 0;rst_n = 0;#15rst_n = 1;#3000$stop;end
endmodule

仿真结果:
在这里插入图片描述

非常规占空比的奇分频

非常规占空比的奇数分频器,比如 3/10占空比的五分频, 5/18占空比的九分频?

与上面实现50%任意奇数分频器的原理是类似的,但是采用与运算。用占空比为 2/5 上升沿采样的信号和 2/5占空比下降沿采样的信号相与。

所以相与后,占空比就为 2/5 - 1/10 = 3/10 ,示意图如下:
在这里插入图片描述

5/18占空比的九分频就是用上升沿采样的3/9占空比九分频 和下降沿采样的3/9占空比九分频相与,最后结果为3/9-1/18 = 5/18 占空比。具体修改只需要改cnt判断数值以及把clk_out 的赋值从clk_out1,clk_out2相或改成相与。

代码参考:verilog代码

分频时钟的使用

在其他模块利用分频时钟时,分频得到的时钟并未连接到FPGA内部的全局时钟树,因此使用分频时钟的模块与其他使用系统时钟clk的模块存在时钟到达时间的偏差,而在设计过程中,我们希望的使各个模块的时钟达到时间使相近的,减少时序问题。

在一些低速系统或模块中,使用分频时钟出现问题的概率较低,但在高速系统和模块中,使用分频时钟就会容易出现问题,导致各模块的时钟到达时间存在较大的偏差,为解决这个问题,分频时生成脉冲时钟。
以7分频为例:

module div_3
#(parameter N = 7
)
(input clk,input rst_n,output reg clk_flag
);reg [2:0] cnt;
always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 3'd0;else cnt <= (cnt == (N-1))?3'd0:cnt + 1'd1;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)clk_flag <= 1'b0;else if(cnt == N-2)clk_flag <= 1'b1;elseclk_flag <= 1'b0;
endendmodule

Testbench与上面一样

`timescale 1ns/1ns
module div_3_tb();reg clk;reg rst_n;wire clk_flag;div_3 inst(.clk(clk),.rst_n(rst_n),.clk_flag(clk_flag));always #10 clk = ~clk;initial beginclk = 0;rst_n = 0;#15rst_n = 1;#3000$stop;end
endmodule

仿真结果:
在这里插入图片描述
在Testbench中利用defparam对N进行修改,改为4分频

	defparam inst.N = 4;

在这里插入图片描述
由此,在利用分频后的脉冲信号clk_flag 进行判断和处理,例如:

module div_3
#(parameter N = 7
)
(input clk,input rst_n,output reg clk_out,output reg clk_flag
);reg [2:0] cnt;
always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 3'd0;else cnt <= (cnt == (N-1))?3'd0:cnt + 1'd1;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)clk_out <= 1'b0;else if((cnt == N-1)|(cnt == (N-1)/2))clk_out <= ~clk_out;elseclk_out <= clk_out;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)clk_flag <= 1'b0;else if(cnt == N-2)clk_flag <= 1'b1;elseclk_flag <= 1'b0;
end
reg [2:0] a,b;
always@(posedge clk or negedge rst_n)beginif(!rst_n)a <= 3'b0;else if(clk_flag == 1)a <= a + 1'b1;
endalways@(posedge clk_out or negedge rst_n)beginif(!rst_n)b <= 3'b0;else b <= a + 1'b1;
endendmodule

其中a计数器是由clk_flag控制的,b计数器是由clk_out控制的,而本质上clk_flag是由系统时钟clk进行控制的,因此a由系统时钟clk控制,与其他采用系统时钟clk的模块都保持着相同的时钟关系,b由clk_out控制,与系统时钟clk存在一定的偏差,因此推荐使用脉冲信号的写法。

本质上来说,脉冲信号clk_flag是降频写法,无法对占空比进行设计,clk_out是分频写法,达到的效果是一样的,但在时序问题上,脉冲信号clk_flag写法更好!

小数分频

编码小数分频,就不能看微观了,要用宏观的眼界去看,比如实现一个 17/3 分频,表达成:17 除以 3 得商为 5 余2。
那么我们就可以通过5(商)分频和7(商+余数)来实现 17/3 分频。
确定5分频和7分频的次数,设:5分频的次数为a , 7分频的次数为b,那么应该有:

 a+b=3(除数)5a+7b = 17(被除数)

得a=2,b=1,也就是说通过2次5分频和1次7分频可得到 17/3 分频。
宏观来看,总共是17个时钟周期,由三个分频器均分,那么平均每个分频器就是分到17/3了,这就是小数分频,这是一个宏观的平均概念。
代码:

module div_M_N(input clk,input rst_n,output reg clk_out
);
reg [4:0] cnt;
reg [2:0] cnt1;
reg [2:0] cnt2;always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 5'd0;else cnt <= (cnt == 5'd16)?5'd0:cnt + 1'b1;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt1 <= 3'd0;cnt2 <= 3'd0;clk_out <= 1'b0;endelse if(cnt < 5'd10)begin //cnt<5'd10中包含两个5分频。占空比为1/5if(cnt1 == 3'd4)begincnt1 <= 3'd0;clk_out <= 1'b1;endelse if(cnt1 == 3'd0)begincnt1 <= cnt1 + 1'b1;clk_out <= 1'b0;endelse begincnt1 <= cnt1 + 1'b1;end		endelse begin                   //5'd10=<cnt<=5'd17中包含一个7分频。占空比为1/5if(cnt2 == 3'd6)begincnt2 <= 3'd0;clk_out <= 1'b1;endelse if(cnt2 == 3'd0)begincnt2 <= cnt2 + 1'b1;clk_out <= 1'd0;endelse begincnt2 <= cnt2 + 1'b1;endend	
endendmodule

Testbench:

module div_M_N_tb();reg clk;reg rst_n;wire clk_out;div_M_N inst(.clk(clk),.rst_n(rst_n),.clk_out(clk_out));always #10 clk = ~clk;initial beginclk = 0;rst_n = 0;#20rst_n = 1;#5000$stop;		end
endmodule

仿真结果:
在这里插入图片描述
可以看出,两个五分频,一个七分频,每十七个周期循环一次。即每十七个周期有三个分频器,平摊下来就是 17/3 。
小数分频的缺点就是 占空比不为50%,要想实现50%占空比的小数分频,涉及很多算法,具体算法十分复杂。

可参考:任意小数分频(50%占空比)

在这里插入图片描述

8.7分频verilog代码:

module div_M_N_2(input clk,input rst_n,output clk_out
);parameter M_N = 8'd87;  //总周期
parameter c89 = 8'd24;	//8分频9分频切换点
parameter div_e = 5'd8;	//偶分频周期
parameter div_o = 5'd9;	//奇分频周期reg [7:0] cnt;
reg [3:0] cnt_8;
reg [3:0] cnt_9;
reg clk_out_r;always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 8'd0;else if(cnt == M_N - 1)cnt <= 8'd0;elsecnt <= cnt + 1'b1;
endreg div_flag;
always@(posedge clk or negedge rst_n)beginif(!rst_n)div_flag <= 1'b0;else if((cnt == (M_N -1)) | (cnt ==  (c89 - 1)))div_flag <= ~div_flag;else div_flag <= div_flag;
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_8 <= 3'd0;cnt_9 <= 4'd0;endelse if(div_flag == 0)begincnt_8 <= (cnt_8 == (div_e - 1))?0:cnt_8 + 1'b1;endelse if(div_flag == 1)begincnt_9 <= (cnt_9 == (div_o - 1))?0:cnt_9 + 1'b1;end
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)	clk_out_r <= 1'b0;else if( ((cnt_8 == 4 | cnt_8 == 0)&&(div_flag == 0)) | ( (cnt_9 == 0 | cnt_9 == 4)&&(div_flag == 1 )) )clk_out_r <= ~clk_out_r;
endassign clk_out = clk_out_r;endmodule	

Testbeench:

module div_M_N_tb();reg clk;reg rst_n;wire clk_out;div_M_N_2 inst(.clk(clk),.rst_n(rst_n),.clk_out(clk_out));always #10 clk = ~clk;initial beginclk = 0;rst_n = 0;#20rst_n = 1;#5000$stop;		end
endmodule

仿真结果:

在这里插入图片描述

这篇关于Verilog手撕代码(6)分频器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/m0_51965113/article/details/131034027
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/774872

相关文章

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

HTML5实现的移动端购物车自动结算功能示例代码

《HTML5实现的移动端购物车自动结算功能示例代码》本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用... 目录1. 移动端购物车自动结算概述2. 数据存储与状态保存机制2.1 浏览器端的数据存储方式2.1.

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

Python如何去除图片干扰代码示例

《Python如何去除图片干扰代码示例》图片降噪是一个广泛应用于图像处理的技术,可以提高图像质量和相关应用的效果,:本文主要介绍Python如何去除图片干扰的相关资料,文中通过代码介绍的非常详细,... 目录一、噪声去除1. 高斯噪声(像素值正态分布扰动)2. 椒盐噪声(随机黑白像素点)3. 复杂噪声(如伪

Java Spring ApplicationEvent 代码示例解析

《JavaSpringApplicationEvent代码示例解析》本文解析了Spring事件机制,涵盖核心概念(发布-订阅/观察者模式)、代码实现(事件定义、发布、监听)及高级应用(异步处理、... 目录一、Spring 事件机制核心概念1. 事件驱动架构模型2. 核心组件二、代码示例解析1. 事件定义

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部