• Verilog设计分频器(面试必看)


    分频器是指使输出信号频率为输入信号频率整数分之一的电子电路。在许多电子设备中如电子钟、频率合成器等,需要各种不同频率的信号协同工作,常用的方法是以稳定度高的晶体振荡器为主振源,通过变换得到所需要的各种频率成分,分频器是一种主要变换手段。
        早期的分频器多为正弦分频器,随着数字集成电路的发展,脉冲分频器(又称数字分频器)逐渐取代了正弦分频器。

    下面以Verilog HDL 语言为基础介绍占空比为50%的分频器。

    1、偶分频

      偶分频电路指的是分频系数为 2、4、6、8 ... 等偶数整数的分频电路,我们可以直接进行分频。
       例如下面 divider.v 中,对输入时钟进行6分频,即假设clk 为 50MHz ,分频后的时钟频率为 (50/6) MHz。程序如下:

    设计代码:

     1 //rtl
     2 module divider(
     3     clk,
     4     rst_n,
     5     clk_div
     6 );
     7     input clk;
     8     input rst_n;
     9     output clk_div;
    10     reg clk_div;
    11     
    12     parameter NUM_DIV = 6;
    13     reg    [3:0] cnt;
    14     
    15 always @(posedge clk or negedge rst_n)
    16     if(!rst_n) begin
    17         cnt     <= 4'd0;
    18         clk_div    <= 1'b0;
    19     end
    20     else if(cnt < NUM_DIV / 2 - 1) begin
    21         cnt     <= cnt + 1'b1;
    22         clk_div    <= clk_div;
    23     end
    24     else begin
    25         cnt     <= 4'd0;
    26         clk_div    <= ~clk_div;
    27     end
    28  endmodule
    View Code

    仿真程序:

     1 //tb
     2  module divider_tb();
     3     reg clk;
     4     reg rst_n;
     5     wire clk_div;
     6     parameter DELY=100;
     7 divider U_divider(
     8     .clk    (clk    ),
     9     .rst_n    (rst_n    ),
    10     .clk_div(clk_div)
    11 );
    12     always #(DELY/2) clk=~clk;//产生时钟波形
    13 initial begin
    14     $fsdbDumpfile("divider_even.fsdb");
    15     $fsdbDumpvars(0,U_divider);
    16  end
    17  initial begin
    18           clk=0;rst_n=0;
    19     #DELY rst_n=1;
    20     #((DELY*20)) $finish;
    21 end    
    22 endmodule 
    View Code

    可以看到,clk的上升沿,采样到cnt=2的时候,就翻转,采样到0和1的时候,保持。这样就可以做到一半高电平,一半低电平。

    2、奇分频
      由于奇分频需要保持分频后的时钟占空比为 50% ,所以不能像偶分频那样直接在分频系数的一半时使时钟信号翻转(高电平一半,低电平一半)。
        在此我们需要利用输入时钟上升沿和下降沿来进行设计。
          接下来我们设计一个 5 分频的模块,设计思路如下:
         采用计数器 cnt1 进行计数,在时钟上升沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div1。
           采用计数器 cnt2进行计数,在时钟下降沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div2。
           clk_div1 和clk_div2 的上升沿到来时间相差半个输入周期,所以将这两个信号进行或操作,即可得到占空比为 50% 的5分频时钟。程序如下:
    设计代码:
     1 //rtl
     2 module divider( 
     3     clk,
     4     rst_n,
     5     clk_div
     6 );
     7     input clk;
     8     input rst_n;
     9     output clk_div;
    10     reg clk_div;
    11     
    12     parameter NUM_DIV = 5;
    13     reg[2:0] cnt1;
    14     reg[2:0] cnt2;
    15     reg    clk_div1, clk_div2;
    16 
    17 always @(posedge clk or negedge rst_n)
    18     if(!rst_n)
    19         cnt1 <= 0;
    20     else if(cnt1 < NUM_DIV - 1)
    21         cnt1 <= cnt1 + 1'b1;
    22     else 
    23         cnt1 <= 0;
    24         
    25 always @(posedge clk or negedge rst_n)
    26     if(!rst_n)
    27         clk_div1 <= 1'b1;
    28     else if(cnt1 < NUM_DIV / 2) 
    29         clk_div1 <= 1'b1;
    30     else
    31         clk_div1 <= 1'b0;
    32         
    33 always @(negedge clk or negedge rst_n)
    34     if(!rst_n)
    35        cnt2 <= 0;
    36     else if(cnt2 < NUM_DIV - 1)
    37        cnt2 <= cnt2 + 1'b1;
    38     else 
    39        cnt2 <= 0;
    40 
    41 always @(negedge clk or negedge rst_n)
    42     if(!rst_n)
    43         clk_div2 <= 1'b1;
    44     else if(cnt2 < NUM_DIV / 2) 
    45         clk_div2 <= 1'b1;
    46     else
    47         clk_div2 <= 1'b0;
    48         
    49     assign clk_div = clk_div1 | clk_div2;
    50 endmodule
    View Code

    仿真代码:

     1 //tb
     2 module divider_tb();
     3     reg clk;
     4     reg rst_n;
     5     wire clk_div;
     6     parameter DELY=100;
     7 divider U_divider(
     8     .clk    (clk    ),
     9     .rst_n    (rst_n    ),
    10     .clk_div(clk_div)
    11 );
    12     always #(DELY/2) clk=~clk;//产生时钟波形
    13 initial begin
    14     $fsdbDumpfile("divider_odd.fsdb");
    15     $fsdbDumpvars(0,U_divider);
    16  end
    17  initial begin
    18           clk=0;rst_n=0;
    19     #DELY rst_n=1;
    20     #((DELY*20)) $finish;
    21 end
    22 endmodule
    View Code

    对其进行测试和验证(此仿真波形是三分频,占空比50%),即上述程序吧NUM_DIV改成3即可,得到如下波形:

    3.任意占空比的任意分频

      在verilog程序设计中,我们往往要对一个频率进行任意分频,而且占空比也有一定的要求这样的话,对于程序有一定的要求。
      现在在前面两个实验的基础上做一个简单的总结,实现对一个频率的任意占空比的任意分频。
      比如: FPGA系统时钟是50M Hz,而我们要产生的频率是880Hz,那么,我们需要对系统时钟进行分频。很容易想到用计数的方式来分频:50000000/880 = 56818。
      显然这个数字不是2的整幂次方,那么我们可以设定一个参数,让它到56818的时候重新计数就可以实现了。程序如下:

    设计代码:

     1 //rtl
     2 module div(
     3     clk, 
     4     rst_n,
     5     clk_div
     6 );
     7     input clk,rst_n;
     8     output clk_div;
     9     reg clk_div;
    10   
    11     reg [15:0] counter;
    12 
    13 always @(posedge clk or negedge rst_n)
    14     if(!rst_n) 
    15         counter <= 0;
    16     else if(counter==56817) 
    17         counter <= 0;
    18     else 
    19         counter <= counter+1;
    20 
    21    assign clk_div = counter[15];
    22 endmodule
    View Code

    仿真代码:

     1 //tb
     2 module div_tb();
     3     reg clk;
     4     reg rst_n;
     5     wire clk_div;
     6     parameter DELY=100;
     7 div U_div(
     8     .clk    (clk    ),
     9     .rst_n  (rst_n),
    10     .clk_div(clk_div)
    11 );
    12     always #(DELY/2) clk=~clk;//产生时钟波形
    13 initial begin
    14     $fsdbDumpfile("div_any.fsdb");
    15     $fsdbDumpvars(0,U_div);
    16  end
    17  initial begin
    18           clk=0;rst_n=0;
    19     #DELY rst_n=1;      
    20     #((DELY*80000)) $finish;
    21 end
    22 endmodule
    View Code

    分频的应用很广泛,一般的做法是先用高频时钟计数,然后使用计数器的某一位输出作为工作时钟进行其他的逻辑设计,上面的程序就是一个体现。
      下面我们来算一下它的占空比:
      我们清楚地知道,这个输出波形在counter为0到32767(2的14次方)的时候为低,在32768到56817的时候为高,占空比为40%多一些,
      如果我们需要占空比为50%,那么我们需要再设定一个参数,使它为56817的一半,使达到它的时候波形翻转,就可以实现结果了。
      程序如下:28408=56818/2-1,计数到28408就清零,翻转,其余的计数期间,保持不变。

    设计代码:

     1 //rtl
     2 module div(
     3     clk, 
     4     rst_n,
     5     clk_div
     6 );
     7     input clk,rst_n;
     8     output clk_div;
     9     reg clk_div;
    10     reg [14:0] counter;
    11 always @(posedge clk or negedge rst_n)
    12     if(!rst_n) 
    13         counter <= 0;
    14     else if(counter==28408)
    15         counter <= 0;
    16     else 
    17         counter <= counter+1;
    18 
    19 always @(posedge clk or negedge rst_n)
    20     if(!rst_n) 
    21         clk_div <= 0;
    22     else if(counter==28408) 
    23         clk_div <= ~clk_div;
    24 endmodule
    View Code

    仿真代码:

     1 //tb
     2 module div_tb();
     3     reg clk;
     4     reg rst_n=0;
     5     wire clk_div;
     6     parameter DELY=100;
     7 div U_div(
     8     .clk    (clk    ),
     9     .rst_n  (rst_n),
    10     .clk_div(clk_div)
    11 );
    12     always #(DELY/2) clk=~clk;//产生时钟波形
    13 initial begin
    14     $fsdbDumpfile("div_any.fsdb");
    15     $fsdbDumpvars(0,U_div);
    16  end
    17  initial begin
    18           clk=0;rst_n=0;
    19     #DELY rst_n=1;
    20     #((DELY*80000)) $finish;
    21 end
    22 endmodule
    View Code
    继续让我们来看如何实现任意占空比,比如还是由50M分频产生880Hz,而分频得到的信号的占空比为30%。
    56818×30%=17045
    设计代码:
     1 //rtl
     2 module div(
     3     clk,
     4     rst_n,
     5     clk_div,
     6     counter
     7 );
     8     input clk,rst_n;
     9     output clk_div;
    10     reg clk_div;
    11     output [15:0] counter;
    12     reg [15:0] counter;
    13     
    14 always @(posedge clk)
    15     if(!rst_n) 
    16         counter <= 0;
    17     else if(counter==56817) 
    18         counter <= 0;
    19     else counter <= counter+1;
    20 
    21 always @(posedge clk)
    22   if(!rst_n) 
    23     clk_div <= 0;
    24   else if(counter<17045) 
    25     clk_div <= 1;
    26   else 
    27     clk_div <= 0;
    28  endmodule
    View Code

    仿真代码:

     1 //tb
     2 module div_tb();
     3     reg clk;
     4     reg rst_n;
     5     wire clk_div;
     6     wire [15:0] counter;
     7     parameter DELY=100;
     8 div U_div(
     9     .clk    (clk    ),
    10     .rst_n  (rst_n  ),
    11     .counter(counter),
    12     .clk_div(clk_div)
    13 );
    14     always #(DELY/2) clk=~clk;//产生时钟波形
    15 initial begin
    16     $fsdbDumpfile("div_any.fsdb");
    17     $fsdbDumpvars(0,U_div);
    18  end
    19  initial begin
    20           clk=0;rst_n=0;
    21     #DELY rst_n=1; 
    22     #((DELY*80000)) $finish;
    23 end
    24 endmodule
    View Code
    4 小结
     通过以上几个例子对比不难发现,借助计数器来实现任意点空比的任意分频的方法简单,且用verilog语言进行行为描述时,代码简洁、易懂、通用。
     通过以上的学习,对分频器有了比较深刻的认识,将在以后的学习中会有广泛的应用。

    原出处:https://www.chipist.cn/article/166  如有什么疑问,欢迎讨论:QQ:447574829

  • 相关阅读:
    黑苹果崩溃恢复
    黑苹果声音小解决方法
    idea plugin 进度条
    phpstorm 插件
    awesome mac
    webstorm vue eslint 自动修正配置
    Laravel/php 一些调试技巧
    php ZipArchive 压缩整个文件夹
    laravel 模型事件 updated 触发条件
    php 开启 opcache 之后 require、include 还会每次都重新加载文件吗?
  • 原文地址:https://www.cnblogs.com/zhangxianhe/p/11083208.html
Copyright © 2020-2023  润新知