基於Verilog的偶數、奇數、半整數分頻以及任意分頻器設計


在FPGA的學習過程中,最簡單最基本的實驗應該就是分頻器了。由於FPGA的晶振頻率都是固定值,只能產生固定頻率的時序信號,但是實際工程中我們需要各種各樣不同頻率的信號,這時候就需要對晶振產生的頻率進行分頻。比如如果FPGA芯片晶振的頻率為50MHz,而我們希望得到1MHz的方波信號,那么就需要對晶振產生的信號進行50分頻。

分頻器的設計雖然是FPGA學習過程中最簡單的實驗,但是真正想要把分頻器的來龍去脈弄清楚,還是需要花費一番功夫的。下面先介紹一下最常見的幾種分頻器寫法: 

1.偶數分頻器

相信大多數朋友在學習FPGA過程中接觸到的第一個實驗應該就是偶數分頻器了,偶數分頻器的設計較為簡單,用一個簡單的計數器就可以實現。比如要實現一個N分頻(N為偶數)的分頻器,可以先寫一個計數器,當計數到(N/2-1)時,讓輸出狀態翻轉,並將計數器清零,這樣輸出的信號就是輸入時鍾的N分頻了。具體代碼如下:

偶數分頻器示例,20分頻即N=20,占空比50%

module clk_div(clk_out, clk, rst_n); input clk, rst_n; output clk_out; reg clk_out; reg [4:0] cnt; always @(posedge clk or negedge rst_n) if(!rst_n) begin
 cnt <= 5'b0;
    clk_out <= 1'b0;
    end
