數字分頻器設計(偶數分頻、奇數分頻、小數分頻、半整數分頻、狀態機分頻|verilog程式碼|Testbench|模擬結果)

陸路慧發表於2023-05-14

請在此新增圖片描述



數字IC經典電路設計

經典電路設計是數字IC設計裡基礎中的基礎,蓋大房子的第一部是打造結實可靠的地基,每一篇筆者都會分門別類給出設計原理、設計方法、verilog程式碼、Testbench、模擬波形。然而實際的數字IC設計過程中考慮的問題遠多於此,透過本系列希望大家對數字IC中一些經典電路的設計有初步入門瞭解。能力有限,紕漏難免,歡迎大家交流指正。

快速導航連結如下:

個人主頁連結

1.數字分頻器設計

2.序列檢測器設計

3.序列發生器設計

4.序列模三檢測器設計

5.奇偶校驗器設計

6.自然二進位制數與格雷碼轉換



一、前言

作為IC設計中經典電路之一,數字分頻器在IC(積體電路)設計中有廣泛的應用。以下是數字分頻器在IC設計中的一些應用:

時鐘發生器:時鐘發生器的原理是時鐘分頻,數字分頻器可以用來將時鐘訊號分頻為所需的頻率。例如,如果需要一個1Hz的時鐘訊號,可以使用數字分頻器將10Hz的時鐘訊號分頻為1Hz,滿足模組時序要求外還可以達到降低功耗的作用。時鐘發生器是數字系統中非常重要的元件,你就說重不重要!

數字鎖相環(DLL):數字分頻器可以用於數字鎖相環的設計中,以實現時鐘的相位同步。在 IC 設計中,時鐘同步是非常重要的一部分,因為時鐘訊號的穩定性和精度直接影響到整個系統的效能和可靠性。數字鎖相環是數字系統中的一種重要的時鐘同步技術之一。你就說重不重要!

數字頻率合成器(DDS):數字分頻器可以用於數字頻率合成器的設計中,以產生所需的頻率。在頻率合成器中,數字分頻器可以用於將高頻訊號分頻為多個低頻訊號,然後透過DSP進行數字訊號處理和合成,最終生成一個高頻訊號。雖然分頻只能將高頻分解成低頻訊號,但是與DSP結合可以合成高頻訊號。可分解高頻訊號亦可合成高頻訊號,你就說重不重要!

總之,數字分頻器在IC設計中有廣泛的應用。它是數字系統中重要的元件之一,可以實現各種複雜的數字訊號處理和時鐘同步技術。它是現代電子技術中不可或缺的一部分。所以掌握數字分頻器的設計是十分重要的!

二、偶數分頻

2.1 觸發器級聯法

採用觸發器反向輸出端連線到輸入端的方式,暫存器級聯法能實現2^N的偶數分頻,具體是採用暫存器結構的電路,每當時鐘上升沿到來的時候輸出結果進行翻轉,以此來實現偶數分頻。

根據以上原理,可實現簡單的 2 分頻電路,以此為基礎進行串聯,可構成 4 分頻和8 分頻電路。電路結構如下圖所示,用 Verilog 描述時只需使用簡單的取反邏輯即可。

請在此新增圖片描述

在此基礎上可畫出2分頻、4分頻、8分頻電路的波形圖(圖由TimeGen繪製,該軟體功能實用,推薦使用),如下圖所示。

請在此新增圖片描述

2分頻設計:只需要使用基準時鐘在第1個時鐘週期輸出高電平(或低電平),在第2個時鐘週期輸出相反電平。

同理,4分頻設計:使用基準時鐘在第1、2個時鐘週期輸出高電平(或低電平),在第3、4個時鐘週期輸出相反電平。

同理,8分頻設計:使用基準時鐘在第1、2、3、4個時鐘週期輸出高電平(或低電平),在第5、6、7、8個時鐘週期輸出相反電平。

2.2 計數器法

如果偶數分頻係數過大或者暫存器級聯法無法實現對應的分頻,可以採用計數器法進行分頻,計數器法可以實現任意偶數分頻。在計數週期達到分頻係數中間數值 (N/2-1) 時進行時鐘翻轉,可保證分頻後時鐘的佔空比為 50%。
Tips:中間數值(N/2-1) 需要減1是因為從0開始計數

