前言
由于FPGA只有一个晶振产生时钟,然而实际项目中需要用到不同频率的时钟信号,这个时候就需要进行分频与倍频,产生分频方式有两种:使用PLL核和verilog代码自己实现;而倍频我们只能通过PLL核来实现;
偶分频
首先以一个六分频为例来向大家讲解。
通过Visio 2013绘制的原理图如下
假设原来的时钟是sys_clk,复位信号是sys_rst_n,计数器为cnt,六分频之后的时钟为clk_flag,则六分频就是把原来的六个时钟周期变成现在的一个时钟周期。cnt计数范围0~5,占空比是1:6
可以设clk_flag的低电平对应cnt的0~4,高电平对应5,分频之后clk_flag由低电平变为高电平的时刻是clk_flag为4时。
当我们要产生任意偶分频时,我们假设分频系数为DIV_CLK_FACTOR
,也就是我们要几分频,计数器仍然为cnt
,并且cnt
的初始值为0。基于上面的分析,可以知道,当cnt
计数到DIV_CLK_FACTOR-1
时,恰好是我们所需要的时钟的一个周期。至于clk_flag何时拉高,要根据占空比确定,当cnt计数到DIV_CLK_FACTOR*(1-占空比)-1时,将
clk_flag拉高,当cnt计数到0时,将
clk_flag拉低,之所以这样,是因为方便代码的编写,此公式适合于任何占空比。
下面通过代码分析
module divider_six
(
input wire sys_clk,
input wire sys_rst_n,
output reg clk_flag
);
reg [2:0] cnt;
//分频计数器计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd5)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
//分频
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_flag <= 1'b0;
else if(cnt == 3'd4)
clk_flag <= 1'b1;
else
clk_flag <= 1'b0;
endmodule
仿真代码:
`timescale 1ns/1ns
module tb_divider_six();
reg sys_clk;
reg sys_rst_n;
wire clk_flag;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
divider_six divider_six_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.clk_flag (clk_flag)
);
endmodule
仿真波形图如下:
偶分频还可以另外一种方法
此种方法只适合于产生占空比50%的偶分频,同样以六分频为例
通过Visio 2013绘制的原理图如下
cnt计数范围是0~DIV_CLK_FACTOR/2-1,
当计数器cnt计数到DIV_CLK_FACTOR/2-1时,clk_out电平发生翻转,此时产生的占空比就是50%
代码如下
module divider_six
(
input wire sys_clk,
input wire sys_rst_n,
output reg clc_out
);
reg [2:0] cnt;
//分频计数器计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 2'd0;
else if(cnt == 2'd2)
cnt <= 2'd0;
else
cnt <= cnt + 2'd1;
//分频
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clc_out <= 1'b0;
else if(cnt == 2'd2)
clc_out <= ~clc_out;
else
clc_out <= clc_out;
endmodule
仿真波形图如下
仿真代码如下
`timescale 1ns/1ns
module tb_divider_six();
reg sys_clk;
reg sys_rst_n;
wire clc_out;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
divider_six divider_six_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.clc_out (clc_out)
);
endmodule
奇分频
以5分频为例
占空比2:3时,原理图如下
其中sys_clk为系统时钟,cnt为计数器,clk_out为输出信号,clk_1、clk_2分别为上升沿和下降沿翻转产生的中间时钟,clk_out = (clk_1 | clk_2),因此clk_1、clk_2的电平跳变时刻需要根据clk_out来确定。
代码如下
module divider_five
(
input wire sys_clk,
input wire sys_rst_n,
output wire clc_out
);
reg [2:0] cnt;
reg clk_1;
reg clk_2;
//分频计数器计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd4)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
//分频
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_1 <= 1'b0;
else if(cnt == 3'd2)
clk_1 <= 1'b1;
else if(cnt == 3'd4)
clk_1 <= 1'b0;
else
clk_1 <= clk_1;
always@(negedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_2 <= 1'b0;
else if(cnt == 3'd2)
clk_2 <= 1'b1;
else if(cnt == 3'd4)
clk_2 <= 1'b0;
else
clk_2 <= clk_2;
assign clc_out = (clk_1 | clk_2);
endmodule
仿真波形图如下
仿真代码如下
`timescale 1ns/1ns
module tb_divider_five();
reg sys_clk;
reg sys_rst_n;
wire clc_out;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
divider_five divider_five_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.clc_out (clc_out)
);
endmodule
其它占空比的奇分频的设计如下
其实和偶分频套路一样,代码如下
module divider_five
(
input wire sys_clk,
input wire sys_rst_n,
output reg clk_flag
);
reg [2:0] cnt;
//分频计数器计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd4)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
//分频
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_flag <= 1'b0;
else if(cnt == 3'd3)
clk_flag <= 1'b1;
else
clk_flag <= 1'b0;
endmodule
仿真波形图如下
任意分频的参考下面的博客