else if(cnt == 4'd9)
    begin
 cnt <= 5'b0;
    clk_out <= ~clk_out; end
else cnt <= cnt + 1'b1;

endmodule
 
2.奇數分頻器
 

奇數分頻器的設計比偶數分頻器復雜一些,特別是占空比為50%的奇數分頻器。如果對占空比沒有明確的要求,則可以直接對上升沿計數,計數到(N-1)/2 時讓輸出翻轉,計數到(N-1)時讓輸出狀態再次翻轉,並將計數器清零,這樣就可以得到一個占空比為2:3的N分頻(N為奇數)的分頻器。而如果要實現50%的占空比,可以通過“錯位相或”的方法實現。具體方法是用剛才的方法先通過對上升沿計數產生一個占空比為不是50%的N分頻器,再用同樣的方法對下降沿計數產生一個占空比也不是50%的N分頻器,最后將這兩個分頻器的輸出進行“或”運算,就可以得到占空比為50%的奇數N分頻器,具體實現代碼如下:

 
奇數分頻器示例,5分頻,占空比50%
module div_odd ( input clk, input rst_n, output clk_out ); //----------count the posedge---------------------
reg [2:0] cnt_p; reg clk_p; always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_p <= 3'd0;
else if(cnt_p == 3'd4)
    cnt_p <= 3'd0;
else cnt_p <= cnt_p + 1'b1;

always @ (posedge clk or negedge rst_n) if(!rst_n) clk_p <= 1'b0;
else if((cnt_p == 3'd2) || (cnt_p == 3'd4)) clk_p <= ~ clk_p; //--------------------------------------------- //----------count the negedge------------------
reg [2:0] cnt_n; reg clk_n; always @ (negedge clk or negedge rst_n) if(!rst_n) cnt_n <= 3'd0;
else if(cnt_n == 3'd4) 
    cnt_n <= 3'd0;
else
 cnt_n <= cnt_n + 1'b1;
    
always @ (negedge clk or negedge rst_n) if(!rst_n) clk_n <= 1'b0;
else if((cnt_n == 3'd2) || (cnt_n == 3'd4)) clk_n <= ~clk_n; //----------------------------------------------

assign clk_out = clk_p | clk_n; endmodule

 

here is the old version :

module div_odd(clk_out, clk, rst_n); input clk, rst_n; oput clk_out; reg clk_p, clk_n; reg [4:0] cnt1, cnt2;  //注意根據實際需要調整位寬

parameter N = 5;  //此處N可以設為任意奇數 //用上升沿產生非50%占空比的分頻信號clk_p
always @(posedge clk or negedge rst_n) if(!rst_n) begin
 cnt1 <= 0; clk_p <= 0; end
else if(cnt1 == 5'b10) //cnt_p == (N-1)/2,翻轉
    begin
 cnt1 <= cnt1 + 1'b1;
    clk_p <= ~clk_p; end
else if(cnt1 == 5'b100) //cnt_p == N-1,翻轉
    begin
 cnt1 <= 1'b0;
    clk_p <= ~clk_p; end
else cnt1 <= cnt1 +1'b1;

//用下降沿產生非50%占空比的分頻信號clk_n
always @(negedge clk or negedge rst_n) if(!rst_n) begin
 cnt2 <= 0; clk_n <= 0; end
else if(cnt2 == 5'b10) //cnt_n == (N-1)/2,翻轉
    begin
 cnt2 <= cnt2 + 1'b1;
    clk_n <= ~clk_n; end
else if(cnt2 == 5'b100) //cnt_n == N-1,翻轉
    begin
 cnt2 <= 1'b0;
    clk_n <= ~clk_n; end
else cnt2 <= cnt2 +1'b1;

//相與運算,得到50%占空比的分頻信號
assign clk_out = clk_p | clk_n; endmodule
3.半分頻器(N+0.5分頻)
   在實際工程中,我們還經常會遇到半分頻器。比如要得到2MHz的時鍾信號,而系統晶振頻率為25MHz,這時候就需要對系統時鍾作12.5分頻。那么這種半分頻器又該如何實現呢?最直接的辦法當然還是用計數器了,由於半整數分頻無法實現50%的占空比(因為50%占空比就要求一個周期內高低電平都是6.25個系統時鍾周期,這個0.25是不可能實現的),我們只能讓占空比盡可能接近50%。以12.5分頻為例,可以對系統時鍾計數,在前6.5個周期輸出低電平,后6個周期輸出高電平,依次循環,就可以實現12.5分頻,占空比為(6.5/12.5),接近50%。在計數時涉及到0.5個周期,因此對上升沿和下降沿都要計數。具體代碼如下:
 
半分頻器,以12.5分頻為例,占空比(6.5/12.5)
module clk_half(clk_out, clk1, clk, rst_n); input clk,rst_n; output clk_out,clk1; parameter N = 13; //以12.5分頻為例,N=13

wire clk1; reg clk_out; reg[4:0] cnt; reg flag = 1'b0;

//系統時鍾clk計數器
always @(negedge clk or negedge rst_n) if(!rst_n) flag <= 1'b0;
else if(cnt == 5'd6) flag <= ~flag;

//在第五個時鍾結束后立即將 clk1 狀態翻轉
assign clk1 = (flag)? ~clk:clk; //時鍾 clk1 計數器,模為N
always @(posedge clk1 or negedge rst_n) if(!rst_n) cnt <= 5'b0;
else if(cnt == 5'd12) cnt <= 5'b0; else cnt <= cnt + 1'b1;

//前6.5個周期為低電平,后6個周期為高電平, //即為12.5分頻
always @(posedge clk1 or negedge rst_n) if(!rst_n) clk_out <= 1'b0;
else if(cnt == 5'd0) clk_out <= 1'b0; else if(cnt == 5'd7) clk_out <= 1'b1; else clk_out <= clk_out; endmodule
網上還有許多其他大神寫的半分頻程序,比如:
http://www.cnblogs.com/yuzeren48/p/3965003.html

 
4.任意分頻——基於相位累加原理

相位累加器主要用在直接數字頻率合成器(DDS)中,其中的幾個主要的參數為輸入頻率fc,輸出頻率fo,計數器位寬N,頻率控制字K(即計數器遞增步長)。它們之間的關系為:fo=(fc*K)/(2^N)。假設輸入頻率fc為50MHz,計數器位寬N為32,要產生1kHz的信號,則K=(fo*2^N)/fc=85.9*fo=85900。當計數值小於等於((2^N)/2)時,輸出低電平,當計數值大於((2^N)/2)時,輸出高電平,依次循環,就可以產生占空比為50%的1kHz信號了。據此可以設計如下程序:

 
任意分頻示例,輸出1kHz,占空比50%
/*************************************** 晶振頻率 fc = 50MHz 輸出頻率 fo = 1kHz(根據需要可以設為任意值) 控制參數 K = (fo*2^N)/fc 參數 N = 2^32,(32為計數器的位寬) ****************************************/
module div_free(clk_out, clk, rst_n); input clk, rst_n; output clk_out; reg clk_out; reg [31:0] cnt; always @(posedge clk or negedge rst_n) if(!rst_n) cnt <= 0; else cnt <= cnt + 32'd85900; //計數器步長 K

always @(posedge clk or negedge rst_n) if(!rst_n) begin
 clk_out <= 1'b0;
    end
else if(cnt < 32'h7FFF_FFFF)
    clk_out <= 1'b0;
else clk_out <= 1'b1;


endmodule


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM