今天呢,由泡泡鱼工做室发布的微信公共号“硬件为王”(微信号: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分频
六:总结
看到这个时候,若是您还记得我在开头说过要做一首诗,那么请您必定要关注“硬件为王”这个微信公共号(二维码见最下方),由于您是彻彻底底的逻辑设计分析师。若是您已经忘了这个事了,极可能您只是百度进来抄代码的,那也请您关注“硬件为王”,由于咱们会按期放出一些有用的代码和相关知识,上百度找总不如直接推送到手机上来的方便吧。
谢谢各位看官,请求你们多多支持并随时给咱们提出宝贵意见!