基於Verilog的奇數偶數小數分頻器設計

      今天呢,由泡泡魚工作室發佈的微信公共號“硬件爲王”(微信號:king_hardware)正式上線啦,關注有驚喜哦。在這個普天同慶的美好日子裏,小編腦洞大開,決定寫一首詩讚美一下我們背後偉大的團隊,雖然連上我只有兩個人,但絲毫不影響我們的工作熱情和創業野心。合抱之木,生於毫末;九層之臺,起於壘土;千里之行,始於足下!

      首先小編在這裏分享一個基於Verilog語言的分頻器設計,該分頻器實現了奇數、偶數、小數(0.5)分頻,可綜合,能跑700M左右的時鐘,基本能夠滿足大部分應用需求。

一:背景

      前天,組長交待一個任務,關於光纖通道時鐘同步模塊的設計。裏面需要用到一個10M的時鐘,而我的PCIe時鐘爲125M,所以需要一個12.5分頻的分頻器。小編偷懶從網上搜了一個,代碼簡潔,行爲仿真也沒問題,直接就用上了。昨天組長調用我的設計,發現綜合出現了問題,一查代碼,把我批了一通,還暫時取消了我帶小弟的資格,原因就出在這分頻器上。

二:問題代碼分析

 1 module divf #
 2 (    parameter N = 12 ,    // 分頻數
 3      parameter state=1     //奇偶分頻爲0,半分頻爲1
 4 )
 5 (
 6 input                 clr,                          
 7 input                 clk,   
 8 output                clkout
 9 );
10 
11 reg [5:0]  M;
12 reg [24:0]  count;
13 
14 always@(posedge clk or negedge clk)
15 begin
16 case(state)
17   0:begin
18       if(!clr) count<=2*N-1;
19       else if(count==2*N-1)
20                  begin 
21                        count<=0;
22                        M<=2;    //只on一個clk
23                  end
24            else count<=count+1;
25     end
26   
27   1:begin
28       if(!clr) count<=2*N;
29       else if(count==2*N)
30                  begin 
31                        count<=0;
32                        M<=N+1;
33                  end
34            else count<=count+1;
35     end 
36     
37    default:;
38 endcase
39 end
40 
41 assign clkout=(count<M)?1:0;
42 
43 endmodule

看到這樣的代碼,像我一樣的菜鳥見了都會怦然心動,但仔細分析,問題就出來了。

always@(posedge clk or negedge clk)

      觸發器(FF)一般是上升沿觸發,我做過實驗,即使想要下降沿觸發,佈局佈線後也會有一個反相器反相後用上升沿去觸發。若同時使用上升沿和下降沿觸發,例如always@(posedge clk or negedge clk),佈局佈線後等效於always@(posedge clk)。所以上面這種寫法,若不是採用特定器件如ODDR,是很難完成上下時鐘沿都採數據的(應該還有別的方法,請大牛不吝賜教)。所以如果用在高速時鐘上,建議不要採用這種寫法。

 assign clkout=(count<M)?1:0;

      組合邏輯輸出問題,如果時鐘頻率較高,100M以上,組合邏輯的延時很有可能超過時鐘的建立時間,會產生毛刺,所以我們一般都要求寄存器打一拍輸出。上面這個例子中,clkout=(count<M)?1:0; 比較器是個延時比較多的器件,所以對時鐘要求高的情況下不能使用。

 

三:解決方案

使用兩個always塊,但兩個always塊不能對同一變量進行操作。

Always@(posedge clk) begin  … end

Always@(negedge clk) begin  … end

或者使用鎖相環產生兩個頻率相同,相位差180度的clk,然後在每個上升沿輸出

Always@(posedge clk1) begin  … end

Always@(negedge clk2) begin  … end

針對組合邏輯輸出問題,能避免使用則避免使用,如果非要使用,也只能使用足夠簡單的組合邏輯,比如與或非邏輯。

 

四:代碼示例

說明:用一個大case分三類討論,看上去很挫,實際是爲了裁剪方便。

