分频器简介
实现分频一般有两种方法,一种方法是直接使用 PLL 进行分频,比如在 FPGA 或者 ASIC 设计中,都可以直接使用 PLL 进行分频。但是这种分频有时候受限于 PLL 本身的特性,无法得到频率很低的时钟信号,比如输入 100Mhz 时钟,很多PLL 都无法得到 1Mhz 以下的时钟信号。另外一种方法是直接使用 Verilog 代码来实现分频。
注意:
使用 Verilog 代码分频得到的时钟信号尽量不要当做其他模块的输入时钟信号,因为通过 Verilog 代码分频得到的时钟信号默认不会连接到 FPGA 的时钟网络上,这样会导致时钟出现偏移和抖动,在高频电路中会影响电路稳定性,这种分频方式一般用于产生外部低速总线的参考时钟,如SPI、I2C的参考时钟。
偶数分频器和奇数分频器
根据分频器的分频比例是偶数还是奇数,可以将其分为偶数分频器和奇数分频器。
- 偶数分频:就是分频前的频率和分频后的频率比值是偶数,比如一个 50Mhz 的晶振时钟,进行二分频后,就是 50Mhz/2=25Mhz
- 奇数分频:就是分配前的频率和分频后的频率比值是奇数。比如一个 50Mhz 的晶振时钟,进行三分频后,就是 50Mhz/3=16.667Mhz
偶数分频实现
假设 N(N为偶数)分频,只需计数到 N/2-1,然后时钟翻转、计数器清零,如此循环就可以得到 N 分频。举个例子,比如晶振时钟是 100Mhz 时钟,想得到一个 25Mhz 的时钟,那么这个是一个 100/25=4 的四分频设计,按照我们刚说的计数到 4/2-1=1,然后时钟翻转、计数器清零,就可以得到一个 25Mhz 的时钟。
偶数分频 Verilog 代码
`timescale 1ns / 1ps
module divider4(
input sys_clk,
input sys_rst_n,
output reg out_clk
);
//分频计数器
reg [2:0] count;
//分频计数器,按分频比/2-1进行计数
always @(posedge sys_clk) begin
if(!sys_rst_n)
count <= 2'd0;
else if(count < 2'd1)
count <= count + 2'd1;
else
count <= 2'd0;
end
//翻转输出时钟
always @(posedge sys_clk) begin
if(!sys_rst_n)
out_clk<= 1'b0;
else if(count == 2'd1)
out_clk <= ~out_clk;
end
endmodule
奇数分频器实现
同样假设 N(N为奇数)分频,计数器需要计数到 N-1,当计数器为0时输出时钟1在输入时钟的上升沿拉低,当计数器计数到 N/2 取整时输出时钟1在输入时钟的上升沿进行拉高,同时当计数器为0时输出时钟2在输入时钟的下降沿拉低,当计数器计数到 N/2 取整时输出时钟2在输入时钟的下降沿进行拉高,将输出时钟1和输出时钟2相与即可得到真正的输出时钟。
奇数分频 Verilog 代码
`timescale 1ns / 1ps
module divider5(
input sys_clk,
input sys_rst_n,
output out_clk
);
//分频计数器
reg [2:0] count;
//上升沿跳变的中间时钟
reg out_clk1;
//下降沿跳变的中间时钟
reg out_clk2;
//分频计数器,按分频比-1进行计数
always @(posedge sys_clk) begin
if(!sys_rst_n)
count <= 3'd0;
else if(count < 3'd4)
count <= count + 3'd1;
else
count <= 3'd0;
end
//输出时钟1
always @(posedge sys_clk) begin
if(!sys_rst_n)
out_clk1 <= 1'b0;
else if(count == 3'd0)
out_clk1 <= 1'b0;
else if(count == 3'd2)
out_clk1 <= 1'b1;
end
//输出时钟2
always @(negedge sys_clk) begin
if(!sys_rst_n)
out_clk2 <= 1'b0;
else if(count == 3'd0)
out_clk2 <= 1'b0;
else if(count == 3'd2)
out_clk2 <= 1'b1;
end
//输出分频后的时钟
assign out_clk = out_clk1 & out_clk2;
endmodule
任意分频器实现
在实现任意分频器时可以利用条件生成语句,当模块例化时传入的参数为偶数则生成偶数分频的代码,否则生成奇数分频的代码,有关生成语句相关的内容参考03 Verilog HDL 语法。
任意分频 Verilog 代码
`timescale 1ns / 1ps
module divider #(
//参数列表
parameter COUNT_WIDTH = 3, //内部分频计数器宽度
parameter DIV = 5 //分频系数
)
(
input sys_clk,
input sys_rst_n,
output reg out_clk
);
//分频计数器
reg [COUNT_WIDTH-1:0] count;
generate
if((DIV % 2) == 0) begin
//偶数分频
//按分频比/2-1进行计数
always @(posedge sys_clk) begin
if(!sys_rst_n)
count <= 0;
else if(count < (DIV / 2 -1))
count <= count + 1;
else
count <= 0;
end
//翻转输出时钟
always @(posedge sys_clk) begin
if(!sys_rst_n)
out_clk <= 1'b0;
else if(count == (DIV / 2 -1))
out_clk <= ~out_clk;
end
end
else begin
//奇数分频
//上升沿跳变的中间时钟
reg out_clk1;
//下降沿跳变的中间时钟
reg out_clk2;
//按分频比-1进行计数
always @(posedge sys_clk) begin
if(!sys_rst_n)
count <= 0;
else if(count < (DIV -1))
count <= count + 1;
else
count <= 0;
end
//输出时钟1
always @(posedge sys_clk) begin
if(!sys_rst_n)
out_clk1 <= 1'b0;
else if(count == 0)
out_clk1 <= 1'b0;
else if(count == (DIV / 2))
out_clk1 <= 1'b1;
end
//输出时钟2
always @(negedge sys_clk) begin
if(!sys_rst_n)
out_clk2 <= 1'b0;
else if(count == 0)
out_clk2 <= 1'b0;
else if(count == (DIV / 2))
out_clk2 <= 1'b1;
end
//输出分频后的时钟
always @(*) begin
out_clk = out_clk1 & out_clk2;
end
end
endgenerate
endmodule