1.理论部分
板载晶振提供的频率是固定的,为了满足工程需求,需要对时钟分频或者倍频,这里学习基础的分频方法。毫无疑问,分频是通过计数器来实现的。分频器分为偶分频和奇分频,分频的方法这里介绍普通分频和降分频。这里需要注意一下两者分频方法的区别:在FPGA中,凡是时钟信号都会连接带全局时钟网络上【又称为全局时钟树】,着使得时钟信号到达每个寄存器的时间都尽可能相同,保持更低的偏斜【skew】和抖动【jitter】,但是普通分频得到的时钟并没有连接到全局时钟网络上,但 系统时钟是由外部晶振直接通过管脚连接到了FPGA的专用时钟管脚上,自然就会连接到全局时钟网络上,所以在高速系统中,系统时钟工作下的信号相对于普通分频得到的时钟下的工作信号要更加稳定。
2.降分频
降分频的方式下可以不用区分奇数和偶数,通过拉高一个标志位来完成对系统时钟的分频,下面以6分频为例:
2.1 当计数器计数到6-1的时候拉高一个标志位clk_flag,那么两个标志位之间则刚好对系统时钟clk完成6分频,画出波形如下:
2.2 代码部分也比较简单
module divider(
input wire clk,
input wire rst_n,
output reg cnt_flag
);
//6分频,根据分频数对DIVIDER更改参数即可
parameter DIVEDER = 'd6;
//根据分频的要求更改位宽以满足计数
reg [4:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt <= 'd0;
end
else if (cnt == DIVEDER - 1'b1) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt_flag <= 1'b0;
end
else if (cnt == DIVEDER - 'd2) begin
cnt_flag <= 1'b1;
end
else begin
cnt_flag <= 1'b0;
end
end
endmodule
2.3 建立tb文件
`timescale 1ns/1ns
module tb_divider();
reg clk;
reg rst_n;
wire cnt_flag;
initial begin
clk = 1;
rst_n = 0;
#30
rst_n = 1;
end
always #10 clk = ~clk;
divider inst_divider (
.clk(clk),
.rst_n(rst_n),
.cnt_flag(cnt_flag));
endmodule
2.4 仿真结果如下:
3.普通分频
普通分频中的偶分频比较简单,以6分频为例,当计数器计数到一半时,输出的信号取反一次即可。这里主要了解一下奇数分频的技巧,以5分频为例:
3.1 5分频中,需要根据系统时钟上升沿和下降沿分别变化,才能得到分频的时钟,波形图如下:clk1在系统时钟的上升沿发生翻转,clk2在系统时钟的下降沿发生翻转,翻转是时刻分别在(5-1)/2和5-1,clk1和clk2的相与得到5分频的时钟clk_out。
3.2 代码如下;
`timescale 1ns / 1ns
module divider(
input wire clk,
input wire rst_n,
output wire clk_out
);
//参数化,设置为5分频
parameter DIVIDER = 'd5;
//5分频读的话计数到2翻转一次
parameter HALF = (DIVIDER-1)>>1;
reg [4:0] cnt;
reg clk1;
reg clk2;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt <= 5'b0;
end
else if (cnt == DIVIDER-1) begin
cnt <= 5'b0;
end
else begin
cnt <= cnt + 1'b1;
end
end
//clk1在clk的上升沿发生翻转
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
clk1 <= 1'b1;
end
else if (cnt == HALF) begin
clk1 <= 1'b0;
end
else if (cnt == DIVIDER - 1'b1) begin
clk1 <= 1'b1;
end
end
//clk2在clk的下降沿发生翻转
always @(negedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
clk2 <= 1'b1;
end
else if (cnt == HALF) begin
clk2 <= 1'b0;
end
else if (cnt == DIVIDER - 1'b1) begin
clk2 <= 1'b1;
end
end
assign clk_out = clk1 & clk2;
endmodule
3.3 建立tb文件
`timescale 1ns/1ns
module tb_divider();
reg clk;
reg rst_n;
wire clk_out;
initial begin
clk = 1;
rst_n = 0;
#30
rst_n = 1;
end
always #10 clk = ~clk;
divider inst_divider (
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out));
endmodule
3.4 仿真结果如下:
文章内容参考野火FPGA