集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 2672|回复: 1

基于Verilog的奇数偶数小数分频器设计

[复制链接]
fpga_feixiang 发表于 2017-5-18 11:40:26 | 显示全部楼层 |阅读模式
一:背景

      前天,组长交待一个任务,关于光纤通道时钟同步模块的设计。里面需要用到一个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   
 楼主| fpga_feixiang 发表于 2024-6-28 14:11:53 | 显示全部楼层
6                    
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2024-11-23 11:38 , Processed in 0.058092 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表