以六分頻為例,電路需要實現的是:計數器從0開始計數至2,計數器到0時訊號翻轉,具體的時序圖如下(圖由TimeGen繪製,該軟體功能實用,推薦使用)。

請在此新增圖片描述

因為是偶數分頻,只要對分頻係數中間數值進行迴圈計數,在對應的地方讓訊號進行反轉即可得到任意分頻的分頻器。

2.3 verilog程式碼

//偶數分頻電路設計(2分頻、4分頻、8分頻、6分頻)
//觸發器法實現2分頻、4分頻、8分頻
//計數器法實現6分頻
module clk_div_even(
    input 		rst_n,			//復位訊號
    input 		clk,			//源時鐘訊號
    output 		clk_div2,		//輸出2分頻
    output 		clk_div4,		//輸出4分頻
    output 		clk_div6,		//輸出6分頻
    output 		clk_div8		//輸出8分頻
    );

//定義4箇中間暫存器和1個計數器    
reg         clk_div2_r;
reg 		clk_div4_r;
reg 		clk_div6_r;
reg 		clk_div8_r;
reg [3:0] cnt;

//2分頻時鐘輸出模組
//源時鐘上升沿觸發,低電平非同步復位
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		//低電平復位
        clk_div2_r <= 1'b0;
    end
    else begin
        clk_div2_r <= ~clk_div2_r;		//源時鐘上升沿訊號翻轉得到2分頻時鐘
    end
end

assign clk_div2 = clk_div2_r;		//延時輸出,消除亞穩態

//4分頻時鐘輸出模組
//2分頻時鐘上升沿觸發 低電平非同步復位
always @(posedge clk_div2 or negedge rst_n) begin
    if (!rst_n) begin
        clk_div4_r <= 1'b0;
    end
    else begin
        clk_div4_r <= ~clk_div4_r;		//2分頻時鐘上升沿訊號翻轉得到4分頻時鐘
    end
end

assign clk_div4 = clk_div4_r;		//延時輸出,消除亞穩態

//8分頻時鐘輸出模組
//4分頻時鐘上升沿觸發 低電平非同步復位
always @(posedge clk_div4 or negedge rst_n) begin
    if (!rst_n) begin
        clk_div8_r <= 'b0;
    end
    else begin
        clk_div8_r <= ~clk_div8_r;		//4分頻時鐘上升沿訊號翻轉得到8分頻時鐘
    end
end
    
assign clk_div8 = clk_div8_r;		//延時輸出,消除亞穩態

//計數器模組
//源時鐘上升沿觸發,低電平非同步復位
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		//低電平復位
        cnt    <= 4'b0 ;
    end
    else if (cnt == 2) begin		//計數器從0計數,到2清零
        cnt    <= 4'b0 ;
    end
    else begin				//計數累加
        cnt    <= cnt + 1'b1 ;
    end
end

//6分頻時鐘輸出模組
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div6_r <= 1'b0;
    end
    else if (cnt == 2 ) begin		//3個週期訊號翻轉得到6分頻時鐘
        clk_div6_r <= ~clk_div6_r;
    end
end
    
assign clk_div6 = clk_div6_r;		//延時輸出,消除亞穩態

endmodule

2.4 Testbench

