st_n;
wire clk_div3_1; //占空比非50%的3分频时钟信号
wire clk_div3_2; //占空比50%的3分频时钟信号
parameter DIV_CLK = 5; //定义源时钟信号一周期时间
//复位信号生成
initial begin
clk = 0; //时钟信号赋初值
rst_n = 1; //复位信号赋初值
#(3*DIV_CLK)
rst_n = 0;
#(6*DIV_CLK)
rst_n = 1;
#(20*DIV_CLK);
end
//源时钟信号生成
always #DIV_CLK clk = ~clk;
//模块例化
clk_div_odd u_clk_div_odd
(.clk (clk),
.rst_n (rst_n),
.clk_div3_1 (clk_div3_1),
.clk_div3_2 (clk_div3_2)
);
endmodule
3.5 仿真结果
四、小数分频
4.1 双模前置分频法
不规整的小数分频不能做到分频后的每个时钟周期都是源时钟周期的小数分频倍,更不能做到分频后的时钟占空比均为 50%,因为 Verilog 不能对时钟进行小数计数。
小数分频是基于可变分频和多次平均的方法实现的。
例如进行5.4倍分频,则保证源时钟54个周期的时间等于分频时 10个周期的时间即可。此时需要在54个源时钟周期内进行6次5分频,4次6分频。
T = ( Ma+(M+1)b )/ a+b,这里我们发现组成小数分频使用了a个M分频和b个M+1分频的整数分频电路。
以 5.4 倍分频为例:
基本思想是在54个源时钟周期里完成10个5.4分频,根据前面的公式可知:有6的5分频和4个6分频。只要将5分频和6分频插入在54个源时钟周期即可。
同时我们应当考虑分频信号的实现顺序
5分频和6分频的实现顺序一般有以下 4 种:
(1)先进行 6 次 5 分频,再进行 4 次 6 分频;
(2) 先进行 4 次 6分频,再进行 6 次 5 分频;
(3) 将 6 次 5 分频平均的插入到 4 次 6 分频中;
(4) 将 4 次 6 分频平均的插入到 6 次 5 分频中。
前两种方法时钟频率不均匀,相位抖动较大,所以一般会采用后两种平均插入的方法进行小数分频操作。
那又如何平均插入呢?
平均插入可以通过分频次数差累计的方法实现,5.4 分频的实现过程如下:
(1) 第一次分频次数差值 54 - 10×5 = 4 < 10,第一次进行 5 分频。
(2) 第二次差值累加结果为 4+4=8 < 10,第二次使用 5 分频,同时差值修改为(54-10×5) + (54 -10×5) = 8 。
(3) 第三次差值累加结果为 4 + 8 = 12 > 10,第三次使用 6分频。
(4) 第四次差值累加结果为 12 + (54-10×6) < 10,第四次使用 5 分频。
以此类推,完成将 6 次 5分频平均插入到 4 次 6分频的过程
具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用),此时相位抖动相对较小。
Tips:每一段并不是严格的5.4分频(因为信号翻转只在边沿触发),而是在54个源时钟周期平均下来有10个分频,而且时序难以保证。且占空比几乎达不到50%。
4.2 Verilog代码
//小数分频电路设计
//双模前置法实现5.4分频
module clk_div_fraction
(
input rst_n, //复位信号
input clk, //时钟信号
output clk_frac //小数分频输出信号
);
//定义介于5.4分频的5分频和6分频
parameter CLK_DIV_1 = 5;
parameter CLK_DIV_2 = 6;
parameter DIFF = 4; //10个周期内5分频与5.4分频的差值
reg [3:0] cnt_end; //分频插入计数器 (用于判断插入什么分频)
reg [3:0] cnt; //总计数器
reg clk_frac_r; //小数分频中间寄存器信号
reg [4:0] diff_cnt_r; //差值信号
reg [4:0] diff_cnt; //差值信号
wire diff_cnt_en= cnt == cnt_end; //使能信号
//差值累加逻辑模块
always @(*) begin
if(diff_cnt_r >= 10) begin
diff_cnt = diff_cnt_r -10 + DIFF; //差值大于10,插入6分频,差值减6
end
else begin
diff_cnt = diff_cnt_r + DIFF; //差值小于10,插入5分频,差值加4
end
end
// 借用寄存器延迟输出diff_cnt_r
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin //复位差值清零
diff_cnt_r <= 0;
end
else if(diff_cnt_en) begin //使能信号高电平时,差值信号延迟输出
diff_cnt_r <= diff_cnt;
end
end
//5分频和6分频插入逻辑模块
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_end <= CLK_DIV_1-1 ; //复位先插入5分频
end
else if(diff_cnt >= 10) begin
cnt_end <= CLK_DIV_2-1 ; //差值大于10,插入6分频
end
else begin
cnt_end <= CLK_DIV_1-1 ; //差值小于10,插入5分频
end
end
//
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin //总计数器、分频信号信号复位
cnt <= 1'b0;
clk_frac_r <= 1'b0;
end
else if(cnt == cnt_end) begin //计数器到分频插入界限点
cnt <= 1'b0; //总计数器清零
clk_frac_r <= 1'b1; //时钟分频信号电平置"1"
end
else begin //其他情况下,计数器累加计数、时钟分频信号电平保持"0"
cnt <= cnt + 1'b1;
clk_frac_r <= 1'b0;
end
end
//延时输出,消除亚稳态
assign clk_frac = clk_frac_r;
endmodule
4.3 Testbench
`timescale 1ns/1ps //时间刻度:单位1ns,精度1ps
module clk_div_fraction_tb;
//信号申明
reg clk;
reg rst_n;
wire clk_frac;
parameter DIV_CLK = 5; //定义源时钟信号一周期时间
//复位信号生成
initial begin
clk = 0; //时钟信号赋初值
rst_n = 1; //复位信号赋初值