分频电路
2.2.1 简单的计数器
计数器实质是对输入的驱动时钟进行计数,所以计数器在某种意义上讲,等同于对时钟进行分频。例如一个最大计数长度为N=2^M(从0计数到N-1)的计数器,也就是寄存器位数为M,那么寄存器最高位的输出为N=2^M分频,次高位为N/2分频...例如下面的代码:
module test#(parameter N=3)( input clk, input rst_n, output clk_div ); reg [N-1:0] div_reg ;//分频计数器 assign clk_div = div_reg[N-1] ; always @(posedge clk or negedge rst_n) if (rst_n == 1'b0 ) div_reg <= 0 ; else div_reg <= div_reg + 1'b1 ; endmodule
parameter例化方式
clk_div #(.N(3) ) u_clk_div( .clk(clk), .rst_n(rst_n), .clk_div(clk_div) );
该代码描述的将一个3位的计数器最高位输出,也就是计数长度为8(计数从0~7)波形如下所示:
可以看到最高位的输出为输入时钟的8分频。
当N不是2的整数次幂时,即N≠2^M时,从0计数到N-1,其最高位作为时钟输出(占空比不一定为 1:1)是输入时钟的1/N,也就是N分频。我们来举个例子,比如最大计数长度为5的计数器,即从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-0-1-0-0-0-0-1-0-0-0-0-1···
代码如下所示:
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 if(div_reg == 3'd4) //从0计数到4,然后返回到0,5分频 div_reg <= 0; else div_reg <= div_reg + 1'b1 ; assign clk_div = div_reg[N-1] ; endmodule
由此可以看到,每一个分频后的时钟周期=5倍原来的时钟周期,因此是5分频。
那么这个情况是不是也可以包含8分频的情况呢?我们设置为8分频,即前面的3'd4改成3'd7,得到的仿真波形如下所示:
可以看到,计数器的最高位输出也是输入频率的1/N。
因此我们得到结论:一个最大计数长度为N(从0计数到N-1)的计数器,其最高位的输出,是输入频率的N分频。
通常 ASIC 和 FPGA 中,时钟都是全局信号,都需要通过 PLL 处理才能使用,但某些简易场合,采用计数器输出时钟也是能够使用的,只是需要注意时序约束。
偶数倍分频(占空比50%)
偶数分频,也就是2分频、4分频、6分频...这个还是比较简单的,N(N当然是2的倍数)分频,那么计数到N/2-1,然后时钟翻转,例如N=6时,代码如下所示:
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
当N=2的仿真波形如下所示
奇数倍分频
①占空比接近50%
对于占空比不是50%的计数分频,我们可以直接用上面的计数器方法,这里就不说了,我们介绍其他接近50%的占空比的方法,比如下面使用的状态机分频:
上图的状态机除了用一般的状态机设计方式之外,我们也可以用简单的计数器实现,这种方法如下所示:
假设时钟分频是N,则设置一个计数器,计数长度是N(即从0计数到N-1),然后在计数器为计数到(N-1)/2的时候,翻转一下分频时钟信号;在计数器计数到为N-1的时候,再翻转一下时钟。代码如下所示:
module test#(parameter N=3)( //N分频,这里是3分频 input clk, input rst_n, output clk_div ); reg [N-1:0] div_cnt ;//分频计数器 reg div_reg ; always @(posedge clk or negedge rst_n)begin if (rst_n == 1'b0 )begin div_cnt <= 0 ; div_reg <= 1 ; end else if (div_cnt == (N-1)/2)begin //计数到(N-1)/2,进行翻转和继续计数 div_reg <= ~div_reg; div_cnt <= div_cnt + 1'b1 ; end else if ( div_cnt == (N-1) )begin //计数到N-1,进行清零和翻转 div_cnt <= 0 ; div_reg <= ~div_reg; end else div_cnt <= div_cnt + 1'b1 ; end assign clk_div = (N == 1)?clk:div_reg ; //注意这里 endmodule
分频,N=3:
分频,N=5:
奇数分频(占空比50%)
产生具有50%占空比的奇数分频时钟的算法如下所示,假设N分频(N是计数):
设置一个计数长度为N的上升沿计数器,和一个信号寄存器;信号寄存器在上升沿计数器为(N-1)/2的时候进行翻转,然后再在计数到N-1的时候进行翻转(这里相当于得到一个N分频信号A)。
再设置一个计数长度为N的下降沿计数器,和另一个信号寄存器;信号寄存器在下降沿计数器为(N-1)/2的时候进行翻转,然后再在计数到N-1的时候进行翻转(这里相当于得到一个N分频信号B)。
将A和B相或就可以得到占空比50%的奇数分频信号;代码实现如下:
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
奇数分频
若是奇数分频,则处理比较特殊,以5分频器为例,其要求产生的时序关系如下图所示
很显然,该电路要用上MCLK的上沿和下研,对上图时序进行分解,得下图:
图中, COUNT0采用上沿计数, COUNT1采用下沿计数, DIV0和DIV1是分别是上沿触发器和下沿触发器的输出, DIV5_CLK是DIV0和DIV1的或门输出。
利用上升下降沿计数,刚好在一个计数周期内,经过或门之后,高电平时间加半个周期,低电平时间减半个周期,刚好使得50%占空比计数。
在使用该电路时,需要注意:
(1)DIV0和DIV1到DIV5_CLK的约束要严,越快越好。不然,无法保证1:1的占空比。
(2)MCLK频率要求较高,尽量不要出现窄脉冲,尤其是在高频电路里。
(3)COUNT1可有可无,视时钟频率高低而定。频率越高, COUNT1越需要。
50%占空比 2430分频器
我们推荐使用同步计数器最高位的方法,如果需要保证占空比,可以使用下图所示电路进行最后一次二分频。下图是19.44MHz分频到8kMHz(分频数为2430)的电路:计数到N/2-1清零。
分频数为2430的电路
module div_clk #(parameter N=11)( input clk, input rst_n, output clk_div ); reg [N-1:0] cnt_r;//上升沿计数器 always @(posedge clk or negedge rst_n) if(rst_n == 1'b0) cnt_r <= 0 ; else if( cnt_r == 11'd1214 ) cnt_r <= 0 ; else cnt_r <= cnt_r+1 ; wire ENA=(cnt_r==11'd1214); reg Q; always@(posedge clk or negedge rst_n) if(rst_n==1'b0)begin Q=1'b0; end else if(ENA) Q<=~Q; else Q<=Q; assign clk_div = Q; endmodule
分数分频(待补充)
边沿检测电路
上升沿检测电路
采用时钟检测信号,出现0->1的变化即为上升沿。当被测信号与时钟相关时,可以不用第一个触发器。
下降沿检测
采用时钟检测信号, 出现1->0的变化即为下降沿。 当被测信号与时钟相关时, 可以不用第一个触发器。