参考链接:
小数分频基本原理
attention
注意这样实现的小数分频只有统计意义,并不是真正意义上的小数分频
info
不规整的小数分频不能做到分频后的每个时钟周期都是源时钟周期的小数分频倍,更不能做到分频后的时钟占空比均为 50%,因为 Verilog 不能对时钟进行小数计数。和半整数分频中第一次分频时引入的"平均频率"概念类似,小数分频也是基于可变分频和多次平均的方法实现的。
例如进行 7.6 倍分频,则保证源时钟 76 个周期的时间等于分频时钟 10 个周期的时间即可。此时需要在 76 个源时钟周期内进行 6 次 8 分频,4 次 7 分频。再例如进行 5.76 分频,需要在 576 个源时钟周期内进行 76 次 6 分频,24 次 5 分频。
下面阐述下这些分频参数的计算过程。
当进行 7 分频时,可以理解为 70 个源时钟周期内进行 10 次 7 分频。在 76 个源时钟周期内仍然进行 10 次分频,相当于将多余的 6 个源时钟周期增加、分配到 70 个源时钟周期内,即完成了 7.6 倍分频操作。分频过程中必然有 6 个分频时钟是 8 分频得到的,剩下的 4 个分频时钟则仍然会保持原有的 7 分频状态。可以用方程来表示
7N+8M=76N+M=10
RTL实现
实现方法一
以 7.6 倍分频为例,7 分频和 8 分频的实现顺序一般有以下 4 种:
- (1) 先进行 4 次 7 分频,再进行 6 次 8 分频
- (2) 先进行 6 次 8 分频,再进行 4 次 7 分频
- (3) 将 4 次 7 分频平均的插入到 6 次 8 分频中
- (4) 将 6 次 8 分频平均的插入到 4 次 7 分频中
info
下面以先进行 6 次 8 分频,再进行 4 次 7 分频为例子,实现rtl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| module div_M_N( input wire clk_in, input wire rst, output wire clk_out ); parameter M_N = 8'd76; parameter c89 = 8'd48; parameter div_e = 5'd8; parameter div_o = 5'd7; localparam CNTWIDTH = $clog2(M_N); localparam DELAY_W = div_e > div_o ? $clog2(div_e) : $clog2(div_o); localparam E_DIV_HALF = div_e >> 1; localparam O_DIV_HALF = div_o >> 1;
reg [CNTWIDTH-1: 0] cnt ; reg [DELAY_W-1: 0] delay_cnt; reg clk_out_reg; wire div_flag;
assign div_flag = (cnt < c89) ? 1'b0 : 1'b1; assign clk_out = clk_out_reg;
always @(posedge clk_in or negedge rst) begin if(!rst) cnt <= {CNTWIDTH{1'b0}}; else if(cnt == M_N - 1) cnt <= {CNTWIDTH{1'b0}}; else cnt <= cnt + 1'b1; end
always @(posedge clk_in or negedge rst)begin if(!rst) delay_cnt <= {DELAY_W{1'b0}}; else if(!div_flag && delay_cnt == div_e - 1) delay_cnt <= {DELAY_W{1'b0}}; else if(div_flag && delay_cnt == div_o - 1) delay_cnt <= {DELAY_W{1'b0}}; else delay_cnt <= delay_cnt + 1'b1; end
always @(posedge clk_in or negedge rst)begin if(!rst) clk_out_reg <= 1'b0; else if(!div_flag && (delay_cnt == 0 || delay_cnt == E_DIV_HALF)) clk_out_reg <= ~clk_out_reg; else if(div_flag && (delay_cnt == 0 || delay_cnt == O_DIV_HALF)) clk_out_reg <= ~clk_out_reg; else clk_out_reg <= clk_out_reg; end
endmodule
|
attention
在上述实现的rtl中,先进行偶数分频,再进行奇数分频,需要自己计算对应的值
实现方法二
前面方法时钟频率不均匀,相位抖动较大,所以一般会采用后两种平均插入的方法进行小数分频操作。平均插入可以通过分频次数差累计的方法实现,7.6 分频的实现过程如下:
- 第一次分频次数差值为 76-10*7=6 < 10,第一次进行 7 分频
- 第二次差值累加结果为 6+6=12 > 10,第二次使用 8 分频,同时差值修改为 12-10=2
- 第三次差值累加结果为 2+6=8 < 10,第三次使用 7 分频
- 第四次差值累加结果为 8+6=14 > 10,第四次使用 8 分频,差值修改为 14-10=4
| 分频次数 |
差值累加 |
差值修改 |
分频周期 |
| 1 |
6 |
6 |
7 |
| 2 |
6+6=12 |
2 |
8 |
| 3 |
2+6=8 |
8 |
7 |
| 4 |
8+6=14 |
4 |
8 |
| 5 |
4+6=10 |
0 |
8 |
| 6 |
6 |
6 |
7 |
| 7 |
6+6=12 |
2 |
8 |
| 8 |
2+6=8 |
8 |
7 |
| 9 |
8+6=14 |
4 |
8 |
| 10 |
4+6=10 |
0 |
8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| module frac_divisor #( parameter SOURCE_NUM = 76 , parameter DEST_NUM = 10 ) ( input rst_n , input clk_in, output clk_out ); localparam DIV_0 = SOURCE_NUM / DEST_NUM; localparam DIV_1 = DIV_0 + 1; localparam DIFF_INIT = SOURCE_NUM - DIV_0 * DEST_NUM ;
localparam DELAY_W = $clog2(DIV_1);
reg [DELAY_W-1: 0] delay_cnt_e; reg [DELAY_W-1: 0] delay_cnt; reg clk_out_reg; reg [ 3: 0] diff_cnt_reg;
assign clk_out = clk_out_reg;
always @(posedge clk_in or negedge rst_n)begin if(!rst_n) delay_cnt <= {DELAY_W{1'b0}}; else if(delay_cnt == (delay_cnt_e) && delay_cnt_e != 0) delay_cnt <= {DELAY_W{1'b0}}; else delay_cnt <= delay_cnt + 1'b1; end
always @(posedge clk_in or negedge rst_n)begin if(!rst_n) clk_out_reg <= 1'b0; else if(delay_cnt == 0 || delay_cnt == ((delay_cnt_e + 1) >> 1)) clk_out_reg <= ~clk_out_reg; else clk_out_reg <= clk_out_reg; end
always @(posedge clk_in or negedge rst_n)begin if(!rst_n) diff_cnt_reg <= {DELAY_W{1'b0}}; else if(delay_cnt == delay_cnt_e)begin if(diff_cnt_reg >= 10 - DIFF_INIT) diff_cnt_reg <= diff_cnt_reg + DIFF_INIT - 10; else diff_cnt_reg <= diff_cnt_reg + DIFF_INIT; end else diff_cnt_reg <= diff_cnt_reg; end
always @(posedge clk_in or negedge rst_n)begin if(!rst_n) delay_cnt_e <= {DELAY_W{1'b0}}; else if(delay_cnt == delay_cnt_e)begin if(diff_cnt_reg < 10 - DIFF_INIT) delay_cnt_e <= DIV_0 - 1'b1; else delay_cnt_e <= DIV_1 - 1'b1; end else delay_cnt_e <= delay_cnt_e; end endmodule
|
仿真波形如下所示:
