zoukankan      html  css  js  c++  java
  • 基于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分频

    六:总结

          看到这个时候,如果您还记得我在开头说过要作一首诗,那么请您一定要关注“硬件为王”这个微信公共号(二维码见最下方),因为您是不折不扣的逻辑设计分析师。如果您已经忘了这个事了,很可能您只是百度进来抄代码的,那也请您关注“硬件为王”,因为我们会定期放出一些有用的代码和相关知识,上百度找总不如直接推送到手机上来的方便吧。

          谢谢各位看官,请求大家多多支持并随时给我们提出宝贵意见!

  • 相关阅读:
    [BJOI2019] 光线
    C# 从零开始写 SharpDx 应用 笔刷
    BAT 脚本判断当前系统是 x86 还是 x64 系统
    BAT 脚本判断当前系统是 x86 还是 x64 系统
    win2d 通过 CanvasActiveLayer 画出透明度和裁剪
    win2d 通过 CanvasActiveLayer 画出透明度和裁剪
    PowerShell 拿到显卡信息
    PowerShell 拿到显卡信息
    win10 uwp 如何使用DataTemplate
    win10 uwp 如何使用DataTemplate
  • 原文地址:https://www.cnblogs.com/yuzeren48/p/3965003.html
Copyright © 2011-2022 走看看