Verilog設計分頻器(面試必看)


分頻器是指使輸出信號頻率為輸入信號頻率整數分之一的電子電路。在許多電子設備中如電子鍾、頻率合成器等,需要各種不同頻率的信號協同工作,常用的方法是以穩定度高的晶體振盪器為主振源,通過變換得到所需要的各種頻率成分,分頻器是一種主要變換手段。
    早期的分頻器多為正弦分頻器,隨着數字集成電路的發展,脈沖分頻器(又稱數字分頻器)逐漸取代了正弦分頻器。

下面以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
View Code

 

仿真程序:

 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 
View Code

可以看到,clk的上升沿,采樣到cnt=2的時候,就翻轉,采樣到0和1的時候,保持。這樣就可以做到一半高電平,一半低電平。

2、奇分頻
  由於奇分頻需要保持分頻后的時鍾占空比為 50% ,所以不能像偶分頻那樣直接在分頻系數的一半時使時鍾信號翻轉(高電平一半,低電平一半)。
    在此我們需要利用輸入時鍾上升沿和下降沿來進行設計。
      接下來我們設計一個 5 分頻的模塊,設計思路如下:
     采用計數器 cnt1 進行計數,在時鍾上升沿進行加 1 操作,計數器的值為 0、1 時,輸出時鍾信號 clk_div 為高電平;計數器的值為2、3、4 時,輸出時鍾信號 clk_div 為低電平,計數到 5 時清零,從頭開始計數。我們可以得到占空比為 40% 的波形 clk_div1。
       采用計數器 cnt2進行計數,在時鍾下降沿進行加 1 操作,計數器的值為 0、1 時,輸出時鍾信號 clk_div 為高電平;計數器的值為2、3、4 時,輸出時鍾信號 clk_div 為低電平,計數到 5 時清零,從頭開始計數。我們可以得到占空比為 40% 的波形 clk_div2。
       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
View Code

仿真代碼:

 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
View Code

對其進行測試和驗證(此仿真波形是三分頻,占空比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
View Code

仿真代碼:

 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
View Code

分頻的應用很廣泛,一般的做法是先用高頻時鍾計數,然后使用計數器的某一位輸出作為工作時鍾進行其他的邏輯設計,上面的程序就是一個體現。
  下面我們來算一下它的占空比:
  我們清楚地知道,這個輸出波形在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
View Code

仿真代碼:

 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
View Code
繼續讓我們來看如何實現任意占空比,比如還是由50M分頻產生880Hz,而分頻得到的信號的占空比為30%。
56818×30%=17045
設計代碼:
 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
View Code

仿真代碼:

 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
View Code
4 小結
 通過以上幾個例子對比不難發現,借助計數器來實現任意點空比的任意分頻的方法簡單,且用verilog語言進行行為描述時,代碼簡潔、易懂、通用。
 通過以上的學習,對分頻器有了比較深刻的認識,將在以后的學習中會有廣泛的應用。

原出處:https://www.chipist.cn/article/166  如有什么疑問,歡迎討論:QQ:447574829

 


免責聲明!

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



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