简析

分频器

小数分频器是分频器中最难的一种,而且不能像整数分频那样得到均匀且占空比为50%的分频输出。

小数分频

假设输出clk_out是输入clk_inNN分频。首先要将分频系数NN化为分数形式,比如4.751944.75\rightarrow \frac{19}{4}3.434103.4\rightarrow \frac{34}{10}。本题中,8.78.7可以化为8710\frac{87}{10}。这意味着在87个clk_in周期内输出10个clk_out周期就可以实现分频。

然后采用若干种(一般是两种)整数分频在87个原周期clk_in内产生10个新时钟周期clk_out。整数分频的分频系数有很多种选择,但要尽可能接近,提高clk_out的均匀度。一般推荐在小数分频系数NN的附近选取。因为8<N<98<N<9,所以两个整数分频系数是8和9。接着要计算87个clk_out周期分别有多少个是8分频和9分频的。设每10个clk_out中有xx个8分频输出和yy个9分频输出,则可列出如下方程:

{x+y=10,8x+9y=87\left\{ \begin{array}{l} x+y=10, \\ 8x+9y=87 \end{array} \right.

可得x=3x=3y=7y=7。也就是3个8分频和7个9分频一组,循环输出,就等效于8.7分频。 最后安排组内8分频和9分频的位置。这里的方法也不固定,不过本题要求3个8分频先输出,再输出7个9分频,如下图。

参数介绍

题目提供的

  • M_N=87。一组clk_out输出需要的clk_in时钟数量。
  • c89=24。切换分频系数的时间点。从这里可以看出,本题要求先输出3个8分频。
  • div_e=8。分频系数1。
  • div_o=9。分频系数2。

自定义的

  • cyc_cnt。对clk_in进行计数,达到M_N后清零。
  • div_flag。8/9分频标志。当div_flag==0时是8分频;当div_flag==1时是9分频。cyc_cnt==M_N-1或者cyc_cnt==c89-1时该标志位翻转。
  • clk_cnt。用于产生分频输出。当div_flag==0时,计数最大值是div_e-1;当div_flag==1时,计数最大值是div_o-1
  • clk_out_r。根据clk_cntdiv_flag产生分频输出。

代码

`timescale 1ns/1ns

module div_M_N(
     input  wire clk_in,
     input  wire rst,
     output wire clk_out
);
    parameter M_N = 8'd87; 
    parameter c89 = 8'd24;  // 8/9时钟切换点
    parameter div_e = 5'd8; //偶数周期
    parameter div_o = 5'd9; //奇数周期
//*************code***********//
    reg [3:0] clk_cnt;
    reg [6:0] cyc_cnt;
    reg div_flag;
    reg clk_out_r;
    
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            clk_cnt <= 0;
        else if(~div_flag)
            clk_cnt <= clk_cnt==(div_e-1)? 0: clk_cnt+1;
        else
            clk_cnt <= clk_cnt==(div_o-1)? 0: clk_cnt+1;
    end
    
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            cyc_cnt <= 0;
        else
            cyc_cnt <= cyc_cnt==(M_N-1)? 0: cyc_cnt+1;
    end
    
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            div_flag <= 0;
        else
            div_flag <= cyc_cnt==(M_N-1)||cyc_cnt==(c89-1)? ~div_flag: div_flag;
    end
    
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            clk_out_r <= 0;
        else if(~div_flag)
            clk_out_r <= clk_cnt<=((div_e>>2)+1);
        else
            clk_out_r <= clk_cnt<=((div_o>>2)+1);
    end
    
    assign clk_out = clk_out_r;
//*************code***********//
endmodule