今天呢,由泡泡魚工做室發佈的微信公共號「硬件爲王」(微信號:king_hardware)正式上線啦,關注有驚喜哦。在這個普天同慶的美好日子裏,小編腦洞大開,決定寫一首詩讚美一下咱們背後偉大的團隊,雖然連上我只有兩我的,但絲絕不影響咱們的工做熱情和創業野心。合抱之木,生於毫末;九層之臺,起於壘土;千里之行,始於足下!微信
首先小編在這裏分享一個基於Verilog語言的分頻器設計,該分頻器實現了奇數、偶數、小數(0.5)分頻,可綜合,能跑700M左右的時鐘,基本可以知足大部分應用需求。佈局
一:背景spa
前天,組長交待一個任務,關於光纖通道時鐘同步模塊的設計。裏面須要用到一個10M的時鐘,而個人PCIe時鐘爲125M,因此須要一個12.5分頻的分頻器。小編偷懶從網上搜了一個,代碼簡潔,行爲仿真也沒問題,直接就用上了。昨天組長調用個人設計,發現綜合出現了問題,一查代碼,把我批了一通,還暫時取消了我帶小弟的資格,緣由就出在這分頻器上。設計
二:問題代碼分析code
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
看到這樣的代碼,像我同樣的菜鳥見了都會怦然心動,但仔細分析,問題就出來了。blog
① always@(posedge clk or negedge clk)ci
觸發器(FF)通常是上升沿觸發,我作過實驗,即便想要降低沿觸發,佈局佈線後也會有一個反相器反相後用上升沿去觸發。若同時使用上升沿和降低沿觸發,例如always@(posedge clk or negedge clk),佈局佈線後等效於always@(posedge clk)。因此上面這種寫法,若不是採用特定器件如ODDR,是很難完成上下時鐘沿都採數據的(應該還有別的方法,請大牛不吝賜教)。因此若是用在高速時鐘上,建議不要採用這種寫法。get
② assign clkout=(count<M)?1:0;input
組合邏輯輸出問題,若是時鐘頻率較高,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分頻
六:總結
看到這個時候,若是您還記得我在開頭說過要做一首詩,那麼請您必定要關注「硬件爲王」這個微信公共號(二維碼見最下方),由於您是徹徹底底的邏輯設計分析師。若是您已經忘了這個事了,極可能您只是百度進來抄代碼的,那也請您關注「硬件爲王」,由於咱們會按期放出一些有用的代碼和相關知識,上百度找總不如直接推送到手機上來的方便吧。
謝謝各位看官,請求你們多多支持並隨時給咱們提出寶貴意見!