代碼功能:完成奇數分頻和偶數分頻,佔空比50%。完成n+0.5分頻,佔空比無要求。

 1 module divf #
 2 (    parameter Div_num = 12 ,    // 分頻數
 3      parameter state=0        //半分頻爲0,奇數分頻爲1,偶數分頻爲2
 4 )     
 5 (
 6 input                 clr,                          
 7 input                 clk,   
 8 output                Div_clk
 9 );
10 reg [24:0]  count;
11 
12 case(state)
13 1:   begin  //ji_shu
14           reg         pos_clk;
15           reg         neg_clk;
16           
17           always@(posedge clk or negedge clr)
18           if(!clr)                     count<=0;
19           else if(count==0 & pos_clk)  count<=Div_num/2-1;
20           else if(count==0)            count<=Div_num/2;
21           else                         count<=count-1;
22           
23           always@(posedge clk or negedge clr)
24           if(!clr)                     pos_clk<=0;
25           else if(count==0)            pos_clk<=~pos_clk;
26           else                         pos_clk<=pos_clk;
27           
28           always@(negedge clk or negedge clr)
29           if(!clr)                     neg_clk<=0;
30           else                         neg_clk<=pos_clk;
31           
32           assign Div_clk = pos_clk & neg_clk;          
33      end
34 
35 2:   begin  //ou_shu
36           reg          Div_clk1;        
37             
38           always@(posedge clk or negedge clr)                  
39           if(!clr)                     count<=0;                        
40           else if(count==0)            count<=Div_num/2-1;       
41           else                         count<=count-1; 
42 
43           always@(posedge clk or negedge clr)                  
44           if(!clr)                     Div_clk1<=0;                        
45           else if(count==0)            Div_clk1<=~Div_clk1;   
46               
47           assign Div_clk = Div_clk1; 
48      end  
49           
50           
51 0:   begin   //ban_fen_pin
52           reg         count_div;
53           reg         count_div2; 
54           wire        clk_half;
55           
56           assign  clk_half = clk^count_div2;
57           always@(posedge clk_half or negedge clr)   //模Div_num 計數            
58           if(!clr)                       count<=0;               
59           else if(count== Div_num-1)      count<=0;         
60           else                         count<=count+1; 
61  
62           always@(posedge clk_half or negedge clr)   //模Div_num 計數            
63           if(!clr)                       count_div<=0;               
64           else if(count== Div_num-1)      count_div<=1;         
65           else                         count_div<=0;  
66           
67           always@(posedge count_div or negedge clr)   //對count_div二分頻            
68           if(!clr)                       count_div2<=0;                
69           else                         count_div2<=~count_div2;  
70         
71           assign Div_clk = count_div;
72      end
73 endcase    
74 
75 endmodule

五:仿真代碼及結果

 1 module test_divf;
 2 reg clk;
 3 reg clr;
 4 wire Div_clk;
 5 
 6 always #1 clk=~clk;
 7 
 8 initial
 9 begin
10  #0 clr=0;clk=1;
11  #99 clr=1;
12  //#1000 $stop;
13 end
14 
15 divf #
16 (
17    .Div_num    (          5            ), 
18    .state      (          1            )
19 )divf(
20    .clr        (         clr           ),
21    .clk        (         clk           ),
22    .Div_clk    (        Div_clk        )
23 );  
24                                                           
25 endmodule            

仿真結果

Div_num=5,state=1,實現5分頻

Div_num=6,state=2,實現6分頻

Div_num=6,state=0,實現5.5分頻

六:總結

      看到這個時候,如果您還記得我在開頭說過要作一首詩,那麼請您一定要關注“硬件爲王”這個微信公共號(二維碼見最下方),因爲您是不折不扣的邏輯設計分析師。如果您已經忘了這個事了,很可能您只是百度進來抄代碼的,那也請您關注“硬件爲王”,因爲我們會定期放出一些有用的代碼和相關知識,上百度找總不如直接推送到手機上來的方便吧。

      謝謝各位看官,請求大家多多支持並隨時給我們提出寶貴意見!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章