前言
FPGA开发板上一般只有一个晶振,即一种时钟频率。数字系统设计中,时间的计算都要以时钟作为基本单元,对基准时钟进行不同倍数的分频而得到各模块所需时钟频率,可通过Verilog代码实现;倍频可通过锁相环【PLL】实现。
分频原理
把输入信号的频率变成成倍的低于输入频率的输出信号;每经历几个单位时钟周期就输出一个时钟周期。
例:clk_in
为12MHz;clk_out
为2MHz.则分频系数为6
。那么如何实现6
分频:
把输入信号作为计数脉冲(边沿触发一次,计数一次),
当计数6次,为输出信号的一个周期,此时输入信号经历了6个周期,置0,重新开始计数,循环往复。(前提:输入信号占空比50%)
而输出信号的不同占空比的实现无非是输出信号经历的单位周期输出高低电平不同;
偶数分频
6分频
占空比50%:6分频电路:一个
always
块实现一个模3计数器。
另一个always
块实现当计数到2(从0计数到2,计数,3次)输出信号翻转,
占空比50%。
占空比不是50%:先实现模6计数器,然后可任意设计当计数到某个值输出高电平,再计数到另一个值输出低电平。
代码
//不带标志位占空比50%
module divider_six(clk,reset,clk_out,cnt);
input clk;
input reset;
output reg clk_out;
output reg [1:0] cnt;//用于计数的寄存器; always块内,reg型变量
always@(posedge clk)
if(reset == 1'b0)
cnt <= 2'd0;
else if(cnt == 2'd2)//从0开始,计数到2,计数为3,则6分频
cnt <= 2'd0;
else
cnt <= cnt + 1'b1;
//计数3次,输出翻转;所以cnt计数3次作为标志,实现翻转功能
always@(posedge clk)
if(reset == 1'b0)
clk_out <= 1'b0;
else if(cnt == 2'd2)
clk_out <= ~clk_out;
else;
endmodule
tb
`timescale 1ns/100ps
`define clk_cycle 50
module divider_six_tb;
reg clk;
reg reset;
wire clk_out;
wire [1:0]cnt;
always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
initial begin
clk = 0;
reset = 1;
#10 reset = 0;
#110 reset = 1;
#100000
$stop;
end
divider_six u1(
.reset(reset),
.clk(clk),
.clk_out(clk_out),
.cnt(cnt)
);
endmodule
仿真波形
奇数分频
算法
:需要在系统时钟为上升沿和下降沿都工作(不是同时工作)
产生50%的占空比,需要设计两个分频时钟跳变点,分频周期的N-1
和(N-1)/2
。
例:13分频
:两个分频时钟跳变点:6[(13-1)/2]和12[13-1],此时输出保持7个高电平时钟周期,6个低电平时钟周期分别在上升沿触发clk1
和下降沿触发clk2
,两者相位相差半个单位周期,clk_out = clk1 & clk2;
之后clk_out
占空比50%
如图仿真波形:
仿真波形
代码
//13分频,占空比为50%
module div_13(clk,reset,clk_out,clk1,clk2);
input clk;
input reset;
output clk_out;
reg [3:0] cnt;
output reg clk1;
output reg clk2;
//计数从0到12
always@(posedge clk)
if(reset == 1'b0 || cnt == 4'd12)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
//实现分频,clk1:7个高电平时钟周期,6个低电平时钟周期,上升沿触发
always@(posedge clk)
if(reset == 1'b0)
clk1 <= 1'b0;
else if(cnt == 4'd6)//复位后,计数到6,开始低电平,保持6个周期,
clk1 <= 1'b0; //此时计数到12,重新计数,保持高电平直至计数到6
else if(cnt == 4'd12)//计数到6和12,这两个是分频跳变点
clk1 <= 1'b1; //如何得到?N-1;(N-1)/2
//clk2:下降沿触发,7个高电平时钟周期,6个低电平时钟周期
always@(negedge clk)
if(reset == 1'b0)
clk2 <= 1'b0;
else if(cnt == 4'd6)
clk2 <= 1'b0;
else if(cnt == 4'd12)
clk2 <= 1'b1;
//clk1,clk2两者相差半个时钟周期
//clk1,clk2配合作用,都计数7次后,clk1立即回到低电平,而clk2延迟半个周期
assign clk_out = clk1 & clk2;
endmodule
tb
`timescale 1ns/100ps
`define clk_cycle 50
module div_13_tb;
reg clk;
reg reset;
wire clk_out;
wire clk1;
wire clk2;
always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
initial begin
clk = 0;
reset = 1;
#10 reset = 0;
#110 reset = 1;
#100000
$stop;
end
div_13 u1(
.reset(reset),
.clk(clk),
.clk_out(clk_out),
.clk1(clk1),
.clk2(clk2)
);
endmodule
小数分频
说明
1.等效意义上的小数分频:例5.3分频,没有周期为5.3的分频电路,实际是输出时钟53的周期对应于输出时钟的10个周期。
2.按序输出:时钟用处不大,没有意义
3.插入乱序输出:防止相位抖动,把多出来的脉冲均匀分散开
半整数分频:N+0.5
思路
:例:5.5分频
原时钟的半周期为单位,此时周期为11可分频输出6高5低。
上升沿触发,使得clk1
前6周期输出高电平【N-1=10为一个跳变点】后5周期输出低电平【N-1)/2=5为另一个跳变点】;
下降沿触发使得clk2
前5周期为低电平(cnt为0时的跳变点),后6【cnt=N-1)/2=5】周期为高电平。两个信号相与即为分频信号clk_div = clk1 & clk2;
,类似奇分频
仿真波形
代码
//5.5分频
//原时钟周期一半为单位,输出1高10低,从0到10计数,前6周期高电平,后5周期低电平;
//再使用下降沿触发前5周期低电平,前6周期高电平,最后相与
module div_half_1(clk,rst_n,clk_div,clk1,clk2,cnt);
input clk;
input rst_n;
output clk_div;
output reg[3:0] cnt;
output reg clk1;
output reg clk2;
//从0到10计数 模11
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 4'd0;
else if (cnt==4'd10)
cnt <= 0;
else
cnt <= cnt + 1'b1;
always@(posedge clk or negedge rst_n)
if(!rst_n)
clk1 <= 1'b0;
else if(cnt==4'd10)//6个周期的高电平N-1=10
clk1 <= 1'b1;
else if(cnt==4'd5)//5个周期的低电平(N-1)/2=5
clk1 <= 1'b0; //两个跳变点,(N-1)/2;N-1
//clk2相比于clk1延时半个时钟周期,
always@(negedge clk or negedge rst_n)
if(!rst_n)
clk2 <= 1'b0;
else if(cnt==4'd0)//5个周期的低电平
clk2 <= 1'b0;
else if(cnt==4'd5)//6个周期的高电平
clk2 <= 1'b1;
assign clk_div = clk1 & clk2;
endmodule
tb
`timescale 1ns/100ps
`define clk_cycle 50
module div_half_1_tb;
reg clk;
reg reset;
wire clk_out;
wire clk1;
wire clk2;
wire[3:0] cnt;
always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
initial begin
clk = 0;
reset = 1;
#10 reset = 0;
#110 reset = 1;
#100000
$stop;
end
div_half_1 u1(
.rst_n(reset),
.clk(clk),
.clk_div(clk_out),
.clk1(clk1),
.clk2(clk2),
.cnt(cnt)
);
endmodule
小数分频
5.3分频
53/10=5.3 设5分频a次;6分频次
a+b=10;
5a+6b=53 ->a=7 ; b=3
如何插入排序:6556556555
Verilog实现:
脉冲删除:53个单位周期删除43个算法
:分母作为累加值,在clk
的上升沿cnt
加上分母10,判断是否大于分子,若小于(脉冲删除标志为1,删除)继续累加,若大于(脉冲删除标志为0,不删除)在下一周期减去分子。结果刚好是6556556555(由来:分母-余数=7,作为累加值,大于分母,输出N分频,然后减去分母,继续累加7,;小于分母,输出N+1分频)
代码
//脉冲删除小数分频
module div_5_3_1(clk,rst_n,clk_out,cnt);
input clk;
input rst_n;
output clk_out;
parameter fra = 6'd53;//分子
parameter den = 6'd10;//分母
output reg[5:0] cnt;
reg flag_del;//脉冲删除标志位
always@(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 6'd0;
flag_del <= 1'b0;
end
//大于分子,不删除
else if(cnt>fra) begin
cnt <= cnt + den - fra;
flag_del <= 1'b0;
end
//小于分子,累加;删除:保持高电平
else begin
cnt <= cnt + den;
flag_del <= 1'b1;
end
end
assign clk_out = flag_del ? 1 : clk;
endmodule
tb
`timescale 1ns/100ps
`define clk_cycle 50
module div_5_3_1_tb;
reg clk;
reg reset;
wire clk_out;
wire [5:0]cnt;
always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
initial begin
clk = 0;
reset = 0;
#10 reset = 0;
#110 reset = 1;
#100000
$stop;
end
div_5_3_1 u1 (
.clk(clk),
.rst_n(reset),
.clk_out(clk_out),
.cnt(cnt)
);
endmodule
仿真波形
最后
不足之处:未实现参数化编程;
参考:文章来源:https://www.toymoban.com/news/detail-430275.html
https://blog.csdn.net/weixin_43698385/article/details/122773225
https://blog.csdn.net/Reborn_Lee/article/details/97553078
https://blog.csdn.net/wangyanchao151/article/details/81204126?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%B0%8F%E6%95%B0%E5%88%86%E9%A2%91&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-5-81204126.nonecase&spm=1018.2226.3001.4187文章来源地址https://www.toymoban.com/news/detail-430275.html
到了这里,关于FPGA基础设计(二):任意分频器(奇数,偶数,小数)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!