写在前面(重要): (本次设计只适用于对占空比要求不是很严格的设计,设计出来的时钟 高低电平之比为N:N+1或者N+1:N 本人觉得这已经算是0.5分频的极限了)
设计小数分频时,假如 不使用外部资源,只是用FPGA内部逻辑资源(即只有一个输入时钟clk_in以及一些计数器和线网,不使用锁相环),最后设计出来的输出时钟clk_out 不可能达到真正的占空比为50%。
如果用锁相环倍频的话,那假如设计一个输入时钟clk_in的3.5分频。使用2倍频的话,其实就相当于设计的是clk_in 2倍频之后的7分频(即一个奇数分频);而使用4倍频的话,其实就相当于设计的是clk_in 4倍频之后的14分频(即一个偶数分频)。
这样虽然设计出来了,但是却比较浪费资源,并且也根本算不上是小数分频。
一、常规设计思路
常规设计思路如下:
假设设计一个3.5分频,我们令半个clk_in周期为一个clk,后面我们以clk为单位,而不用clk_in的一个完整周期。
3.5分频即有7个clk为它的一个完整周期,N.5分频就有2N+1个clk,那么肯定高电平时间和低电平时间是不可能一样的,具体原理如下图,这也是仿真结果图(其实就是自己不想画图):
说明一下:第一行是时钟信号clk_in; 第二行是复位信号rst_n; 第三行是clk_p,上升沿翻转时钟;
第四行是clk_n,下降沿翻转时钟; 第五行是clk_N_point5,就是输出时钟
假设从最左侧蓝线处算作初始化,(注意:初始化的时候,也就是下降沿复位的话,对clk_p和clk_n的赋值一定要相反,可以让clk_p为0,clk_n为1)
具体如何计数呢?
复位之后,在一个上升沿进行上升沿计数,计数到6清零,并且在0和4的时候进行翻转(计数到2N清零,并且在0和N+1的时候进行翻转),得到clk_p;
复位之后,在一个下降沿进行下降沿计数,计数到6清零,并且在0和3的时候进行翻转(计数到2N清零,并且在0和N的时候进行翻转),得到clk_n;
最后,对clk_p和clk_n进行与运算,得到clk_N_point5 ; clk_N_point5 = clk_p && clk_n;
仿真效果如下:
但是可以看到:输出时钟的占空比非常低,占空比为 1:2N+1,如果N更大一些,占空比会更小。
二、改进设计思路
不想画图,放出改进后的原理图:
复位之后,在一个上升沿进行上升沿计数,计数到6清零,并且在0和5的时候进行翻转,得到clk_p;
复位之后,在一个下降沿进行下降沿计数,计数到6清零,并且在1和3的时候进行翻转,得到clk_n;
最后,对clk_p和clk_n进行与运算,得到clk_N_point5 ; clk_N_point5 = clk_p && clk_n;
这是当N==3的时候,在这些地方翻转,那当N为其他值呢?经过我的带入—纸上—递归法得到:
对任意N:复位之后,在一个上升沿进行上升沿计数,计数到2N清零,并且在0和N+1+N/2的时候进行翻转,得到clk_p;
复位之后,在一个下降沿进行下降沿计数,计数到6清零,并且在N/2和N的时候进行翻转,得到clk_n。
三、仿真效果
N=3:
N=5:
N=20:
可以看出:高低电平总是只相差半个clk_in,当N越大,输出时钟的占空比也越大,越接近50%
四、源代码以及仿真文件
1、源代码
`timescale 1ns / 1ps
module divider_point5 #(parameter CLK_N = 2)
(
input clk_in,
input rst_n,
output reg clk_2in, // 2N倍频时钟
output reg clk_2in_2, // 下降沿2N倍频时钟
//output clk_en,
output clk_N_point5,
output reg clk_out
);
reg [7:0] cnt_2N; // 2N倍频时钟计数
reg [7:0] cnt_2N_2; // 下降沿2N倍频时钟计数
reg clk_out_g1;
assign clk_N_point5 = clk_2in && clk_2in_2;
// 上升沿生成2N分频时钟电路
always@(posedge clk_in or negedge rst_n) begin
if(!rst_n)
cnt_2N <= 8'b0;
else if(cnt_2N == 2*CLK_N)
cnt_2N <= 8'b0;
else
cnt_2N <= cnt_2N + 1'b1;
end
always@(posedge clk_in or negedge rst_n) begin
if(!rst_n)
clk_2in <= 1'b0;
//else if((cnt_2N == CLK_N+1) ||( cnt_2N ==0 )) begin // 这个是占空比为1:2N+1,未改进
else if((cnt_2N == CLK_N+1+CLK_N/2) ||( cnt_2N ==0 )) begin // 这个是占空比尽量靠近0.5的方式
clk_2in <= ~clk_2in;
end
else
clk_2in <= clk_2in;
end
// 下降沿生成2N分频时钟电路
always@(negedge clk_in or negedge rst_n) begin
if(!rst_n)
cnt_2N_2 <= 8'b0;
else if(cnt_2N_2 == 2*CLK_N)
cnt_2N_2 <= 8'b0;
else
cnt_2N_2 <= cnt_2N_2 + 1'b1;
end
always@(negedge clk_in or negedge rst_n) begin
if(!rst_n)
clk_2in_2 <= 1'b1;
//else if((cnt_2N_2 == CLK_N) ||( cnt_2N_2 == 0 )) begin // 这个是占空比为1:2N+1,未改进
else if((cnt_2N_2 == CLK_N) ||( cnt_2N_2 == CLK_N/2 )) begin // 这个是占空比尽量靠近0.5的方式
clk_2in_2 <= ~clk_2in_2;
end
else
clk_2in_2 <= clk_2in_2;
end
endmodule
2、仿真文件
`timescale 1ns / 1ps
`define clk_period 20
module divider_point5_tb();
parameter CLK_N = 20;
reg clk_in;
reg rst_n;
wire clk_2in;
wire clk_2in_2;
wire clk_N_point5;
initial begin
rst_n = 1;
clk_in = 0;
#2;
rst_n = 0;
#3;
rst_n = 1;
end
always #(`clk_period/2) clk_in = ~clk_in;
divider_point5 #(.CLK_N (CLK_N))
divider_point5(
.clk_in (clk_in),
.rst_n (rst_n),
.clk_2in (clk_2in),
.clk_2in_2 (clk_2in_2),
.clk_N_point5 (clk_N_point5)
);
endmodule