`timescale 1ns/1ns		//時間刻度:單位1ns,精度1ns

module clk_div_even_tb;

//訊號申明
 reg 		clk ;
 reg 		rst_n;
 wire 		clk_div2;
 wire 		clk_div4;
 wire 		clk_div6;
 wire 		clk_div8;

//定義源時鐘訊號一週期時間
parameter DIV_CLK = 10;

//復位訊號生成
initial begin
    clk = 0;		//時鐘訊號賦初值
    rst_n = 1;		//復位訊號賦初值
    #(1.5*DIV_CLK) rst_n = 0;
    #DIV_CLK rst_n = 1;
    #(30*DIV_CLK);
end

//源時鐘訊號生成
always #(DIV_CLK/2) clk = ~ clk;

//模組例化
clk_div_even u_clk_div_even
    (
    .clk         (clk),
    .rst_n       (rst_n),
    .clk_div2    (clk_div2),
    .clk_div4    (clk_div4),
    .clk_div6    (clk_div6),
    .clk_div8    (clk_div8)
    );

endmodule

2.5 模擬結果

請在此新增圖片描述

三、奇數分頻

3.1 佔空比非50%奇數分頻

若要實現N分頻(N為奇數),只需將計數器在待分頻時鐘上升沿觸發迴圈計數,計數到0時輸出時鐘翻轉,當計數到(N-1)/2後再次將輸出時鐘翻轉。

以三分頻為例,電路需要實現的是:計數器從0開始計數至2,計數器到0時且在上升沿訊號翻轉,計數器到1時且在上升沿訊號清零,具體的時序圖如下(圖由TimeGen繪製,該軟體功能實用,推薦使用)。

請在此新增圖片描述

3.2 佔空比50%奇數分頻

如果對於佔空比要求不高的話,只需要簡單地對訊號計數並且在對應的計數器位置上升沿觸發訊號翻轉即可以得到一個奇數分頻,此奇數分頻往往佔空比達不到50%的要求。

那麼如何得到一個50%佔空比的奇數分頻呢?

從50%佔空比奇數分頻波形看,訊號的翻轉對應的源時鐘訊號分別是上升沿和下降沿,但是雙邊沿觸發在電路設計的時候是不允許的。

那麼如何實現這種“類雙邊沿觸發”的效果呢?

對於50%佔空比奇數分頻,就是分別利用待分頻時鐘的上升沿觸發生成一個時鐘,然後用下降沿觸發生成另一個時鐘,然後將兩個時鐘訊號進行或/與運算得到佔空比為50%的奇數分頻。

以三分頻為例,電路需要實現的是:設計2個分別用上升、下降沿觸發的計數器cnt_p和cnt_n,設計2個分別用上升、下降沿觸發的計數器clk_p和clk_n,利用clk_p和clk_n透過或邏輯運算生成佔空比為50%的分頻時鐘,具體的時序圖如下(圖由TimeGen繪製,該軟體功能實用,推薦使用)。

請在此新增圖片描述

此處我們透過兩個計數器分別對上升沿和下降沿訊號進行翻轉,最後透過或運算得到佔空比50%的分屏訊號。

Tips:此處亦可借用與邏輯運算,對比上面的clk_p和clk_n稍稍不同,大家可以試著自己畫出對應時序圖。

3.3 Verilog程式碼

//奇數分頻電路設計(佔空比非50%的3分頻和佔空比50%的3分頻)
module clk_div_odd (  
    input 		clk,			//時鐘訊號
    input 		rst_n,			//復位訊號
    output 		clk_div3_1,		//佔空比非50%的3分頻時鐘訊號輸出
    output 		clk_div3_2		//佔空比50%的3分頻時鐘訊號輸出
    );

//定義分頻的數目
parameter N = 3;

reg [3:0] 		cnt_p;		//上升沿觸發計數器計數
reg [3:0]		cnt_n;		//下降沿觸發計數器計數
reg       		clk_p;		//上升沿觸發生成的時鐘訊號
reg       		clk_n;		//下降沿觸發生成的時鐘訊號

//上升沿觸發計數器模組
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
       cnt_p <= 4'b0000;
    else if (cnt_p == N-1)		//計數器從0計數,到2清零
       cnt_p <= 4'b0000;
    else
       cnt_p <= cnt_p + 1'b1;		//計數器累加
end

//下降沿觸發計數器模組
always @(negedge clk or negedge rst_n) begin
    if(!rst_n)
       cnt_n <= 4'b0000;
    else if(cnt_n == N-1)		//計數器從0計數,到2清零
      cnt_n <= 4'b0000;
    else
      cnt_n <= cnt_n + 1'b1;		//計數器累加
end

//上升沿觸發生成的時鐘訊號模組
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
       clk_p <= 1'b0;
    else if(cnt_p == (N-1)/2)		//計數器到1且在上升沿,時鐘訊號翻轉
       clk_p <= ~clk_p;
    else if (cnt_p <= 0)			//計數器到0且在上升沿,時鐘訊號翻轉
       clk_p <= ~clk_p;
    else
       clk_p <= clk_p;				//防止latch產生
end

//下降沿觸發生成的時鐘訊號模組
always @(negedge clk or negedge rst_n)
begin
   if(!rst_n)
      clk_n <= 1'b0;
   else if (cnt_n == (N-1)/2)		//計數器到1且在上升沿,時鐘訊號翻轉
      clk_n <= ~clk_n;
   else if (cnt_n == 0)				//計數器到0且在上升沿,時鐘訊號翻轉
      clk_n <= ~clk_n;
   else
      clk_n <= clk_n;				//防止latch產生
end

//延時輸出,消除亞穩態
assign clk_div3_1 = clk_p;				//得到佔空比非50%的3分頻時鐘訊號
assign clk_div3_2 = clk_p | clk_n;		//或邏輯運算得到佔空比50%的3分頻時鐘訊號

endmodule

3.4 Testbench

`timescale 1ns/1ps		//時間刻度:單位1ns,精度1ps
module clk_div_odd_tb;

//訊號申明
reg clk;
reg rst_n;
wire clk_div3_1;		//佔空比非50%的3分頻時鐘訊號
wire clk_div3_2;		//佔空比50%的3分頻時鐘訊號

parameter DIV_CLK = 5;		//定義源時鐘訊號一週期時間

//復位訊號生成
initial begin
	clk = 0;		//時鐘訊號賦初值
    rst_n = 1;		//復位訊號賦初值
	#(3*DIV_CLK)
	rst_n = 0;
	#(6*DIV_CLK)
	rst_n = 1;
	#(20*DIV_CLK);
end

//源時鐘訊號生成
always #DIV_CLK clk = ~clk;

//模組例化
clk_div_odd u_clk_div_odd
    (.clk           (clk),
     .rst_n         (rst_n),
     .clk_div3_1    (clk_div3_1),
     .clk_div3_2    (clk_div3_2)
    );

endmodule

3.5 模擬結果

請在此新增圖片描述

四、小數分頻

4.1 雙模前置分頻法

不規整的小數分頻不能做到分頻後的每個時鐘週期都是源時鐘週期的小數分頻倍,更不能做到分頻後的時鐘佔空比均為 50%,因為 Verilog 不能對時鐘進行小數計數。

小數分頻是基於可變分頻和多次平均的方法實現的。

例如進行5.4倍分頻,則保證源時鐘54個週期的時間等於分頻時 10個週期的時間即可。此時需要在54個源時鐘週期內進行6次5分頻,4次6分頻。

T = ( Ma+(M+1)b )/ a+b,這裡我們發現組成小數分頻使用了a個M分頻和b個M+1分頻的整數分頻電路。

以 5.4 倍分頻為例:

基本思想是在54個源時鐘週期裡完成10個5.4分頻,根據前面的公式可知:有6的5分頻和4個6分頻。只要將5分頻和6分頻插入在54個源時鐘週期即可。

同時我們應當考慮分頻訊號的實現順序

5分頻和6分頻的實現順序一般有以下 4 種:

(1)先進行 6 次 5 分頻,再進行 4 次 6 分頻;

(2) 先進行 4 次 6分頻,再進行 6 次 5 分頻;

(3) 將 6 次 5 分頻平均的插入到 4 次 6 分頻中;

(4) 將 4 次 6 分頻平均的插入到 6 次 5 分頻中。

前兩種方法時脈頻率不均勻,相位抖動較大,所以一般會採用後兩種平均插入的方法進行小數分頻操作。

那又如何平均插入呢?

平均插入可以透過分頻次數差累計的方法實現,5.4 分頻的實現過程如下:

(1) 第一次分頻次數差值 54 - 10×5 = 4 < 10,第一次進行 5 分頻。

(2) 第二次差值累加結果為 4+4=8 < 10,第二次使用 5 分頻,同時差值修改為(54-10×5) + (54 -10×5) = 8 。

(3) 第三次差值累加結果為 4 + 8 = 12 > 10,第三次使用 6分頻。

(4) 第四次差值累加結果為 12 + (54-10×6) < 10,第四次使用 5 分頻。

以此類推,完成將 6 次 5分頻平均插入到 4 次 6分頻的過程

具體的時序圖如下(圖由TimeGen繪製,該軟體功能實用,推薦使用),此時相位抖動相對較小。

Tips:每一段並不是嚴格的5.4分頻(因為訊號翻轉只在邊沿觸發),而是在54個源時鐘週期平均下來有10個分頻,而且時序難以保證。且佔空比幾乎達不到50%。

請在此新增圖片描述

4.2 Verilog程式碼

//小數分頻電路設計
//雙模前置法實現5.4分頻
module clk_div_fraction
   (
    input				rst_n,		//復位訊號
    input               clk,		//時鐘訊號
    output              clk_frac	//小數分頻輸出訊號
    );
 
 //定義介於5.4分頻的5分頻和6分頻
