分頻器是指使輸出信號頻率為輸入信號頻率整數分之一的電子電路。在許多電子設備中如電子鍾、頻率合成器等,需要各種不同頻率的信號協同工作,常用的方法是以穩定度高的晶體振盪器為主振源,通過變換得到所需要的各種頻率成分,分頻器是一種主要變換手段。
早期的分頻器多為正弦分頻器,隨着數字集成電路的發展,脈沖分頻器(又稱數字分頻器)逐漸取代了正弦分頻器。
下面以Verilog HDL 語言為基礎介紹占空比為50%的分頻器。
1、偶分頻
偶分頻電路指的是分頻系數為 2、4、6、8 ... 等偶數整數的分頻電路,我們可以直接進行分頻。
例如下面 divider.v 中,對輸入時鍾進行6分頻,即假設clk 為 50MHz ,分頻后的時鍾頻率為 (50/6) MHz。程序如下:
設計代碼:

1 //rtl 2 module divider( 3 clk, 4 rst_n, 5 clk_div 6 ); 7 input clk; 8 input rst_n; 9 output clk_div; 10 reg clk_div; 11 12 parameter NUM_DIV = 6; 13 reg [3:0] cnt; 14 15 always @(posedge clk or negedge rst_n) 16 if(!rst_n) begin 17 cnt <= 4'd0; 18 clk_div <= 1'b0; 19 end 20 else if(cnt < NUM_DIV / 2 - 1) begin 21 cnt <= cnt + 1'b1; 22 clk_div <= clk_div; 23 end 24 else begin 25 cnt <= 4'd0; 26 clk_div <= ~clk_div; 27 end 28 endmodule
仿真程序:

1 //tb 2 module divider_tb(); 3 reg clk; 4 reg rst_n; 5 wire clk_div; 6 parameter DELY=100; 7 divider U_divider( 8 .clk (clk ), 9 .rst_n (rst_n ), 10 .clk_div(clk_div) 11 ); 12 always #(DELY/2) clk=~clk;//產生時鍾波形 13 initial begin 14 $fsdbDumpfile("divider_even.fsdb"); 15 $fsdbDumpvars(0,U_divider); 16 end 17 initial begin 18 clk=0;rst_n=0; 19 #DELY rst_n=1; 20 #((DELY*20)) $finish; 21 end 22 endmodule
可以看到,clk的上升沿,采樣到cnt=2的時候,就翻轉,采樣到0和1的時候,保持。這樣就可以做到一半高電平,一半低電平。
由於奇分頻需要保持分頻后的時鍾占空比為 50% ,所以不能像偶分頻那樣直接在分頻系數的一半時使時鍾信號翻轉(高電平一半,低電平一半)。
在此我們需要利用輸入時鍾上升沿和下降沿來進行設計。
clk_div1 和clk_div2 的上升沿到來時間相差半個輸入周期,所以將這兩個信號進行或操作,即可得到占空比為 50% 的5分頻時鍾。程序如下:

1 //rtl 2 module divider( 3 clk, 4 rst_n, 5 clk_div 6 ); 7 input clk; 8 input rst_n; 9 output clk_div; 10 reg clk_div; 11 12 parameter NUM_DIV = 5; 13 reg[2:0] cnt1; 14 reg[2:0] cnt2; 15 reg clk_div1, clk_div2; 16 17 always @(posedge clk or negedge rst_n) 18 if(!rst_n) 19 cnt1 <= 0; 20 else if(cnt1 < NUM_DIV - 1) 21 cnt1 <= cnt1 + 1'b1; 22 else 23 cnt1 <= 0; 24 25 always @(posedge clk or negedge rst_n) 26 if(!rst_n) 27 clk_div1 <= 1'b1; 28 else if(cnt1 < NUM_DIV / 2) 29 clk_div1 <= 1'b1; 30 else 31 clk_div1 <= 1'b0; 32 33 always @(negedge clk or negedge rst_n) 34 if(!rst_n) 35 cnt2 <= 0; 36 else if(cnt2 < NUM_DIV - 1) 37 cnt2 <= cnt2 + 1'b1; 38 else 39 cnt2 <= 0; 40 41 always @(negedge clk or negedge rst_n) 42 if(!rst_n) 43 clk_div2 <= 1'b1; 44 else if(cnt2 < NUM_DIV / 2) 45 clk_div2 <= 1'b1; 46 else 47 clk_div2 <= 1'b0; 48 49 assign clk_div = clk_div1 | clk_div2; 50 endmodule
仿真代碼:

1 //tb 2 module divider_tb(); 3 reg clk; 4 reg rst_n; 5 wire clk_div; 6 parameter DELY=100; 7 divider U_divider( 8 .clk (clk ), 9 .rst_n (rst_n ), 10 .clk_div(clk_div) 11 ); 12 always #(DELY/2) clk=~clk;//產生時鍾波形 13 initial begin 14 $fsdbDumpfile("divider_odd.fsdb"); 15 $fsdbDumpvars(0,U_divider); 16 end 17 initial begin 18 clk=0;rst_n=0; 19 #DELY rst_n=1; 20 #((DELY*20)) $finish; 21 end 22 endmodule
對其進行測試和驗證(此仿真波形是三分頻,占空比50%),即上述程序吧NUM_DIV改成3即可,得到如下波形:
3.任意占空比的任意分頻
在verilog程序設計中,我們往往要對一個頻率進行任意分頻,而且占空比也有一定的要求這樣的話,對於程序有一定的要求。
現在在前面兩個實驗的基礎上做一個簡單的總結,實現對一個頻率的任意占空比的任意分頻。
比如: FPGA系統時鍾是50M Hz,而我們要產生的頻率是880Hz,那么,我們需要對系統時鍾進行分頻。很容易想到用計數的方式來分頻:50000000/880 = 56818。
顯然這個數字不是2的整冪次方,那么我們可以設定一個參數,讓它到56818的時候重新計數就可以實現了。程序如下:
設計代碼:

