1. 偶分頻
1.1 寄存器級聯法
實現偶數分頻,例如二分頻、四分頻,占空比為50%。
//2/4分頻(任意偶數分頻),要求50%占空比 module clk_div2(clk, rstn, clk2, clk4); input clk, rstn; output reg clk2, clk4; always @(posedge clk or negedge rstn) begin if (!rstn) clk2 <= 0; else clk2 <= ~clk2; end always @(posedge clk2 or negedge rstn) begin if (!rstn) clk4 <= 0; else clk4 <= ~clk4; end endmodule
具體時序圖如下:
從0開始計數至N/2-1,可得到任意偶數N分頻時鍾,占空比為50%。
//6分頻(任意偶數分頻),要求50%占空比 module clk_div2(clk, rstn, clkn); input clk, rstn; output reg clkn; parameter N = 6; parameter WD = $clog2(N); reg [WD-1:0] count; always @(posedge clk or negedge rstn) begin if (!rstn || count == N/2-1) count <= 0; else count <= count + 1'b1; end always @(posedge clk or negedge rstn) begin if (!rstn) clkn <= 0; else if (count == N/2 - 1) clkn <= ~clkn; else clkn <= clkn; end endmodule
例如N=6,得到6分頻時序圖如下:
若需要占空比不滿足50%的6分頻電路,可使用計數器/狀態機,在定義的6個計數狀態中,選擇某幾個狀態輸出時鍾為1,其余為0,以控制特殊占空比。
2. 奇分頻
2.1 q != 50%
不需要占空比50%的奇分頻,例如三分頻,采用計數器法,每次遇到count=0或1時翻轉。
//3分頻(任意奇數分頻),不要求50%占空比 module clk_div3(clk, rstn, clk3); input clk, rstn; output reg clk3; parameter N = 3; parameter WD = $clog2(N); reg [WD-1:0] count; always @(posedge clk or negedge rstn) begin if (!rstn || count == N-1) count <= 'd0; else count <= count + 1'b1; end always @(posedge clk or negedge rstn) begin if (!rstn) clk3 <= 1'b0; else if (count == 'd0 || count == 'd1) clk3 <= !clk3; else clk3 <= clk3; end endmodule
可得到時序圖如下:
需要占空比50%的奇分頻,例如三分頻,需要兩個計數器,分別是上升沿計數器和下降沿計數器,執行同樣的操作,均在count=0或1時翻轉。
//N分頻(任意奇數分頻),要求50%占空比 module clk_div_odd(clk, rstn, clk_o); input clk, rstn; output clk_o; reg clk_pos, clk_neg; parameter N = 3; //parameter WD = $clog2(N); localparam WD = 31; reg [WD:0] count_pos, count_neg; assign clk_o = clk_pos || clk_neg; always @(posedge clk or negedge rstn) begin if(!rstn || count_pos == N-1) count_pos <= 0; else count_pos <= count_pos + 1; end always @(negedge clk or negedge rstn) begin if(!rstn || count_neg == N-1) count_neg <= 0; else count_neg <= count_neg + 1; end always @(posedge clk or negedge rstn) begin if(!rstn) clk_pos <= 0; else if (count_pos == N>>1 || count_pos == (N-1)) clk_pos <= ~clk_pos; else clk_pos <= clk_pos; end always @(negedge clk or negedge rstn) begin if (!rstn) clk_neg <= 0; else if (count_neg == N>>1 || count_neg == (N-1)) clk_neg <= ~clk_neg; else clk_neg <= clk_neg; end endmodule
時序圖如下:
小數分頻常采用兩種方法,使用方法二實現。
方法一:用數字鎖相環實現,利用鎖相環電路將輸入時鍾倍頻,再對新產生的高頻信號分頻得到所需頻率。例如產生8.6分頻信號,可以先把輸入時鍾10倍頻,再進行86分頻,可精確實現8.6的小數分頻。一般只在某些對信號頻率精度要求較高的場合下使用。
方法二:設計兩個不同分頻比的整數分頻器,通過控制分頻出現次數獲得所需小數分頻,但硬件電路難以實現小數計時,此處所指的是平均意義上的小數分頻。
3.1 任意小數分頻
3.1.1 整數分頻計算
例如8.6分頻,時鍾周期為10ns,得到的輸出時鍾應為5個周期有效信號430ns,推導如下:
T = 8.6 = M.N = M + \frac{b}{a+b} = \frac{Ma + (M+1)b}{a+b}
$$
其中M=8, N=6, a+b 是平均意義的時鍾周期,也即使用a個M分頻和b個M+1分頻。
\left\{ \begin{array}{**lr**} 8a+9b=43 \\ a+b=5 \end{array} \right.
$$
得到a=2,b=3,也即使用2個8分頻和3個9分頻。
該方法為雙模前置小數分頻,最重要的是以M分頻和M+1分頻兩個相近頻率實現。
再舉例實現5.3分頻,有:
T=5.3=\frac{5a+6b}{a+b}=> \left\{ \begin{array}{**ls**} 5a+6b=53 \\ a+b=10 \end{array} \right.
$$
a=7, b=3, 也即使用7個5分頻和3個6分頻。
分頻方法並不唯一,比如8.6分頻可以使用1個11分頻和4個8分頻,但得到的信號在平均周期中的每個周期有效時間差別較大,信號質量差。
同樣地,為了得到時鍾頻率較為均勻的信號,2個8分頻和3個9分頻實現的順序如下,將2次8分頻均勻插入3次9分頻中:
3.1.2 RTL+testbench
//8.6分頻,以2個8分頻和3個9分頻實現 module clk_div_fraction(clk, rst_n, clk_out); input clk, rst_n ; output reg clk_out ; reg [2:0] cnt ; reg [3:0] cnt_odd, cnt_even ; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt <= 3'd0; cnt_odd <= 4'd0; cnt_even <= 4'd0; end else if (cnt == 3'd0 || cnt == 3'd2) begin //cnt=0/2 9分頻 if (cnt_odd == 4'd8) begin cnt_odd <= 4'd0; cnt <= cnt + 1'b1; end else begin cnt_even <= 4'd0; cnt_odd <= cnt_odd + 1'b1; end end else if (cnt == 3'd4) begin //cnt=4 9分頻 if (cnt_odd == 4'd8) begin cnt_odd <= 4'd0; cnt <= 3'd0; end else begin cnt_even <= 4'd0; cnt_odd <= cnt_odd + 1'b1; end end else if (cnt == 3'd1 || cnt == 3'd3) begin //cnt=1/3 8分頻 if (cnt_even == 4'd7) begin cnt_even <= 4'd0; cnt <= cnt + 1'b1; end else begin cnt_odd <= 4'd0; cnt_even <= cnt_even + 1'b1; end end else begin cnt <= 3'd0; cnt_odd <= 4'd0; cnt_even <= 4'd0; end end always @(*) begin if (cnt_odd == 4'd8 || cnt_even == 4'd7) clk_out = 1'b1; else clk_out = 1'b0; end endmodule
testbench如下:
`timescale 1ns/1ps module clk_div_fraction_tb(); reg clk, rst_n ; wire clk_out ; clk_div_fraction u0( .clk (clk ), .rst_n (rst_n ), .clk_out (clk_out ) ); always #5 clk = ~clk; initial begin clk = 0; rst_n = 1; #15 rst_n = 0; #15 rst_n = 1; end initial begin #3000 $stop; end endmodule
仿真結果如下圖,可以看到輸出時鍾5個周期為430ns,滿足8.6分頻。
3.2 半整數仿真
上述任意小數分頻已涵蓋N.5分頻,但對於N.5分頻特殊情況,可以使用更為有效的電路結構優化縮小面積。
例如3.5分頻,使用3.1節方法需要1個4分頻和1個3分頻得到輸出時鍾,平均的概念使時鍾抖動很大,信號質量得不到保證。
用上升沿和下降沿分別產生一個7分頻的時鍾信號,兩個信號距離是3.5個時鍾周期。
//3.5分頻電路,上升沿下降沿分別產生兩個7分頻, //兩個信號距離是3.5個時鍾周期,邏輯或 module half_div(clk, rstn, clk_out); input clk, rstn; output clk_out; reg [3:0] p_cnt, n_cnt; wire p_out, n_out; always @(posedge clk or negedge rstn) begin if (!rstn) p_cnt <= 0; else if (p_cnt < 4'd6) p_cnt <= p_cnt + 1; else p_cnt <= 0; end always @(posedge clk or negedge rstn) begin if (!rstn) n_cnt <= 0; else if (n_cnt < 4'd6) n_cnt <= n_cnt + 1; else n_cnt <= 0; end assign p_out = p_cnt == 4'd0; assign n_out = n_cnt == 4'd4; assign clk_out = p_out || n_out; endmodule
testbench如下:
`timescale 1ns/1ps module half_div_tb(); reg clk, rst_n ; wire clk_out ; half_div u0( .clk (clk ), .rstn (rst_n ), .clk_out (clk_out ) ); always #5 clk = ~clk; initial begin clk = 0; rst_n = 1; #15 rst_n = 0; #15 rst_n = 1; end initial begin #4000 $stop; end endmodule
仿真結果如下圖,輸入時鍾周期10ns,得到35ns周期3.5分頻信號:
參考
【數字IC手撕代碼】Verilog半整數分頻|題目|原理|設計|仿真_myhhhhhhhh的博客-CSDN博客
以及其他資料