verilog 分频器设计(奇偶分频、半整数分频、任意分频、任意占空比)

分频器是指使输出信号频率为输入信号频率整数分之一的电子电路。在许多电子设备中如电子钟、频率合成器等,需要各种不同频率的信号协同工作,常用的方法是以稳定度高的晶体振荡器为主振源,通过变换得到所需要的各种频率成分,分频器是一种主要变换手段。

早期的分频器多为正弦分频器,随着数字集成电路的发展,脉冲分频器(又称数字分频器)逐渐取代了正弦分频器。

一、偶数分频

采用触发器反向输出端连接到输入端的方式,可构成简单的 2 分频电路。

以此为基础进行级联,可构成 4 分频,8 分频电路。

电路实现如下图所示,用 Verilog 描述时只需使用简单的取反逻辑即可。

如果偶数分频系数过大,就需要对分频系数 N 循环计数进行分频。在计数周期达到分频系数中间数值 N/2 时进行时钟翻转,可保证分频后时钟的占空比为 50%。因为是偶数分频,也可以对分频系数中间数值 N/2 进行循环计数。

偶数分频的 Verilog 描述举例如下。

module even_divisor
  # (parameter DIV_CLK = 10 )
    (
    input               rstn ,
    input               clk,
    output              clk_div2,
    output              clk_div4,
    output              clk_div10
    );

   //2 分频
   reg                  clk_div2_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_div2_r     <= 'b0 ;
      end
      else begin
         clk_div2_r     <= ~clk_div2_r ;
      end
   end
   assign       clk_div2 = clk_div2_r ;

   //4 分频
   reg                  clk_div4_r ;
   always @(posedge clk_div2 or negedge rstn) begin
      if (!rstn) begin
         clk_div4_r     <= 'b0 ;
      end
      else begin
         clk_div4_r     <= ~clk_div4_r ;
      end
   end
   assign clk_div4      = clk_div4_r ;

   //N/2 计数
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == (DIV_CLK/2)-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //输出时钟
   reg                  clk_div10_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_div10_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK/2)-1 ) begin
         clk_div10_r <= ~clk_div10_r ;
      end
   end
   assign clk_div10 = clk_div10_r ;
endmodule

仿真结果如下:

二、奇数分频

奇数分频如果不要求占空比为 50%,可按照下面任意整数分频和任意占空比的方法进行分频。即计数器对分频系数 N 进行循环计算,然后根据计数值选择一定的占空比输出分频时钟。

如果奇数分频输出时钟的高低电平只差一个 cycle ,则可以利用源时钟双边沿特性并采用"与操作"或"或操作"的方式将分频时钟占空比调整到 50%。

或操作调整占空比:采用"或操作"产生占空比为 50% 的 3 分频时序图如下所示。

  • 利用源时钟上升沿分频出高电平为 1 个 cycle、低电平为 2 个 cycle 的 3 分频时钟。

  • 利用源时钟下降沿分频出高电平为 1 个 cycle、低电平为 2 个 cycle 的 3 分拼时钟。

  • 两个 3 分频时钟应该在计数器相同数值、不同边沿下产生,相位差为半个时钟周期。然后将 2 个时钟进行"或操作",便可以得到占空比为 50%的 3 分频时钟。

同理,9 分频时,则需要在上升沿和下降沿分别产生 4 个高电平、5 个低电平的 9 分频时钟,然后再对两个时钟做"或操作"即可。Verilog 描述如下。

module odo_div_or
  #(parameter DIV_CLK = 9)
   (
    input               rstn ,
    input               clk,
    output              clk_div9
    );

   //计数器
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //在上升沿产生9分频
   reg                  clkp_div9_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clkp_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1)-1 ) begin //计数4-8位低电平
        clkp_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin //计数 0-3 为高电平
        clkp_div9_r <= 1 ;
      end
   end
  
   //在下降沿产生9分频
   reg                  clkn_div9_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clkn_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1)-1 ) begin 
        clkn_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin 
        clkn_div9_r <= 1 ;
      end
   end

   //或操作,往往使用基本逻辑单元库
   // or (clk_div9, clkp_div9_r, clkn_div9_r) ;
   assign clk_div9 = clkp_div9_r | clkn_div9_r ;

endmodule

仿真结果如下:

与操作调整占空比:采用"与操作"产生占空比为 50% 的 3 分频时序图如下所示。

  • 利用源时钟上升沿分频出高电平为 2 个 cycle、低电平为 1 个 cycle 的 3 分频时钟。

  • 利用源时钟下降沿分频出高电平为 2 个 cycle、低电平为 1 个 cycle 的 3 分拼时钟。

  • 两个 3 分频时钟应该在计数器相同数值、不同边沿下产生,相位差为半个时钟周期。然后将 2 个时钟进行"与操作",便可以得到占空比为 50%的 3 分频时钟。

同理,9 分频时,则需要在上升沿和下降沿分别产生 5 个高电平、4 个低电平的 9 分频时钟,然后再对两个时钟做"与操作"即可。Verilog 描述如下。

module odo_div_and
   #( parameter DIV_CLK = 9 )
   (
    input               rstn ,
    input               clk,
    output              clk_div9
    );

   //计数器
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //在上升沿产生9分频
   reg                  clkp_div9_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clkp_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1) ) begin //计数5-8位低电平
        clkp_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin //计数 0-4 为高电平
        clkp_div9_r <= 1 ;
      end
   end

   //在下降沿产生9分频
   reg                  clkn_div9_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clkn_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1) ) begin 
        clkn_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin 
        clkn_div9_r <= 1 ;
      end
   end

   //与操作,往往使用基本逻辑单元库
   //and (clk_div9, clkp_div9_r, clkn_div9_r) ;
   assign clk_div9 = clkp_div9_r & clkn_div9_r ;