1 //rtl 2 module div( 3 clk, 4 rst_n, 5 clk_div 6 ); 7 input clk,rst_n; 8 output clk_div; 9 reg clk_div; 10 11 reg [15:0] counter; 12 13 always @(posedge clk or negedge rst_n) 14 if(!rst_n) 15 counter <= 0; 16 else if(counter==56817) 17 counter <= 0; 18 else 19 counter <= counter+1; 20 21 assign clk_div = counter[15]; 22 endmodule
仿真代碼:

1 //tb 2 module div_tb(); 3 reg clk; 4 reg rst_n; 5 wire clk_div; 6 parameter DELY=100; 7 div U_div( 8 .clk (clk ), 9 .rst_n (rst_n), 10 .clk_div(clk_div) 11 ); 12 always #(DELY/2) clk=~clk;//產生時鍾波形 13 initial begin 14 $fsdbDumpfile("div_any.fsdb"); 15 $fsdbDumpvars(0,U_div); 16 end 17 initial begin 18 clk=0;rst_n=0; 19 #DELY rst_n=1; 20 #((DELY*80000)) $finish; 21 end 22 endmodule
分頻的應用很廣泛,一般的做法是先用高頻時鍾計數,然后使用計數器的某一位輸出作為工作時鍾進行其他的邏輯設計,上面的程序就是一個體現。
下面我們來算一下它的占空比:
我們清楚地知道,這個輸出波形在counter為0到32767(2的14次方)的時候為低,在32768到56817的時候為高,占空比為40%多一些,
如果我們需要占空比為50%,那么我們需要再設定一個參數,使它為56817的一半,使達到它的時候波形翻轉,就可以實現結果了。
程序如下:28408=56818/2-1,計數到28408就清零,翻轉,其余的計數期間,保持不變。
設計代碼:

1 //rtl 2 module div( 3 clk, 4 rst_n, 5 clk_div 6 ); 7 input clk,rst_n; 8 output clk_div; 9 reg clk_div; 10 reg [14:0] counter; 11 always @(posedge clk or negedge rst_n) 12 if(!rst_n) 13 counter <= 0; 14 else if(counter==28408) 15 counter <= 0; 16 else 17 counter <= counter+1; 18 19 always @(posedge clk or negedge rst_n) 20 if(!rst_n) 21 clk_div <= 0; 22 else if(counter==28408) 23 clk_div <= ~clk_div; 24 endmodule
仿真代碼:

1 //tb 2 module div_tb(); 3 reg clk; 4 reg rst_n=0; 5 wire clk_div; 6 parameter DELY=100; 7 div U_div( 8 .clk (clk ), 9 .rst_n (rst_n), 10 .clk_div(clk_div) 11 ); 12 always #(DELY/2) clk=~clk;//產生時鍾波形 13 initial begin 14 $fsdbDumpfile("div_any.fsdb"); 15 $fsdbDumpvars(0,U_div); 16 end 17 initial begin 18 clk=0;rst_n=0; 19 #DELY rst_n=1; 20 #((DELY*80000)) $finish; 21 end 22 endmodule

1 //rtl 2 module div( 3 clk, 4 rst_n, 5 clk_div, 6 counter 7 ); 8 input clk,rst_n; 9 output clk_div; 10 reg clk_div; 11 output [15:0] counter; 12 reg [15:0] counter; 13 14 always @(posedge clk) 15 if(!rst_n) 16 counter <= 0; 17 else if(counter==56817) 18 counter <= 0; 19 else counter <= counter+1; 20 21 always @(posedge clk) 22 if(!rst_n) 23 clk_div <= 0; 24 else if(counter<17045) 25 clk_div <= 1; 26 else 27 clk_div <= 0; 28 endmodule
仿真代碼:

1 //tb 2 module div_tb(); 3 reg clk; 4 reg rst_n; 5 wire clk_div; 6 wire [15:0] counter; 7 parameter DELY=100; 8 div U_div( 9 .clk (clk ), 10 .rst_n (rst_n ), 11 .counter(counter), 12 .clk_div(clk_div) 13 ); 14 always #(DELY/2) clk=~clk;//產生時鍾波形 15 initial begin 16 $fsdbDumpfile("div_any.fsdb"); 17 $fsdbDumpvars(0,U_div); 18 end 19 initial begin 20 clk=0;rst_n=0; 21 #DELY rst_n=1; 22 #((DELY*80000)) $finish; 23 end 24 endmodule
通過以上的學習,對分頻器有了比較深刻的認識,將在以后的學習中會有廣泛的應用。
原出處:https://www.chipist.cn/article/166 如有什么疑問,歡迎討論:QQ:447574829