parameter		CLK_DIV_1  =  5;
parameter    	CLK_DIV_2  =  6;
parameter    	DIFF       =  4;		//10個週期內5分頻與5.4分頻的差值


reg [3:0]            cnt_end;			//分頻插入計數器	(用於判斷插入什麼分頻)	
reg [3:0]            cnt;				//總計數器
reg                  clk_frac_r; 		//小數分頻中間暫存器訊號
reg [4:0]            diff_cnt_r;		//差值訊號
reg [4:0]            diff_cnt;			//差值訊號
wire                 diff_cnt_en= cnt == cnt_end;		//使能訊號

//差值累加邏輯模組
always @(*) begin
    if(diff_cnt_r >= 10) begin
        diff_cnt = diff_cnt_r -10 + DIFF;	//差值大於10,插入6分頻,差值減6
    end
    else begin
        diff_cnt = diff_cnt_r + DIFF;		//差值小於10,插入5分頻,差值加4
    end
end
  
// 借用暫存器延遲輸出diff_cnt_r                           
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin				//復位差值清零
        diff_cnt_r <= 0;
    end
    else if(diff_cnt_en) begin		//使能訊號高電平時,差值訊號延遲輸出
        diff_cnt_r <= diff_cnt;
    end
end

//5分頻和6分頻插入邏輯模組
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_end <= CLK_DIV_1-1 ;		//復位先插入5分頻
    end
    else if(diff_cnt >= 10) begin
        cnt_end <= CLK_DIV_2-1 ;		//差值大於10,插入6分頻
    end
    else begin
        cnt_end <= CLK_DIV_1-1 ;		//差值小於10,插入5分頻
    end
end

//
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin					//總計數器、分頻訊號訊號復位
        cnt <= 1'b0;
        clk_frac_r <= 1'b0;
    end
    else if(cnt == cnt_end) begin		//計數器到分頻插入界限點
        cnt <= 1'b0;					//總計數器清零
        clk_frac_r <= 1'b1;				//時鐘分頻訊號電平置"1"
    end
    else begin			//其他情況下,計數器累加計數、時鐘分頻訊號電平保持"0"
        cnt <= cnt + 1'b1;		
        clk_frac_r <= 1'b0;
    end
end

//延時輸出,消除亞穩態  
assign clk_frac = clk_frac_r;

endmodule

4.3 Testbench

