在一個數字系統中往往需要多種頻率的時鍾脈沖作為驅動源,這樣就需要對FPGA的系統時鍾(頻率太高)進行分頻。分頻器主要分為奇數分頻,偶數分頻,半整數分頻和小數分頻,在對時鍾要求不是很嚴格的FPGA系統中,分頻器通常都是通過計數器的循環來實現的。
偶數分頻:假設為N分頻,由待分頻的時鍾觸發計數器計數,當計數器從0計數到N/2-1時,輸出時鍾進行翻轉,並給計數器一個復位信號,使得下一個時鍾從零開始計數。以此循環下去。這種方法可以實現任意的偶數分頻。如圖所示,兩個D觸發器級聯實現四分頻電路,原理:來一個時鍾脈沖,D端數據就被送到輸出端Q,同時輸出一個反向數據到Q非端,下一個時鍾脈沖到,重復上面過程,但數據己被取反,由此每兩個時鍾,Q端數被取反一次,由此得到二份頻,繼而得到四分頻。
Tips: D觸發器的工作原理(驗證其狀態不變,可先假定初值為0或為1,根據邏輯關系分析)
D觸發器的狀態表:CP為時鍾,R為置零端,S為置1端,D為信號輸入端,輸出信號有Q。
實現D觸發器功能的verilog代碼為
module D(q,qn,d,cp,r,s); output q,qn; //D 觸發器的兩個輸出 input d,cp,r,s; //D 觸發器的四個輸入 reg q,qn; // 輸出寄存器 always@(posedge cp) //在 在 cp 的上升沿觸發 begin if({r,s}==2'b01) // 判斷是否有 r=0,s=1 begin q=1'b0; qn=1'b1; end else if({r,s}==2'b10) // 判斷是否有 r=1,s=0 begin q=1'b1; qn=1'b0; end else if({r,s}==2'b11) // 判斷是否有 r=1 ,s=1 begin q=d; qn=~d; end end endmodule
對於分頻系數為10的分頻器,本例的輸入時鍾系統50M時鍾(clk_50M),輸出為十分頻時鍾(f_50)。設置一個3位的計數器,當計時寄存器到4(10/2-1)時,將輸出分頻信號取反即可得到10分頻的輸出。下圖分別為功能仿真和時序仿真(存在延遲)
module fengping_2(clk_50M,f_10); input clk_50M; // 系統輸入時鍾,50M ,周期 20ns output f_10; //10 分頻輸出,5M reg f_10; // 輸出寄存器 reg[2:0] cnt; // 計數寄存器 always@(posedge clk_50M) // 每個時鍾周期的上升沿觸發, // 執行 begin_end 中的語句 begin if(cnt==3'b100) // 判斷 cnt 是否為 4, 是的話執行以下程序 begin f_10<=~f_10; //把 把 f_10 取反 cnt<=3'b0; // 計數寄存器清零 end else //cnt 沒到 4 ,執行以下程序 begin cnt<=cnt+3'b1;// 計數寄存器自加一 end end endmodule
奇數分頻:首先,完全可以通過計數器來實現,如進行三分頻,通過待分頻時鍾上升沿觸發計數器進行模三計數,當計數器計數到鄰近值進行兩次翻轉,比如可以在計數器計數到1時,輸出時鍾進行翻轉,計數到2時再次進行翻轉。即是在計數值在鄰近的1和2進行了兩次翻轉。這樣實現的三分頻占空比為1/3或者2/3。如果要實現占空比為50%的三分頻時鍾,可以通過待分頻時鍾下降沿觸發計數,和上升沿同樣的方法計數進行三分頻,然后下降沿產生的三分頻時鍾和上升沿產生的時鍾進行相或運算,即可得到占空比為50%的三分頻時鍾。這種方法可以實現任意的奇數分頻。歸類為一般的方法為:對於實現占空比為50%的N倍奇數分頻,首先進行上升沿觸發進行模N計數,計數選定到某一個值進行輸出時鍾翻轉,然后經過(N-1)/2再次進行翻轉得到一個占空比非50%奇數n分頻時鍾。再者同時進行下降沿觸發的模N計數,到和上升沿觸發輸出時鍾翻轉選定值相同值時,進行輸出時鍾時鍾翻轉,同樣經過(N-1)/2時,輸出時鍾再次翻轉生成占空比非50%的奇數n分頻時鍾。兩個占空比非50%的n分頻時鍾相或運算,得到占空比為50%的奇數n分頻時鍾。
module fenpin( input i_clk, input i_rst_n, output o_clk ); // log2(3) = 1.5850 <= 2 reg [1:0] cnt_p; // 上升沿計數子 // 3位上升沿計數器: 0 ~ 2 always @ (posedge i_clk, negedge i_rst_n) begin if (!i_rst_n) cnt_p <= 0; else begin if (cnt_p == 2) //2=3-1 cnt_p <= 0; else cnt_p <= cnt_p + 1'b1; end end // log2(3) = 1.5850 <= 2 reg [1:0] cnt_n; // 下降沿計數子 // 3位下降沿計數器: 0 ~ 2 // 2 = 3 - 1 always @ (negedge i_clk, negedge i_rst_n) begin if (!i_rst_n) cnt_n <= 0; else begin if (cnt_n == 2) //2=3-1 cnt_n <= 0; else cnt_n <= cnt_n + 1'b1; end end reg o_clk_p; // 上升沿時鍾輸出寄存器 // 輸出上升沿時鍾 // 0 ~ 1 ↑-> 1 // (1+1) ~ 2 ↑-> 0 // 1 = 3>>1 // 2 = 3 - 1 always @ (posedge i_clk, negedge i_rst_n) begin if (!i_rst_n) o_clk_p <= 0; else begin if (cnt_p <= 1) // 1 = 3>>1 ,右移相當於除以2 o_clk_p <= 1; else o_clk_p <= 0; end end reg o_clk_n; // 下降沿時鍾輸出寄存器 // 輸出下降沿時鍾 // 0 ~ 1 ↓-> 1 // (1+1) ~ 2 ↓-> 0 // 1 = 3>>1 // 2 = 3 - 1 always @ (negedge i_clk, negedge i_rst_n) begin if (!i_rst_n) o_clk_n <= 0; else begin if (cnt_n <= 1) // 1 = 3>>1 o_clk_n <= 1; else o_clk_n <= 0; end end assign o_clk = o_clk_n & o_clk_p; // 按位與(作用:掩碼) endmodule