verilog时序单元分频器

发布时间 2023-07-22 10:16:40作者: luckylan

分频电路

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的变化即为下降沿。 当被测信号与时钟相关时, 可以不用第一个触发器。

2.3.3 上升/下降沿检测