基于Verilog的奇数偶数小数分频器设计
一:背景前天,组长交待一个任务,关于光纤通道时钟同步模块的设计。里面需要用到一个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 M;
12 reg 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 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 assignclk_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 6
页:
[1]