1. 程式人生 > 其它 >Verilog分頻器設計_學習總結

Verilog分頻器設計_學習總結

分頻器設計_Verilog

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;
endalways @(posedge
clk2 or negedge rstn) begin if (!rstn) clk4 <= 0; else clk4 <= ~clk4; endendmodule

具體時序圖如下:

1.2 計數器法

從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; endalways @(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;
endalways @(posedge clk or negedge rstn) begin
    if (!rstn)
        clk3 <= 1'b0;
    else if (count == 'd0 || count == 'd1)
        clk3 <= !clk3;
    else 
        clk3 <= clk3;
endendmodule

可得到時序圖如下:

2.2 q == 50%

需要佔空比50%的奇分頻,例如三分頻,需要兩個計數器,分別是上升沿計數器和下降沿計數器,執行同樣的操作,均在count=0或1時翻轉。

//3分頻(任意奇數分頻),要求50%佔空比
module clk_div3(clk, rstn, clk3);
input   clk, rstn;
output  clk3;
​
reg clk_pos, clk_neg;
​
parameter N  = 3;
parameter WD = $clog2(N);
reg [WD:0] count_pos, count_neg;
​
assign clk3 = 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;
endalways @(negedge clk or negedge rstn) begin
    if(!rstn || count_neg == N-1)
        count_neg <= 0;
    else
        count_neg <= count_neg + 1;
endalways @(posedge clk or negedge rstn) begin
    if(!rstn)
        clk_pos <= 0;
    else if (count_pos == 3'd2)
        clk_pos <= clk_pos;
    else
        clk_pos <= ~clk_pos;
endalways @(posedge clk or negedge rstn) begin
    if (!rstn)
        clk_neg <= 0;       
    else if (count_neg == 3'd2)
        clk_neg <= clk_neg;
    else
        clk_neg <= ~clk_neg;
endendmodule

時序圖如下:

3. 小數分頻

小數分頻常採用兩種方法,使用方法二實現。

方法一:用數字鎖相環實現,利用鎖相環電路將輸入時鐘倍頻,再對新產生的高頻訊號分頻得到所需頻率。例如產生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
endalways @(*) begin
    if (cnt_odd == 4'd8 || cnt_even == 4'd7)
        clk_out = 1'b1;
    else
        clk_out = 1'b0;
endendmodule

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;
endinitial begin
    #3000 $stop;
endendmodule

模擬結果如下圖,可以看到輸出時鐘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;
endalways @(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;
endassign 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;
endinitial begin
    #4000 $stop;
endendmodule

模擬結果如下圖,輸入時鐘週期10ns,得到35ns週期3.5分頻訊號:

 

參考

【數字IC手撕程式碼】Verilog半整數分頻|題目|原理|設計|模擬_myhhhhhhhh的部落格-CSDN部落格

以及其他資料