分频:简单来说,二分频后的方波一个周期为标准方波高低电平循环两个周期,四分频为4个周期。分频后的时钟周期为原来的n倍,即为n分频。
频率和周期的关系:f=1/T
(1)简单的计数器
计数器实质是对输入的驱动时钟进行计数,在某种意义上讲,等同于对时钟进行分频。
下面举例来说明:假设寄存器位数为n,最大计数长度为2n(从0计数到N-1)的计数器。假设位数为3,从0开始依次计数为:000、001、010、011、100、101、110、111。一共计8个数,计满后下一次计数为000,开始循环计数。观察这几个数的最高位,00001111,每一个周期计数一次,共计8次,8个周期,8分频。次高位为00110011,4个周期完成循环,为4分频。最低位为01010101,2个周期完成循环,为2分频。最高位的输出为N=2^n分频,次高位为N/2分频。
module test#(parameter N=3)(
input clk,
input rst_n,
output clk_div
);
reg [N-1:0] div_reg ;//分频计数器
always @(posedge clk or negedge rst_n)
if (rst_n == 1'b0 )
div_reg <= 0 ;
else
div_reg <= div_reg + 1'b1 ;
assign clk_div = div_reg[N-1] ;
endmodule
当n不是2的整数次幂时,如n为5,从0计数到n-1,其最高位作为时钟输出(占空比不一定为 50%),即从0计数到4后又返回0,那么需要定义一个三位的寄存器。寄存器的计数过程为:
000-001-010-011-100-000-001-010-011-100-000-001-010-011-100-000-001-010-011-100···
最高位为0-0-0-0-1-0-0-0-0-1,计数5次,5个周期,为5分频。但占空比不是50%。
可以看出:一个最大计数长度为N(从0计数到N-1)的计数器,其最高位的输出,是输入频率的N分频。
(2)偶数分频(50%占空比)
当进行2、4、8(2的n次)分频时,输出2的n次最高位即为相应分频,并且占空比为50%,但6、10这样的分频,占空比就不是50%了,该怎么办呢?
解决办法很简单,以6分频举例,当计数到3时,将时钟翻转,计数清零并重新计数。当n=6时,6/2=3,计算机计数从0开始,0、1、2、3共计数4次。
module test#(parameter N=6)(
input clk,
input rst_n,
output clk_div
);
reg div_reg ;
reg [N-1:0] div_cnt ;//分频计数器
always @(posedge clk or negedge rst_n)
if (rst_n == 1'b0 )begin
div_cnt <= 0 ;
div_reg <= 0 ;
end
else if(div_cnt == (N/2 - 1))begin
div_cnt <= 0;
div_reg <= ~div_reg ;
end
else
div_cnt <= div_cnt + 1'b1 ;
assign clk_div = div_reg ;
endmodule
(3)奇数倍分频
①占空比接近50%
第一种方法与上面的类似,先将奇数变成偶数,再除以2,计数完成进行时钟翻转。此时得出的分频接近50%,N越大越趋近于50%。
②占空比50%
一个周期的系统时钟可以分为一个高电平和低电平。假设目标为3分频,循环三次的话高低高低高低;假如我是说假如在高低高这三个阶段输出1,在地高低这三个阶段输出0,组成的新时钟刚好是占空比为50%的3分频时钟。
那我们该怎样实现呢?
从上升沿开始计数到1(也就是上面系统时钟的高低),计数时输出1,完成后输出0,一直到计数到2时翻转。
从下降沿开始计数,计一个周期(也就是上面的低高),计数时输出1,完成后输出0,一直到计数为2时翻转。两个时钟相或,就可以得到想要的结果。如下图所示:
第一个上升沿来临后,计数器从0开始自加1,同时输出1,第二个上升沿来临后,计数器值自加1,输出变为0,第三个上升沿来临计数为2,下一个上升沿来临时,计数器满足要求,开始翻转输出,这样循环。
下降沿采用同样思路编写代码。
module test#(parameter N=5)(//N分频
input clk,
input rst_n,
output clk_div
);
reg sig_r ;//定义一个上升沿翻转的信号
reg sig_f ;//定义一个下降沿翻转的信号
reg [N-1:0] cnt_r;//上升沿计数器
reg [N-1:0] cnt_f;//下降沿计数器
wire clk_f ;
assign clk_f = ~clk ;//用来触发下降沿计数器的时钟
//由于同时使用上升沿和下降沿触发器不好,因此我们为同一边沿,都使用上升沿触发
//只不过是将时钟进行反向
always @(posedge clk or negedge rst_n)begin//上升沿计数
if(rst_n == 1'b0)begin
sig_r <= 0 ;
cnt_r <= 0 ;
end else if( cnt_r == (N-1)/2 )begin
sig_r <= ~sig_r ;
cnt_r <= cnt_r + 1 ;
end else if ( cnt_r == (N-1) )begin
sig_r <= ~sig_r ;
cnt_r <= 0 ;
end else
cnt_r <= cnt_r + 1 ;
end
always @(posedge clk_f or negedge rst_n)begin//下降沿计数
if(rst_n == 1'b0)begin
sig_f <= 0 ;
cnt_f <= 0 ;
end else if( cnt_f == (N-1)/2 )begin
sig_f <= ~sig_f ;
cnt_f <= cnt_f + 1 ;
end else if ( cnt_f == (N-1) )begin
sig_f <= ~sig_f ;
cnt_f <= 0 ;
end else
cnt_f <= cnt_f + 1 ;
end
assign clk_div = sig_f || sig_r ;
endmodule
结语:结合奇数部分和偶数部分,编写新的代码。
新的代码如下所示:
module test #( parameter cfactor= 5)(
input clk,
input rst_n,
output clk_div
);
reg clk_loc;
//reg [15:0] cnt;//allowed maximum clock division factor is 65536
reg [7:0] cnt;//allowed maximum clock division factor is 256
assign clk_div = (cfactor==1)? clk : clk_loc;
//assign clk_div = ((rst==1) || (cfactor==1))? clk : clk_loc;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
cnt <= 'd0;
clk_loc = 1;
end
else begin
cnt <= cnt + 1'b1;
if(cnt==cfactor/2-1)
clk_loc = 0;
else if(cnt==cfactor-1) begin
cnt <= 'd0;
clk_loc = 1;
end
end
endmodule