数字分频器设计(偶数分频/奇数分频/小数分频/半整数分频/状态机分频|verilog代码|Testbench|仿真结果)

科技资讯 投稿 7100 0 评论

数字分频器设计(偶数分频/奇数分频/小数分频/半整数分频/状态机分频|verilog代码|Testbench|仿真结果)

目录
    一、前言
  • 二、偶数分频
    • 2.1 触发器级联法
    • 2.2 计数器法
    • 2.3 verilog代码
    • 2.4 Testbench
    • 2.5 仿真结果
  • 三、奇数分频
      3.1 占空比非50%奇数分频
  • 3.2 占空比50%奇数分频
  • 3.3 Verilog代码
  • 3.4 Testbench
  • 3.5 仿真结果
  • 四、小数分频
      4.1 双模前置分频法
  • 4.2 Verilog代码
  • 4.3 Testbench
  • 4.4 仿真结果
  • 五、半整数分频
      5.1 占空比50%半整数分频
  • 5.2 Verilog代码
  • 5.3 Testbench
  • 5.4仿真结果
  • 六、状态机分频
      6.1状态机分频
  • 6.2 verilog代码
  • 6.3 Tsetbench
  • 6.4仿真结果
  • 七、总结


  • 数字IC经典电路设计

    快速导航链接如下:

    个人主页链接

    2.序列检测器设计

    4.序列模三检测器设计

    6.自然二进制数与格雷码转换



    一、前言

    时钟发生器:时钟发生器的原理是时钟分频,数字分频器可以用来将时钟信号分频为所需的频率。例如,如果需要一个1Hz的时钟信号,可以使用数字分频器将10Hz的时钟信号分频为1Hz,满足模块时序要求外还可以达到降低功耗的作用。时钟发生器是数字系统中非常重要的组件,你就说重不重要!

    你就说重不重要!

    你就说重不重要!

    所以掌握数字分频器的设计是十分重要的!

    二、偶数分频

    2.1 触发器级联法

    根据以上原理,可实现简单的 2 分频电路,以此为基础进行串联,可构成 4 分频和8 分频电路。电路结构如下图所示,用 Verilog 描述时只需使用简单的取反逻辑即可。

    同理,4分频设计:使用基准时钟在第1、2个时钟周期输出高电平(或低电平),在第3、4个时钟周期输出相反电平。

    2.2 计数器法

    如果偶数分频系数过大或者寄存器级联法无法实现对应的分频,可以采用计数器法进行分频,计数器法可以实现任意偶数分频。在计数周期达到分频系数中间数值 (N/2-1) 时进行时钟翻转,可保证分频后时钟的占空比为 50%。
    Tips:中间数值(N/2-1) 需要减1是因为从0开始计数

    2.3 verilog代码

    //偶数分频电路设计(2分频、4分频、8分频、6分频)
    //触发器法实现2分频、4分频、8分频
    //计数器法实现6分频
    module clk_div_even(
        input 		rst_n,			//复位信号
        input 		clk,			//源时钟信号
        output 		clk_div2,		//输出2分频
        output 		clk_div4,		//输出4分频
        output 		clk_div6,		//输出6分频
        output 		clk_div8		//输出8分频
        ;
    
    //定义4个中间寄存器和1个计数器    
    reg         clk_div2_r;
    reg 		clk_div4_r;
    reg 		clk_div6_r;
    reg 		clk_div8_r;
    reg [3:0] cnt;
    
    //2分频时钟输出模块
    //源时钟上升沿触发,低电平异步复位
    always @(posedge clk or negedge rst_n begin
        if (!rst_n begin		//低电平复位
            clk_div2_r <= 1'b0;
        end
        else begin
            clk_div2_r <= ~clk_div2_r;		//源时钟上升沿信号翻转得到2分频时钟
        end
    end
    
    assign clk_div2 = clk_div2_r;		//延时输出,消除亚稳态
    
    //4分频时钟输出模块
    //2分频时钟上升沿触发 低电平异步复位
    always @(posedge clk_div2 or negedge rst_n begin
        if (!rst_n begin
            clk_div4_r <= 1'b0;
        end
        else begin
            clk_div4_r <= ~clk_div4_r;		//2分频时钟上升沿信号翻转得到4分频时钟
        end
    end
    
    assign clk_div4 = clk_div4_r;		//延时输出,消除亚稳态
    
    //8分频时钟输出模块
    //4分频时钟上升沿触发 低电平异步复位
    always @(posedge clk_div4 or negedge rst_n begin
        if (!rst_n begin
            clk_div8_r <= 'b0;
        end
        else begin
            clk_div8_r <= ~clk_div8_r;		//4分频时钟上升沿信号翻转得到8分频时钟
        end
    end
        
    assign clk_div8 = clk_div8_r;		//延时输出,消除亚稳态
    
    //计数器模块
    //源时钟上升沿触发,低电平异步复位
    always @(posedge clk or negedge rst_n begin
        if (!rst_n begin		//低电平复位
            cnt    <= 4'b0 ;
        end
        else if (cnt == 2 begin		//计数器从0计数,到2清零
            cnt    <= 4'b0 ;
        end
        else begin				//计数累加
            cnt    <= cnt + 1'b1 ;
        end
    end
    
    //6分频时钟输出模块
    always @(posedge clk or negedge rst_n begin
        if (!rst_n begin		
            clk_div6_r <= 1'b0;
        end
        else if (cnt == 2  begin		//3个周期信号翻转得到6分频时钟
            clk_div6_r <= ~clk_div6_r;
        end
    end
        
    assign clk_div6 = clk_div6_r;		//延时输出,消除亚稳态
    
    endmodule
    

    2.4 Testbench

    `timescale 1ns/1ns		//时间刻度:单位1ns,精度1ns
    
    module clk_div_even_tb;
    
    //信号申明
     reg 		clk ;
     reg 		rst_n;
     wire 		clk_div2;
     wire 		clk_div4;
     wire 		clk_div6;
     wire 		clk_div8;
    
    //定义源时钟信号一周期时间
    parameter DIV_CLK = 10;
    
    //复位信号生成
    initial begin
        clk = 0;		//时钟信号赋初值
        rst_n = 1;		//复位信号赋初值
        #(1.5*DIV_CLK rst_n = 0;
        #DIV_CLK rst_n = 1;
        #(30*DIV_CLK;
    end
    
    //源时钟信号生成
    always #(DIV_CLK/2 clk = ~ clk;
    
    //模块例化
    clk_div_even u_clk_div_even
        (
        .clk         (clk,
        .rst_n       (rst_n,
        .clk_div2    (clk_div2,
        .clk_div4    (clk_div4,
        .clk_div6    (clk_div6,
        .clk_div8    (clk_div8
        ;
    
    endmodule
    

    2.5 仿真结果

    三、奇数分频

    3.1 占空比非50%奇数分频

    以三分频为例,电路需要实现的是:计数器从0开始计数至2,计数器到0时且在上升沿信号翻转,计数器到1时且在上升沿信号清零,具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用)。

    3.2 占空比50%奇数分频

    那么如何得到一个50%占空比的奇数分频呢?

    那么如何实现这种“类双边沿触发”的效果呢?

    以三分频为例,电路需要实现的是:设计2个分别用上升、下降沿触发的计数器cnt_p和cnt_n,设计2个分别用上升、下降沿触发的计数器clk_p和clk_n,利用clk_p和clk_n通过或逻辑运算生成占空比为50%的分频时钟,具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用)。

    Tips:此处亦可借用与逻辑运算,对比上面的clk_p和clk_n稍稍不同,大家可以试着自己画出对应时序图。

    3.3 Verilog代码

    //奇数分频电路设计(占空比非50%的3分频和占空比50%的3分频)
    module clk_div_odd (  
        input 		clk,			//时钟信号
        input 		rst_n,			//复位信号
        output 		clk_div3_1,		//占空比非50%的3分频时钟信号输出
        output 		clk_div3_2		//占空比50%的3分频时钟信号输出
        ;
    
    //定义分频的数目
    parameter N = 3;
    
    reg [3:0] 		cnt_p;		//上升沿触发计数器计数
    reg [3:0]		cnt_n;		//下降沿触发计数器计数
    reg       		clk_p;		//上升沿触发生成的时钟信号
    reg       		clk_n;		//下降沿触发生成的时钟信号
    
    //上升沿触发计数器模块
    always @(posedge clk or negedge rst_n begin
        if(!rst_n
           cnt_p <= 4'b0000;
        else if (cnt_p == N-1		//计数器从0计数,到2清零
           cnt_p <= 4'b0000;
        else
           cnt_p <= cnt_p + 1'b1;		//计数器累加
    end
    
    //下降沿触发计数器模块
    always @(negedge clk or negedge rst_n begin
        if(!rst_n
           cnt_n <= 4'b0000;
        else if(cnt_n == N-1		//计数器从0计数,到2清零
          cnt_n <= 4'b0000;
        else
          cnt_n <= cnt_n + 1'b1;		//计数器累加
    end
    
    //上升沿触发生成的时钟信号模块
    always @(posedge clk or negedge rst_n begin
        if(!rst_n
           clk_p <= 1'b0;
        else if(cnt_p == (N-1/2		//计数器到1且在上升沿,时钟信号翻转
           clk_p <= ~clk_p;
        else if (cnt_p <= 0			//计数器到0且在上升沿,时钟信号翻转
           clk_p <= ~clk_p;
        else
           clk_p <= clk_p;				//防止latch产生
    end
    
    //下降沿触发生成的时钟信号模块
    always @(negedge clk or negedge rst_n
    begin
       if(!rst_n
          clk_n <= 1'b0;
       else if (cnt_n == (N-1/2		//计数器到1且在上升沿,时钟信号翻转
          clk_n <= ~clk_n;
       else if (cnt_n == 0				//计数器到0且在上升沿,时钟信号翻转
          clk_n <= ~clk_n;
       else
          clk_n <= clk_n;				//防止latch产生
    end
    
    //延时输出,消除亚稳态
    assign clk_div3_1 = clk_p;				//得到占空比非50%的3分频时钟信号
    assign clk_div3_2 = clk_p | clk_n;		//或逻辑运算得到占空比50%的3分频时钟信号
    
    endmodule
    

    3.4 Testbench

    `timescale 1ns/1ps		//时间刻度:单位1ns,精度1ps
    module clk_div_odd_tb;
    
    //信号申明
    reg clk;
    reg rst_n;
    wire clk_div3_1;		//占空比非50%的3分频时钟信号
    wire clk_div3_2;		//占空比50%的3分频时钟信号
    
    parameter DIV_CLK = 5;		//定义源时钟信号一周期时间
    
    //复位信号生成
    initial begin
    	clk = 0;		//时钟信号赋初值
        rst_n = 1;		//复位信号赋初值
    	#(3*DIV_CLK
    	rst_n = 0;
    	#(6*DIV_CLK
    	rst_n = 1;
    	#(20*DIV_CLK;
    end
    
    //源时钟信号生成
    always #DIV_CLK clk = ~clk;
    
    //模块例化
    clk_div_odd u_clk_div_odd
        (.clk           (clk,
         .rst_n         (rst_n,
         .clk_div3_1    (clk_div3_1,
         .clk_div3_2    (clk_div3_2
        ;
    
    endmodule
    

    3.5 仿真结果

    四、小数分频

    4.1 双模前置分频法

    小数分频是基于可变分频和多次平均的方法实现的。

    T = ( Ma+(M+1b / a+b,这里我们发现组成小数分频使用了a个M分频和b个M+1分频的整数分频电路。

    基本思想是在54个源时钟周期里完成10个5.4分频,根据前面的公式可知:有6的5分频和4个6分频。只要将5分频和6分频插入在54个源时钟周期即可。

    同时我们应当考虑分频信号的实现顺序

    (1先进行 6 次 5 分频,再进行 4 次 6 分频;

    (3 将 6 次 5 分频平均的插入到 4 次 6 分频中;

    前两种方法时钟频率不均匀,相位抖动较大,所以一般会采用后两种平均插入的方法进行小数分频操作。

    那又如何平均插入呢?

    (1 第一次分频次数差值 54 - 10×5 = 4 < 10,第一次进行 5 分频。

    (3 第三次差值累加结果为 4 + 8 = 12 > 10,第三次使用 6分频。

    以此类推,完成将 6 次 5分频平均插入到 4 次 6分频的过程

    Tips:每一段并不是严格的5.4分频(因为信号翻转只在边沿触发),而是在54个源时钟周期平均下来有10个分频,而且时序难以保证。且占空比几乎达不到50%。

    4.2 Verilog代码

    //小数分频电路设计
    //双模前置法实现5.4分频
    module clk_div_fraction
       (
        input				rst_n,		//复位信号
        input               clk,		//时钟信号
        output              clk_frac	//小数分频输出信号
        ;
     
     //定义介于5.4分频的5分频和6分频
    parameter		CLK_DIV_1  =  5;
    parameter    	CLK_DIV_2  =  6;
    parameter    	DIFF       =  4;		//10个周期内5分频与5.4分频的差值
    
    
    reg [3:0]            cnt_end;			//分频插入计数器	(用于判断插入什么分频)	
    reg [3:0]            cnt;				//总计数器
    reg                  clk_frac_r; 		//小数分频中间寄存器信号
    reg [4:0]            diff_cnt_r;		//差值信号
    reg [4:0]            diff_cnt;			//差值信号
    wire                 diff_cnt_en= cnt == cnt_end;		//使能信号
    
    //差值累加逻辑模块
    always @(* begin
        if(diff_cnt_r >= 10 begin
            diff_cnt = diff_cnt_r -10 + DIFF;	//差值大于10,插入6分频,差值减6
        end
        else begin
            diff_cnt = diff_cnt_r + DIFF;		//差值小于10,插入5分频,差值加4
        end
    end
      
    // 借用寄存器延迟输出diff_cnt_r                           
    always @(posedge clk or negedge rst_n begin
        if(!rst_n begin				//复位差值清零
            diff_cnt_r <= 0;
        end
        else if(diff_cnt_en begin		//使能信号高电平时,差值信号延迟输出
            diff_cnt_r <= diff_cnt;
        end
    end
    
    //5分频和6分频插入逻辑模块
    always @(posedge clk or negedge rst_n begin
        if(!rst_n begin
            cnt_end <= CLK_DIV_1-1 ;		//复位先插入5分频
        end
        else if(diff_cnt >= 10 begin
            cnt_end <= CLK_DIV_2-1 ;		//差值大于10,插入6分频
        end
        else begin
            cnt_end <= CLK_DIV_1-1 ;		//差值小于10,插入5分频
        end
    end
    
    //
    always @(posedge clk or negedge rst_n begin
        if(!rst_n begin					//总计数器、分频信号信号复位
            cnt <= 1'b0;
            clk_frac_r <= 1'b0;
        end
        else if(cnt == cnt_end begin		//计数器到分频插入界限点
            cnt <= 1'b0;					//总计数器清零
            clk_frac_r <= 1'b1;				//时钟分频信号电平置"1"
        end
        else begin			//其他情况下,计数器累加计数、时钟分频信号电平保持"0"
            cnt <= cnt + 1'b1;		
            clk_frac_r <= 1'b0;
        end
    end
    
    //延时输出,消除亚稳态  
    assign clk_frac = clk_frac_r;
    
    endmodule
    

    4.3 Testbench

    `timescale 1ns/1ps		//时间刻度:单位1ns,精度1ps
    module clk_div_fraction_tb;
    
    //信号申明
    reg			clk;				
    reg			rst_n;
    wire		clk_frac;			
    
    parameter DIV_CLK = 5;			//定义源时钟信号一周期时间
    
    //复位信号生成 
    initial begin
        clk = 0;			//时钟信号赋初值
    	rst_n = 1;			//复位信号赋初值
    	#(3*DIV_CLK
    	rst_n = 0;
    	#(6*DIV_CLK
    	rst_n = 1;
    	#(20*DIV_CLK;
    end
    
    //源时钟信号生成
    always #DIV_CLK clk = ~clk;
    
    //模块例化
    clk_div_fraction u_clk_div_fraction
        (.clk             (clk,
         .rst_n           (rst_n,
         .clk_frac        (clk_frac
        ;
    
    endmodule
    

    4.4 仿真结果

    Tips:5.4小数分频并不是每一段都是均匀的长度(即局部不满足小数分频,总体满足小数分频)

    那么是什么原因造成的呢?

    五、半整数分频

    5.1 占空比50%半整数分频

    对于使用小数分频法得到的,以3.5分频为例,需要使用一个四分频和一个三分频,七个周期内,输出两个1,但是信号时序难以得到保障,时钟信号的质量得不到保证。

    那有没有新的方法可以优化半整数分频呢?

    (1)在源时钟上升沿分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。

    (3)两个分频时钟做相位一个延迟半个源时钟周期,一个提前半个源时钟周期。将两次产生的时钟进行“或”操作,便可以得到周期均匀的 3.5 倍分频时钟。分频波形示意图如下所示。

    5.2 Verilog代码

    //半整数分频电路设计
    module clk_div_half
        (
        input               rst_n,
        input               clk,
        output              clk_div
        ;
    
    parameter            DIV_CLK = 7;			//3.5分频的高低电平总个数
    reg [3:0]            cnt;					//总计数器
    reg                  clk_p;					//上升沿触发生成的时钟信号
    reg                  clk_n;					//下降沿触发生成的时钟信号
    
    //计数器模块
    always @(posedge clk or negedge rst_n begin
        if (!rst_n begin
            cnt <= 1'b0 ;
         end
         else if (cnt == DIV_CLK-1 begin 		//从0计数,到6清零
            cnt <= 'b0 ;
         end
         else begin
             cnt <= cnt + 1'b1 ;
         end
    end
    
    //上升沿触发生成的时钟信号模块
    //计数器到0和4并且在上升沿触发信号翻转
    always @(posedge clk or negedge rst_n begin
        if(!rst_n begin
              clk_p <= 1'b0;
         end
          else if (cnt == 0 begin					//计数器到0信号翻转
              clk_p <= 1;
          end
          else if (cnt == (DIV_CLK/2+1 begin		//计数器到4信号翻转
              clk_p <= 1 ;
          end
          else begin
              clk_p <= 0 ;
          end 
    end
    
    //下降沿触发生成的时钟信号模块
    //计数器到1和4并且在下降沿触发信号翻转
    always@(negedge clk or negedge rst_n begin
        if(!rst_n begin
             clk_n <= 1'b0 ;
        end
        else if(cnt == 1 begin						//计数器到1信号翻转
             clk_n <= 1 ;
        end
        else if (cnt == (DIV_CLK/2+1  begin		//计数器到4信号翻转
             clk_n<= 1 ;
        end
        else begin
             clk_n <= 0 ;
        end
    end
    
    //或逻辑运算得到占空比50%的3.5半整数分频信号
    assign clk_div = clk_p | clk_n;
    
    endmodule
    

    5.3 Testbench

    `timescale 1ns/1ps			//时间刻度:单位1ns,精度1ps
    module clk_div_half_tb;
    
    //信号申明
    reg 			clk;
    reg 			rst_n;
    wire 			clk_div;
      
    parameter DIV_CLK0 = 5;		//定义源时钟信号一周期时间
    
    //复位信号生成 
    initial begin
        clk = 0;				//时钟信号赋初值
    	rst_n = 1;				//复位信号赋初值
    	#(3*DIV_CLK0
    	rst_n = 0;
    	#(6*DIV_CLK0
    	rst_n = 1;
    	#(20*DIV_CLK0;
    end
    
    //源时钟信号生成
    always #DIV_CLK0 clk = ~clk;
    
    //模块例化
    clk_div_half u_clk_div_half
        (.clk             (clk,
         .rst_n           (rst_n,
         .clk_div         (clk_div
        ;
    
    endmodule
    

    5.4仿真结果

    六、状态机分频

    6.1状态机分频

    例如完成一个四分频且占空比为25%的分频器,此时可以列出四种状态,状态机在四种状态不断切换,根据下一个输出状态只与当前状态有关而与输出无关,可以知道此分频器可根据Moore状态机完成。

    6.2 verilog代码

    module clk_div_FSM
        (
        input wire clk,
        input wire rst_n,
        output reg clk_FSM
        ;
    
    //定义四种状态 
    parameter	S0 = 2'b00;
    parameter	S1 = 2'b01;
    parameter	S2 = 2'b10;
    parameter	S3 = 2'b11;
     
    reg [1:0]	state;			//定义目前状态
    reg [1:0]	next_state;		//下一状态
    
    //信号复位模块
    always @(posedge clk,negedge rst_n begin
        if(!rst_n begin 
            state <= S0;
        end
        else begin
            state <= next_state;
        end
    end
    
    //状态转换模块(相当于用状态机写计数器)
    always @(* begin
        case (state
            S0:	next_state = S1;
            S1:	next_state = S2;
            S2:	next_state = S3;
            S3:	next_state = S0;
        endcase
    end
    
    //信号输出模块
    always @(* begin
        if(state == S0 begin
            clk_FSM = 1'b1;
        end
        else begin
            clk_FSM = 1'b0;
        end
    end
    
    endmodule
    

    6.3 Tsetbench

    `timescale 1ns/1ps
    module clk_div_FSM_tb;
    
    //信号申明
    reg				 clk;
    reg 			 rst_n;
    wire			 clk_FSM;
    
    parameter DIV_CLK = 5;			//定义源时钟信号一周期时间
    
    //复位信号生成 
    initial begin
        clk = 0;				//时钟信号赋初值
        rst_n = 1;				//复位信号赋初值
        #(3*DIV_CLK
        rst_n = 0;
        #(6*DIV_CLK
        rst_n = 1;
        #(20*DIV_CLK;
    end
    
    //源时钟信号生成
    always	#DIV_CLK clk = ~clk;
    
    //模块例化
    clk_div_FSM u_clk_div_FSM
        (.clk             (clk,
         .rst_n           (rst_n,
         .clk_FSM         (clk_FSM
        ;
    
    endmodule
    

    6.4仿真结果

    七、总结

    奇数分频:计数分频基本原理也是通过计数器实现的,主要分为占空比非50%的奇数分频和占空比50%的奇数分频,后者实现简单而后者稍稍复杂一些。占空比非50%的情况下,时钟信号在上升沿(N-1)/2翻转和 0翻转即可得到需要的分频信号。占空比50%的情况下,一个时钟信号在上升沿而一个时钟信号在下降沿,触发(N-1)/2翻转和0翻转,然后将clk_p和clk_n做或逻辑运算即可得到占空比50%的计数分频信号。从以上可以看出,占空比50%的奇数分频只是在占空比非50%的奇数分频的基础上多做了一个逻辑运算。

    究其根本原因是信号只在源时钟的边沿触发。

    如果需要得50%的半整数分频怎么办? 首先做出两个上升沿下降沿二分频信号,通过在半整数两边寻找相邻的的奇数和偶数(决定信号电平周期数),然后做逻辑运算即可以得到占空比50%的半整数分频。

    不定期检查、补充、纠错,欢迎随时交流纠错

    最后修改日期:2023.5.10

    软件版本:

      仿真软件:Modelsim 10.6c
    • 时序绘制软件:TimeGen 3.2
    • 描述语言:verilog

    编程笔记 » 数字分频器设计(偶数分频/奇数分频/小数分频/半整数分频/状态机分频|verilog代码|Testbench|仿真结果)

    赞同 (38) or 分享 (0)
    游客 发表我的评论   换个身份
    取消评论

    表情
    (0)个小伙伴在吐槽