`timescale 1ns/1ps		//時間刻度:單位1ns,精度1ps
module clk_div_fraction_tb;

//訊號申明
reg			clk;				
reg			rst_n;
wire		clk_frac;			

parameter DIV_CLK = 5;			//定義源時鐘訊號一週期時間

//復位訊號生成 
initial begin
    clk = 0;			//時鐘訊號賦初值
	rst_n = 1;			//復位訊號賦初值
	#(3*DIV_CLK)
	rst_n = 0;
	#(6*DIV_CLK)
	rst_n = 1;
	#(20*DIV_CLK);
end

//源時鐘訊號生成
always #DIV_CLK clk = ~clk;

//模組例化
clk_div_fraction u_clk_div_fraction
    (.clk             (clk),
     .rst_n           (rst_n),
     .clk_frac        (clk_frac)
    );

endmodule

4.4 模擬結果

請在此新增圖片描述

請在此新增圖片描述

把5.4小數分頻和2.6小數分頻進行比較,圖一是5.4小數分頻模擬時序圖,可以看到波形較為整齊;圖二是2.6小數分頻模擬時序圖,可以看到波形較為雜亂;

Tips:5.4小數分頻並不是每一段都是均勻的長度(即區域性不滿足小數分頻,總體滿足小數分頻)

那麼是什麼原因造成的呢?

從前面的基本原理可以知道,透過雙模前置法得到的小數分頻波形是差強人意的,5.4小數分頻透過5分頻和6分頻差值得到,2.6小數分頻透過2分頻和3分頻差值得到,同樣差一個cnt,對於2.6小數分頻的波形破壞要比5.4小數分頻要嚴重。總而言之就是:區域性不滿足小數分頻,總體滿足小數分頻。

五、半整數分頻

5.1 佔空比50%半整數分頻

對於使用小數分頻法得到的,以3.5分頻為例,需要使用一個四分頻和一個三分頻,七個週期內,輸出兩個1,但是訊號時序難以得到保障,時鐘訊號的質量得不到保證。

那有沒有新的方法可以最佳化半整數分頻呢?

可以這樣實現半整數分頻:
(1)在源時鐘上升沿分別產生由 4 個和 3 個源時鐘週期組成的 2 個分頻時鐘。

(2)在源時鐘下降沿分別產生由 4 個和 3 個源時鐘週期組成的 2 個分頻時鐘。

(3)兩個分頻時鐘做相位一個延遲半個源時鐘週期,一個提前半個源時鐘週期。將兩次產生的時鐘進行“或”操作,便可以得到週期均勻的 3.5 倍分頻時鐘。分頻波形示意圖如下所示。

請在此新增圖片描述

5.2 Verilog程式碼

//半整數分頻電路設計
module clk_div_half
    (
    input               rst_n,
    input               clk,
    output              clk_div
    );

parameter            DIV_CLK = 7;			//3.5分頻的高低電平總個數
reg [3:0]            cnt;					//總計數器
reg                  clk_p;					//上升沿觸發生成的時鐘訊號
reg                  clk_n;					//下降沿觸發生成的時鐘訊號

//計數器模組
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt <= 1'b0 ;
     end
     else if (cnt == DIV_CLK-1) begin 		//從0計數,到6清零
        cnt <= 'b0 ;
     end
     else begin
         cnt <= cnt + 1'b1 ;
     end
end

//上升沿觸發生成的時鐘訊號模組
//計數器到0和4並且在上升沿觸發訊號翻轉
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
          clk_p <= 1'b0;
     end
      else if (cnt == 0) begin					//計數器到0訊號翻轉
          clk_p <= 1;
      end
      else if (cnt == (DIV_CLK/2)+1) begin		//計數器到4訊號翻轉
          clk_p <= 1 ;
      end
      else begin
          clk_p <= 0 ;
      end 
end

//下降沿觸發生成的時鐘訊號模組
//計數器到1和4並且在下降沿觸發訊號翻轉
always@(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
         clk_n <= 1'b0 ;
    end
    else if(cnt == 1) begin						//計數器到1訊號翻轉
         clk_n <= 1 ;
    end
    else if (cnt == (DIV_CLK/2)+1 ) begin		//計數器到4訊號翻轉
         clk_n<= 1 ;
    end
    else begin
         clk_n <= 0 ;
    end
end

//或邏輯運算得到佔空比50%的3.5半整數分頻訊號
assign clk_div = clk_p | clk_n;

endmodule

5.3 Testbench

`timescale 1ns/1ps			//時間刻度:單位1ns,精度1ps
module clk_div_half_tb;

//訊號申明
reg 			clk;
reg 			rst_n;
wire 			clk_div;
  
parameter DIV_CLK0 = 5;		//定義源時鐘訊號一週期時間

//復位訊號生成 
initial begin
    clk = 0;				//時鐘訊號賦初值
	rst_n = 1;				//復位訊號賦初值
	#(3*DIV_CLK0)
	rst_n = 0;
	#(6*DIV_CLK0)
	rst_n = 1;
	#(20*DIV_CLK0);
end

//源時鐘訊號生成
always #DIV_CLK0 clk = ~clk;

//模組例化
clk_div_half u_clk_div_half
    (.clk             (clk),
     .rst_n           (rst_n),
     .clk_div         (clk_div)
    );

endmodule

5.4模擬結果

請在此新增圖片描述

六、狀態機分頻

6.1狀態機分頻

Verilog 中狀態機主要用於同步時序邏輯的設計,能夠在有限個狀態之間按一定要求和規律切換時序電路的狀態。狀態的切換方向不但取決於各個輸入值,還取決於當前所在狀態。狀態機可分為 2 類:Moore 狀態機和 Mealy 狀態機。

例如完成一個四分頻且佔空比為25%的分頻器,此時可以列出四種狀態,狀態機在四種狀態不斷切換,根據下一個輸出狀態只與當前狀態有關而與輸出無關,可以知道此分頻器可根據Moore狀態機完成。

請在此新增圖片描述

6.2 verilog程式碼

module clk_div_FSM
    (
    input wire clk,
    input wire rst_n,
    output reg clk_FSM
    );

//定義四種狀態 
parameter	S0 = 2'b00;
parameter	S1 = 2'b01;
parameter	S2 = 2'b10;
parameter	S3 = 2'b11;
 
