对于任意小数分频,如果有PLL的话,直接倍频再分频即可;或常用的方法有双模前置小数分频和脉冲删除小数分频。前一种方法设计较为复杂,因此主要以第二种方式为主设计了一下。
任意小数均可以化为分数,例如要进行5.3分频即53/10分频,因此之后全部以分数来表示。
以13/4分频为例,我们首先要想明白什么是13/4分频。什么是2分频呢?就是每两个输入时钟得到一个输出时钟,4分频就是4/1即四个输入时钟得到一个输出时钟,因此13/4分频其实就是13个输入时钟得到4个输出时钟,想明白这一点很重要。
在双模前置小数分频设计中,虽然这个设计我还没完成不过也提一下,是通过分数值的前后两个正数数分频选择输出得到最终结果的。对于13/4而言:
M = 13/4 = 3 ... 1
这意味着13/4的分频可以通过3分频和4分频选择输出得到,继续计算:
a + b = 4
3a + 4b = 13
得到a=3,b=1。也就是说通过3个3分频和1个4分频可以得到13/4分频。
在不考虑其他情况仅仅做简单选择输出的话,可以画出这样的时序图:
可以看到通过3分频和4分频“凑”出了13/4分频,不过这样的时钟我觉得用处不大。当然了真正这样设计时不能这样简单的先3个3分频再1个4分频,还有一些处理,这里就先不提了。
还是回到刚刚话,13/4分频其实就是13个输入时钟得到4个输出时钟,因此脉冲删除小数分频相对比较简单。意思就是在13个输入时钟里我删掉9个时钟周期,这样不就书粗了4个时钟周期了么,就是这样。那应该怎么删呢,查了一些论文后得到结论:
1.设置寄存器cnt位宽自定,我用的[7:0],初始值为0;
2.在clk_in的上升沿+4,并判断是否大于13,若大于13在下一周期-13;
3.cnt小于13时候,删除脉冲信号delete=1,大于13时候delete=0;
说起来比较乱,画个表表示下,每12个周期我们作为一个循环来看:
时钟序号 | cnt值 | 是否删除 |
0 | 4 | Y |
1 | 8 | Y |
2 | 12 | Y |
3 | (16->)3 | N |
4 | 7 | Y |
5 | 11 | Y |
6 | (15->)2 | N |
7 | 6 | Y |
8 | 10 | Y |
9 | (14->)1 | N |
10 | 5 | Y |
11 | 9 | Y |
12 | (13->)0 | N |
0 | 4 |
从表中可以看到每13个周期有4个周期没有被删除,刚好满足要求。
我们再来试一个,11/9吧:
时钟序号 | cnt值 | 是否删除 |
0 | 9 | Y |
1 | 18->7 | N |
2 | 16->5 | N |
3 | 14->3 | N |
4 | 12->1 | N |
5 | 10 | Y |
6 | 19->8 | N |
7 | 17->6 | N |
8 | 15->4 | N |
9 | 13->2 | N |
10 | 11->0 | N |
0 | 9 |
可以看到11个时钟里有删除了2个,输出了9个,完美。
下面以代码用进行测试设计,代码中fraction是分频的分子,denominator 是分母,以参数形式设置,testbench中进行传入。
module DIV(input clk_in ,input rst ,output clk_out);
parameter fraction = 1;
parameter denominator = 1;reg [7:0]cnt;
reg delete;
always @(posedge clk_in or posedge rst) beginif (rst) begin// resetcnt <= 0;delete <= 0;endelse if (cnt > fraction) begincnt <= cnt + denominator - fraction;delete <= 0;endelse begincnt <= cnt + denominator;delete <= 1;end
endassign clk_out = delete ? 1 : clk_in;
endmodule
testbench文件,测试21/8分频。cnt_in和cnt_out仅仅是为了方便数输入时钟和输出时钟而设置的,没有实际意义:
module tb;// Inputsreg clk_in;reg rst;// Outputswire clk_out;parameter fraction = 21;parameter denominator = 8;// Instantiate the Unit Under Test (UUT)DIV #(.fraction(fraction),.denominator(denominator))uut (.clk_in(clk_in),.rst(rst),.clk_out(clk_out));initial beginclk_in = 0;forever #2 clk_in = ! clk_in;endinitial beginrst = 1;forever #50 rst = 0;endreg [7:0]cnt_in, cnt_out;
always @(posedge clk_in or posedge rst) beginif (rst) begin// resetcnt_in <= 0;endelse if (cnt_in == fraction) begin// resetcnt_in <= 1;endelse begincnt_in <= cnt_in + 1;end
endalways @(posedge clk_out or posedge rst) beginif (rst) begin// resetcnt_out <= 0;endelse if (cnt_out == denominator) begin// resetcnt_out <= 1;endelse begincnt_out <= cnt_out + 1;end
endendmodule
仿真波形图:
从红框区域可以看得出,21个输入周期刚好输出8个输出周期,当然这计数可以不怎么看,我们自己会数嘛。
ps.
在网上还看到了另外一种方式,感觉思路有点相似,也是相加超过分子后就减去分子再相加,把实现的代码贴在这里:
module DIV(input clk_in ,input rst ,output reg clk_out);
parameter fraction = 1;
parameter denominator = 1;reg [8:0]cnt;
wire vld;assign vld = ((cnt >= fraction>>1) && (cnt < fraction)) ? 1 : 0;always @(posedge clk_in or posedge rst) beginif (rst) begin// resetcnt <= 0;endelse if(cnt > fraction) begincnt <= cnt + denominator - fraction;endelse begincnt <= cnt + denominator;end
endalways @(posedge clk_in or posedge rst) beginif (rst) begin// resetclk_out <= 0;endelse if (vld) beginclk_out <= 1;endelse beginclk_out <= 0;end
endendmodule
同样用之前的testbench做21/8的分频,看波形:
的确是实现了目的,不过好乱的波形呀。