FPGA几种常用的分频方法

   时钟分频在实际项目中的使用率非常高,下面就总结几种笔者所知道的分频方式。这里讲的分频方式,占空比都为50%。

  第一种不用自己写代码,直接调用官方的IP核或者原语,但是很多FPGA的PLL资源是有限的,因此一般只用于对使用比较多的时钟信号进行倍频分频。

  第二种就是最常用的,也是最简单易懂的分频方式,这种方式是通过计数,计数完成就取反。以下是代码:

 1     reg            [15:0]       r_pos_cnt;
 2     reg            [15:0]       r_neg_cnt;
 3     wire                        w_pos_end;
 4     wire                        w_neg_end;
 5     wire                        w_pos_clk;
 6     wire                        w_neg_clk;
 7     
 8     always@(posedge i_sys_clk)
 9     begin
10         if(~i_sys_rstn | w_pos_end)
11         begin
12             r_pos_cnt <= 'd0;
13         end
14         else
15         begin
16             r_pos_cnt <= r_pos_cnt + 1'b1;
17         end
18     end
19     
20     assign w_pos_end = (r_pos_cnt == DIV - 1);
21     assign w_pos_clk = (r_pos_cnt < DIV_HALF) ? 0 : 1;
22     
23     always@(negedge i_sys_clk)
24     begin
25         if(~i_sys_rstn | w_neg_end)
26         begin
27             r_neg_cnt <= 'd0;
28         end
29         else
30         begin
31             r_neg_cnt <= r_neg_cnt + 1'b1;
32         end
33     end
34     
35     assign w_neg_end = (r_neg_cnt == DIV - 1);
36     assign w_neg_clk = (r_neg_cnt < DIV_HALF) ? 0 : 1;
37     //Parity judgment
38     assign o_clk_div1 = DIV[0] ? (w_pos_clk & w_neg_clk) : w_pos_clk;

  第三种类似第二种方式,只是方法灵活些,且只能分频2的倍数频率。4bit的计数器,每次上升沿累加1'b1,观察到bit0是2分频,bit1是4分频,bit2是8分频,bit3是16分频。代码如下:

 1     reg            [3:0]        r_div_cnt;
 2     
 3     always@(posedge i_sys_clk)
 4     begin
 5         if(~i_sys_rstn)
 6         begin
 7             r_div_cnt <= 'd0;
 8         end
 9         else
10         begin
11             r_div_cnt <= r_div_cnt + 1'b1;
12         end
13     end
14     //16 frequency divide
15     assign o_clk_div2 = r_div_cnt[3];

  第四种方式可能比较少见,但是在很多地方应用广泛,比如芯片,IP核的信号发生器等。笔者在写IP核使用该方式较多,因为频率可以通过axi4_lite更改,对于低速接口如I2C,SPI,DDS等IP核来说,频率可以根据实际情况修改增加了模块的复用性和通用性。该方式的原理是根据频率分辨率来计算需要频率的步长。比如代码中系统时钟是100MHz,计数器为32bit,那么每bit对应的频率为:100_000_000/(2**32) = 0.02328,如果需要得到1MHz的频率则步进应该为:1_000_000/0.02328 = 42_949_673。那么步进累加器就会随着累加次数发生变化,最高位即想要的频率变化。不过这种方式产生的信号是会有偏差的,不像上面的几种方法能够产生准确的50%占空比信号,因为有分辨率的存在,这种方式产生的信号占空比和频率可能会有小小的误差。

 1     reg            [31:0]        r_fre_sum;
 2     
 3     always@(posedge i_sys_clk)
 4     begin
 5         if(~i_sys_rstn)
 6         begin
 7             r_fre_sum <= 31'd0;
 8         end
 9         else
10         begin
11             r_fre_sum <= r_fre_sum + i_fre_step;
12         end
13     end
14     
15     assign o_clk_div3 = r_fre_sum[31];

  以上便是笔者常用的几种分频方式,当然还有更多,更好的方法只是笔者知识有限,只能列出知道的这几种。

下面是完整的代码及仿真代码:

  1 //**************************************************************************
  2 // *** file name      : clk_div.v
  3 // *** version        : 1.0
  4 // *** Description    : clk_div
  5 // *** Blogs          : https://www.cnblogs.com/WenGalois123/
  6 // *** Author         : Galois_V
  7 // *** Date           : 2022.4.23
  8 // *** Changes        : Initial
  9 //**************************************************************************
 10 `timescale 1ns/1ps
 11 
 12 module clk_div
 13 #(
 14     parameter            DIV        =        3
 15 )
 16 (
 17     input                         i_sys_clk     ,
 18     input                         i_sys_rstn    ,
 19 
 20     input        [31:0]           i_fre_step    ,
 21     output                        o_clk_div1    ,
 22     output                        o_clk_div2    ,
 23     output                        o_clk_div3
 24 );
 25     localparam            DIV_HALF = DIV >> 1;
 26 
 27 /******************************************************************************\
 28 Method 1,DIV frequency divide
 29 \******************************************************************************/
 30     reg            [15:0]       r_pos_cnt;
 31     reg            [15:0]       r_neg_cnt;
 32     wire                        w_pos_end;
 33     wire                        w_neg_end;
 34     wire                        w_pos_clk;
 35     wire                        w_neg_clk;
 36 
 37     always@(posedge i_sys_clk)
 38     begin
 39         if(~i_sys_rstn | w_pos_end)
 40         begin
 41             r_pos_cnt <= 'd0;
 42         end
 43         else
 44         begin
 45             r_pos_cnt <= r_pos_cnt + 1'b1;
 46         end
 47     end
 48 
 49     assign w_pos_end = (r_pos_cnt == DIV - 1);
 50     assign w_pos_clk = (r_pos_cnt < DIV_HALF) ? 0 : 1;
 51 
 52     always@(negedge i_sys_clk)
 53     begin
 54         if(~i_sys_rstn | w_neg_end)
 55         begin
 56             r_neg_cnt <= 'd0;
 57         end
 58         else
 59         begin
 60             r_neg_cnt <= r_neg_cnt + 1'b1;
 61         end
 62     end
 63 
 64     assign w_neg_end = (r_neg_cnt == DIV - 1);
 65     assign w_neg_clk = (r_neg_cnt < DIV_HALF) ? 0 : 1;
 66     //Parity judgment
 67     assign o_clk_div1 = DIV[0] ? (w_pos_clk & w_neg_clk) : w_pos_clk;
 68 
 69 /******************************************************************************\
 70 Method 2
 71 \******************************************************************************/
 72     reg            [3:0]        r_div_cnt;
 73 
 74     always@(posedge i_sys_clk)
 75     begin
 76         if(~i_sys_rstn)
 77         begin
 78             r_div_cnt <= 'd0;
 79         end
 80         else
 81         begin
 82             r_div_cnt <= r_div_cnt + 1'b1;
 83         end
 84     end
 85     //16 frequency divide
 86     assign o_clk_div2 = r_div_cnt[3];
 87 
 88 /******************************************************************************\
 89 Method 3
 90 \******************************************************************************/
 91     reg            [31:0]        r_fre_sum;
 92 
 93     always@(posedge i_sys_clk)
 94     begin
 95         if(~i_sys_rstn)
 96         begin
 97             r_fre_sum <= 31'd0;
 98         end
 99         else
100         begin
101             r_fre_sum <= r_fre_sum + i_fre_step;
102         end
103     end
104 
105     assign o_clk_div3 = r_fre_sum[31];
106 
107 endmodule

tb:

 1 //**************************************************************************
 2 // *** file name      : clk_div_tb.v
 3 // *** version        : 1.0
 4 // *** Description    : clk_div_tb
 5 // *** Blogs          : https://www.cnblogs.com/WenGalois123/
 6 // *** Author         : Galois_V
 7 // *** Date           : 2022.4.23
 8 // *** Changes        : Initial
 9 //**************************************************************************
10 `timescale 1ns/1ps
11 module clk_div_tb();
12 
13 
14     reg            clk;
15     reg            rstn;
16     
17     
18     initial
19     begin
20         clk = 0;
21         rstn = 0;
22         #2000;
23         rstn = 1'b1;
24     end
25     
26     always #5 clk = ~clk;
27     
28     clk_div 
29     #(
30         .DIV          (5            )
31     )
32     u_clk_div
33     (
34         .i_sys_clk     (clk         ),
35         .i_sys_rstn    (rstn        ),
36         .i_fre_step    (32'd42949673),//1MHz
37         .o_clk_div1    (            ),
38         .o_clk_div2    (            ),
39         .o_clk_div3    (            )
40     );
41 
42 endmodule

 

 

posted on 2022-04-23 10:58  Galois_V  阅读(1317)  评论(0编辑  收藏  举报