前言
分频在数字设计中应用广泛,可利用锁相环或者计数器实现。
偶数分频
偶数分频较为简单,利用计数器计数到N-1时钟翻转即可,下面是四分频代码:
module divider_even(
input clk,
input rst_n,
output reg clk4
);
reg cnt;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 1'b0;
end
else begin
cnt <= cnt + 1'b1;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk4 <= 1'b0;
end
else if(cnt == 1'b1)
clk4 <= ~clk4;
else
clk4 <= clk4;
end
endmodule
测试代码:
module divider_even_tb;
// Inputs
reg clk;
reg rst_n;
// Outputs
wire clk4;
// Instantiate the Unit Under Test (UUT)
divider_even uut (
.clk(clk),
.rst_n(rst_n),
.clk4(clk4)
);
initial begin
// Initialize Inputs
clk = 0;
rst_n = 0;
// Wait 100 ns for global reset to finish
#100;
rst_n = 1;
// Add stimulus here
#400;
$stop;
end
always #5 clk <= ~clk;
endmodule
仿真结果:
奇数分频
利用参数化设计的方法,可以实现任意奇数分频,便于模块复用。定义参数parameter N为分频系数,以五分频为例,利用时钟上升沿和下降沿分别作为触发条件,得到两个时钟,都是高电平2个时钟低电平3个时钟,但是相差半个时钟周期,最后相或能得到50%占空比的N分频时钟。
五分频代码:
module divider_odd #(parameter N = 8'd5)(
input clk,
input rst_n,
output clk5
);
reg [7:0]cnt1,cnt2;
reg clk1,clk2;
wire flag1,flag2;
//上升沿触发
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt1 <= 8'd0;
end
else if(cnt1 == N-1)
cnt1 <= 8'd0;
else
cnt1 <= cnt1 + 8'd1;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk1 <= 1'b0;
end
else if(flag1)
clk1 <= ~clk1;
else clk1 <= clk1;
end
//下降沿触发
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt2 <= 8'd0;
end
else if(cnt2 == N-1)
cnt2 <= 8'd0;
else
cnt2 <= cnt2 + 8'd1;
end
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
clk2 <= 1'b0;
end
else if(flag2)
clk2 <= ~clk2;
else clk2 <= clk2;
end
assign flag1 = ((cnt1 == (N-1)>>1) | (cnt1 == N-1));
assign flag2 = ((cnt2 == (N-1)>>1) | (cnt2 == N-1));
assign clk5 = clk1 | clk2;
endmodule
分别在测试文件中令N为5和7,仿真结果如下:
半整数分频
整数分频无法做到50%占空比,以3.5分频为例,我们可以高电平1.5个周期,低电平2个周期。利用三个always块,一个计数模块,另外两个分别以时钟的上升沿和下降沿为触发条件,分别产生标志信号clk_1和clk_2,最后二者相或即可得到半整数分频结果。以3.5分频为例:
module divider_half(
input clk,
input rst_n,
output clk_half
);
//参数定义
parameter dou_coe = 7;
reg [7:0]cnt;
reg clk_1;
reg clk_2;
//计数模块
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 8'd0;
end
else if(cnt == dou_coe-1) begin
cnt <= 8'd0;
end
else cnt <= cnt + 1;
end
//上升沿检测
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_1 <= 1'b0;
end
else if((cnt == 8'd0) | (cnt ==((dou_coe+1)>>1))) begin
clk_1 <= 1'b1;
end
else clk_1 <= 1'b0;
end
//下降沿检测
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_2 <= 1'b0;
end
else if((cnt == 8'd1) | (cnt ==((dou_coe+1)>>1))) begin
clk_2 <= 1'b1;
end
else clk_2 <= 1'b0;
end
assign clk_half = clk_1 | clk_2;
endmodule
测试结果:
DDS任意分频
DDS,即直接数字频率合成技术。网上有说比常规的数字计数精度更高,但是我实际仿真结果却是一样的。我们以100MHz分频到115200Hz为例,常规的任意分频:
K=100_000_000/115200=868(取整)
100_000_000/868=115207,因此误差大概是7Hz
利用DDS进行任意分频器设计,首先我们一个取32位的计数器,K=115200 * 232/100_000_000=4947802.32499,取值为4947802,4947802 * 100_000_000/232=115199.9924,这个结果和115200Hz相差甚小,设计代码如下:
module divider_dds(
input clk,
input rst_n,
output reg clk_dds
);
parameter K1 = 32'd4947802;//115200,实际上是115199.99
reg [31:0]cnt;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 0;
end
else
cnt <= cnt + K1;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_dds <= 0;
end
else if(cnt > 32'h7FFF_FFFF)
clk_dds <= 1;
else
clk_dds <= 0;
end
endmodule
仿真结果如下:
不难看出,分频后的时钟周期是8680ns,频率约为115207Hz,这和常规的计数分频相比,精度上并没有提升。
ISE的工程文件:https://download.csdn.net/download/qq_39496263/85093066?spm=1001.2014.3001.5501
总结
欢迎交流进步!