endmodule

仿真结果如下:

三、半整数分频

利用时钟的双边沿逻辑,可以对时钟进行半整数的分频。但是无论怎么调整,半整数分频的占空比不可能是 50%。半整数分频的方法有很多,这里只介绍一种和奇数分频调整占空比类似的方法。

  • (1) 例如进行 3.5 倍分频时,计数器循环计数到 7,分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。从 7 个源时钟产生了 2 个分频时钟的角度来看,该过程完成了 3.5 倍的分频,但是每个分频时钟并不是严格的 3.5 倍分频。
  • (2) 下面对周期不均匀的分频时钟进行调整。一次循环计数中,在源时钟下降沿分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。相对于第一次产生的 2 个周期不均匀的时钟,本次产生的 2 个时钟相位一个延迟半个源时钟周期,一个提前半个源时钟周期。
  • (3) 将两次产生的时钟进行"或操作",便可以得到周期均匀的 3.5 倍分频时钟。分频波形示意图如下所示。

3.5 倍时钟分频的 Verilog 描述如下:

module half_divisor(
    input               rstn ,
    input               clk,
    output              clk_div3p5
    );

   //计数器
   parameter            MUL2_DIV_CLK = 7 ;
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == MUL2_DIV_CLK-1) begin //计数2倍分频比
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   reg                  clk_ave_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_ave_r <= 1'b0 ;
      end
      //first cycle: 4 source clk cycle
      else if (cnt == 0) begin
         clk_ave_r <= 1 ;
      end
      //2nd cycle: 3 source clk cycle
      else if (cnt == (MUL2_DIV_CLK/2)+1) begin
         clk_ave_r <= 1 ;
      end
      else begin
         clk_ave_r <= 0 ;
      end
   end

   //adjust
   reg                  clk_adjust_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_adjust_r <= 1'b0 ;
      end
      //本次时钟只为调整一致的占空比
      else if (cnt == 1) begin
         clk_adjust_r <= 1 ;
      end
      //本次时钟只为调整一致的精确分频比
      else if (cnt == (MUL2_DIV_CLK/2)+1 ) begin
         clk_adjust_r <= 1 ;
      end
      else begin
         clk_adjust_r <= 0 ;
      end
   end

   assign clk_div3p5 = clk_adjust_r | clk_ave_r ;

endmodule

仿真结果如下:

四、任意整数分频

在verilog程序设计中,我们往往要对一个频率进行任意分频,而且占空比也有一定的要求。这样的话,对于程序有一定的要求。现在在前面两个实验的基础上做一个简单的总结,实现对一个频率的任意占空比的任意分频。

比如: FPGA系统时钟是50MHz,而我们要产生的频率是880Hz,那么,我们需要对系统时钟进行分频。很容易想到用计数的方式来分频:50000000/880 = 56818。显然这个数字不是2的整幂次方,那么我们可以设定一个参数,让它到56818的时候重新计数就可以实现了。程序如下:

//rtl
module div(
    clk,
    rst_n,
    clk_div
);
    input clk,rst_n;
    output clk_div;

    reg [15:0] counter;

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        counter <= 0;
    else if(counter==56817)
        counter <= 0;
    else
        counter <= counter+1;

assign clk_div = counter[15];
endmodule

分频的应用很广泛,一般的做法是先用高频时钟计数,然后使用计数器的某一位输出作为工作时钟进行其他的逻辑设计,上面的程序就是一个体现。

五、任意占空比

下面我们来算一下上面产生的时钟的占空比:

我们清楚地知道,这个输出波形在counter为0到32767(2的15次方)的时候为低,在32768到56817的时候为高,占空比为40%多一些,如果我们需要占空比为50%,那么我们需要再设定一个参数,使它为56817的一半,使达到它的时候波形翻转,就可以实现结果了。程序如下:

28408=56818/2-1,计数到28408就清零,翻转,其余的计数期间,保持不变。

//rtl
module div(
    clk,
    rst_n,
    clk_div
);
    input clk,rst_n;
    output clk_div;
    reg clk_div;
    reg [14:0] counter;
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        counter <= 0;
    else if(counter==28408)
        counter <= 0;
    else
        counter <= counter+1;

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        clk_div <= 0;
    else if(counter==28408)
        clk_div <= ~clk_div;
endmodule

继续让我们来看如何实现任意占空比,比如还是由50M分频产生880Hz,而分频得到的信号的占空比为30%。56818×30%=17045:

//rtl
module div(
    clk,
    rst_n,
    clk_div,
);
    input clk,rst_n;
    output clk_div;
    reg clk_div;
    reg [15:0] counter;

always @(posedge clk)
    if(!rst_n)
        counter <= 0;
    else if(counter==56817)
        counter <= 0;
    else counter <= counter+1;

always @(posedge clk)
  if(!rst_n)
    clk_div <= 0;
  else if(counter<17045)
    clk_div <= 1;
  else
    clk_div <= 0;
 endmodule
posted @ 2020-09-24 10:10  耐心的小黑  阅读(316)  评论(0编辑  收藏  举报