偶分频
分频器是数字系统中最常见的基本电路之一。分频就是将输入信号的频率变成成倍地低于输入频率的输出信号。如输入信号频率为50MHZ,10分频后输出信号频率为5MHZ。这里10是分频系数,分频系数为偶数则为偶分频,分频系数为奇数则为奇分频。先说偶分频,有两种方法:分频法和降频法。
分频法
分频系数为N(N为偶数),就是N分频,分频器实现方法和计数器原理一致,当计数值到(N/2)-1时,输出信号电平翻转,此输出信号即为N分频的信号。
代码如下:
module divider_8
(
input sys_clk,
input rst_n,
output reg clk_out
);
reg [1:0] cnt;
always@(posedge sys_clk or negedge rst_n) begin
if(rst_n == 1'b0)
cnt <= 2'd0;
else if(cnt == 2'd3)
cnt <= 2'd0;
else
cnt <= cnt + 2'd1;
end
always@(posedge sys_clk or negedge rst_n)begin
if(rst_n == 1'd0)
clk_out <= 1'd0;
else if(cnt == 2'd3)
clk_out <= ~clk_out;
else
clk_out <= clk_out;
end
endmodule
上述方法生成了低频时钟clk_out,但是如果直接用clk_out作为驱动时钟实现一些功能会出现隐患。在FPGA中凡是时钟信号都需要连接到全局时钟网络(全局时钟树),是FPGA厂商专门为时钟路径设计的,它能使时钟信号到达每个寄存器的时间尽可能地相同,减少时序问题的产生。而分频方式产生的低频时钟并没有连接到全局时钟网络上,外部晶振产生的时钟信号通过管脚连接到FPGA专用的时钟引脚上,即连接到了全局时钟网络上。所以在系统时钟控制下的信号比低频时钟控制下的信号更能保持稳定。
降频法
下面采用生成标志位(脉冲标志信号cnt_flag)对上述方法进行一些改进。在计数值达到N-2(这里N-2为6,用N-2是因为时序逻辑生成信号会延迟一个周期)时,生成一个脉冲标志信号cnt_flag,并保持一个时钟周期。如此仍用系统时钟作为驱动时钟,而将cnt_flag作为标志信号进行条件判断,可以实现和上述方法相同的分频功能(由下图可知,每隔一个cnt_flag信号同样对应8个时钟周期)。但运用标志信号的方法在高速系统中能够减少时序错误,这种方法称之为降频。
module divider_8
(
input sys_clk,
input rst_n,
//output reg clk_out
output reg cnt_flag
);
reg [2:0] cnt;
/* always@(posedge sys_clk or negedge rst_n) begin //注释掉的部分为生成低频时钟的方法
if(rst_n == 1'b0)
cnt <= 2'd0;
else if(cnt == 2'd3)
cnt <= 2'd0;
else
cnt <= cnt + 2'd1;
end
always@(posedge sys_clk or negedge rst_n)begin
if(rst_n == 1'd0)
clk_out <= 1'd0;
else if(cnt == 2'd3)
clk_out <= ~clk_out;
else
clk_out <= clk_out;
end */
always@(posedge sys_clk or negedge rst_n) begin
if(rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd7)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
end
always@(posedge sys_clk or negedge rst_n)begin
if(rst_n == 1'd0)
cnt_flag <= 1'd0;
else if(cnt == 3'd6) //注意是6(N-2)
cnt_flag <= 1'd1; //保持一个周期高电平
else
cnt_flag <= 1'd0; //其他时刻为低电平
end
endmodule
仿真文件如下:
`timescale 1ns/1ns
module divider_8_tb();
reg sys_clk;
reg rst_n;
//wire clk_out;
wire cnt_flag;
parameter CYCLE = 20;
initial begin
sys_clk = 1'b0;
forever #(CYCLE/2) sys_clk = ~sys_clk;
end
initial begin
rst_n <= 1'b0;
#20
rst_n <= 1'b1;
end
divider_8 divider_8_inst
(
.sys_clk(sys_clk) ,
.rst_n (rst_n) ,
// .clk_out (clk_out)
.cnt_flag (cnt_flag)
);
endmodule
仿真结果如下,经过对比,仿真结果和所绘时序图一致。
如果使用低频时钟作为驱动,则always块如以下形式:
always@(posedge clk_out or negedge rst_n) begin //sys_clk改为clk_out
if(rst_n == 1'b0)
...
...
end
采用系统时钟作为驱动,则形式如下:
always@(posedge sys_clk or negedge rst_n) begin
if(rst_n == 1'b0)
...
else if(cnt_flag == 1’b1) //以脉冲标志位作为条件判断,同样起到分频作用
...
else
...
end
来个简短的总结
在高速系统用到分频时钟的时候,最好采用生成脉冲标志信号的方法,可减少时序错误。低速系统中甚至只有几个寄存器的情况下,采用两种方法皆可。