對于任意小數分頻,如果有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的分頻,看波形:?
的確是實現了目的,不過好亂的波形呀。