reg [1:0]	state;			//定義目前狀態
reg [1:0]	next_state;		//下一狀態

//訊號復位模組
always @(posedge clk,negedge rst_n) begin
    if(!rst_n) begin 
        state <= S0;
    end
    else begin
        state <= next_state;
    end
end

//狀態轉換模組(相當於用狀態機寫計數器)
always @(*) begin
    case (state)
        S0:	next_state = S1;
        S1:	next_state = S2;
        S2:	next_state = S3;
        S3:	next_state = S0;
    endcase
end

//訊號輸出模組
always @(*) begin
    if(state == S0) begin
        clk_FSM = 1'b1;
    end
    else begin
        clk_FSM = 1'b0;
    end
end

endmodule

6.3 Tsetbench

`timescale 1ns/1ps
module clk_div_FSM_tb;

//訊號申明
reg				 clk;
reg 			 rst_n;
wire			 clk_FSM;

parameter DIV_CLK = 5;			//定義源時鐘訊號一週期時間

//復位訊號生成 
initial begin
    clk = 0;				//時鐘訊號賦初值
    rst_n = 1;				//復位訊號賦初值
    #(3*DIV_CLK)
    rst_n = 0;
    #(6*DIV_CLK)
    rst_n = 1;
    #(20*DIV_CLK);
end

//源時鐘訊號生成
always	#DIV_CLK clk = ~clk;

//模組例化
clk_div_FSM u_clk_div_FSM
    (.clk             (clk),
     .rst_n           (rst_n),
     .clk_FSM         (clk_FSM)
    );

endmodule

6.4模擬結果

請在此新增圖片描述

七、總結

偶數分頻:無論是透過D觸發器還是計數器實現,這類分頻都是最容易得到的,並且佔空比容易控制在50%。對於D觸發器實現偶數分頻來說,分頻數只能得2^n,其餘分頻數只能由計數器法等其他方法實現。除此以外,隨著分頻的數目不斷增大,透過D觸發器實現觸發器數目會增多,在電路設計的過程中應當考慮面積因素。對於計數器實現偶數分頻,佔空比和分頻數都可以得到極大的控制,是實現偶數分頻最靈活的一種方式。

奇數分頻:計數分頻基本原理也是透過計數器實現的,主要分為佔空比非50%的奇數分頻和佔空比50%的奇數分頻,後者實現簡單而後者稍稍複雜一些。佔空比非50%的情況下,時鐘訊號在上升沿(N-1)/2翻轉和 0翻轉即可得到需要的分頻訊號。佔空比50%的情況下,一個時鐘訊號在上升沿而一個時鐘訊號在下降沿,觸發(N-1)/2翻轉和0翻轉,然後將clk_p和clk_n做或邏輯運算即可得到佔空比50%的計數分頻訊號。從以上可以看出,佔空比50%的奇數分頻只是在佔空比非50%的奇數分頻的基礎上多做了一個邏輯運算。

小數分頻:目前小數分頻使用較多的方法是雙模前置分頻法,基本原理是在小數分頻的兩側尋找相近的分頻去插入,營造在一定的源時鐘週期走過與小數分頻相當的的時鐘週期。但是往往分頻的時序波形比較亂,佔空比幾乎達不到50%,效果差強人意,究其根本原因是訊號只在源時鐘的邊沿觸發。

半整數分頻:半整數分頻是小數分頻的特殊情況,之所以會拎出來單獨講,是因為根據小數分頻的雙模前置法做出來的波形時序較差。如果需要得50%的半整數分頻怎麼辦? 首先做出兩個上升沿下降沿二分頻訊號,透過在半整數兩邊尋找相鄰的的奇數和偶數(決定訊號電平週期數),然後做邏輯運算即可以得到佔空比50%的半整數分頻。

狀態機分頻:可以實現分頻的方式之一,對於簡單的分頻器可以採用狀態機來實現,大部分情況狀態機用來處理較為複雜的情況與問題,在分頻電路里有大材小用的感覺,電路分頻時可以採用更簡單的計數器代替狀態機的狀態轉換。

不定期檢查、補充、糾錯,歡迎隨時交流糾錯

最後修改日期:2023.5.10

軟體版本:

  • 模擬軟體:Modelsim 10.6c
  • 時序繪製軟體:TimeGen 3.2
  • 描述語言:verilog

相關文章