分频器设计

分频器

  在数字系统的设计中经常会碰到需要使用多个时钟的情况。时钟信号的产生通常具有两种方法,一种是使用PLL(Phase Locked Loop,锁相环),可生成倍频、分频信号;另一种则是使用硬件描述语言构建一个分频电路。
  分频器的设计通常分为以下三类:偶数分频器、奇数分频器、及小数分频器。

偶数分频器

D触发器实现偶分频,占空比50%

  D触发器实现2分频的电路图如下所示:
在这里插入图片描述

  通过将D触发器2分频电路级联,可实现输入时钟的2N倍分频,其中N为D触发器2分频电路级联的个数。(注:不管输入时钟的占空比是多少,输出占空比均为50%。)

RTL实现代码

module Even_Freq_Div_N(clk, rst_n, div_clk);
/********************参数定义********************/

/*********************IO 说明********************/
input    clk;
input    rst_n;
output    reg   div_clk; 
/***************** 内部信号声明 *****************/   

/*******************功能定义*********************/
always@(posedge clk or negedge rst_n)
begin
     if(!rst_n)     
         div_clk <= 1'b0;
     else
         div_clk <= ~div_clk;
end

endmodule

testbench 代码

module tb_Even_Freq_Div_N();
 
/*测试模块输入、输出变量定义*/
//输入
reg    clk;
reg    rst_n;

//输出
wire   div_clk;

/*待测试模块调用*/	
Even_Freq_Div_N    Even_Freq_Div_N_test
(
	.clk         (clk     ),
	.rst_n       (rst_n   ), 
	.div_clk     (div_clk )
);               
/*激励向量定义*/
always    #(10)    clk = ~clk;
//初始化输入信号
initial   begin
    clk = 1'b0;
    rst_n <= 1'b0;
    #20
    rst_n <=1'b1;
end	
/*显示格式定义*/
endmodule

功能仿真
在这里插入图片描述

计数器实现偶分频,输出占空比任意(细度取决于源时钟频率和分频数)

  若要实现N分频(N为偶数)占空比为50%,只需在计数器计数到N-1(重新从0开始计数),在小于N/2-1时输出低电平,输出N/2-1时输出高电平。而要改变占空比的,则调整比较器比较的值。(尽量不要在一个module内用作后级时钟输入,但可以用于其他module的时钟输入)

6分频RTL代码实现

module Even_Freq_Div_N(clk, rst_n, div_clk);
/********************参数定义********************/
parameter    Div_N = 6;  //分频数,N为偶数
parameter    width = $clog2(Div_N);
parameter    Duty = Div_N/2-1;//用于调整占空比
/*********************IO 说明********************/
input    clk;
input    rst_n;
output    reg   div_clk; 
/***************** 内部信号声明 *****************/   
reg  [width-1:0]   cnt;
/*******************功能定义*********************/
always@(posedge clk or negedge rst_n)
begin
     if(!rst_n)     
         cnt <= {width{1'b0}};
     else if(cnt == (Div_N-1))
         cnt <= {width{1'b0}};
     else
         cnt <= cnt + 1'd1;
end

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
       div_clk <= 1'b0;
    else if(cnt > Duty) 
       div_clk <= 1'b1;
    else 
       div_clk <= 1'b0;
end

endmodule

功能仿真:
在这里插入图片描述

计数器实现偶分频,输出分频标志

  若要实现N分频,只需在计数器计数到N-1(重新从0开始计数)时,输出分频标志即可。

6分频,输出分频标志RTL代码实现

module Even_Freq_Div_N(clk, rst_n, div_flag);
/********************参数定义********************/
parameter    Div_N = 6;  //分频数,N为偶数
parameter    width = $clog2(Div_N);

/*********************IO 说明********************/
input    clk;
input    rst_n;
output    reg   div_flag; 
/***************** 内部信号声明 *****************/   
reg  [width-1:0]   cnt;
/*******************功能定义*********************/
always@(posedge clk or negedge rst_n)
begin
     if(!rst_n)     
         cnt <= {width{1'b0}};
     else if(cnt == (Div_N-1))
         cnt <= {width{1'b0}};
     else
         cnt <= cnt + 1'd1;
end

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
       div_flag <= 1'b0;
    else if(cnt == (Div_N-1)) 
       div_flag <= 1'b1;
    else 
       div_flag <= 1'b0;
end

endmodule

功能仿真:
在这里插入图片描述

总结

  采用输出占空比任意的方法得到的分频信号不能直接用作同一个module内后级模块时钟信号,即

always@(posedege div_clk or negedge rst_n )
begin
    if(!rst_n)
        A <= 4'b0;
    else 
        A <= A+1'b1;
     else 
end

  这样的用法衍生的潜在问题在低速系统内不易察觉,但在高速系统中很容易出现问题。而采用输出分频标志的方法,虽然会多使用一些寄存器,但是可以和所在系统时钟下产生的信号都保持几乎相同的时钟关系。在同一个module内后级使用方式如下:

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        A <= 4'b0;
    else if(clk == 1'b1)
        A <= A+1'b1;
     else 
        A <= A;
end

奇分频

计数器实现奇分频,占空比非50%

  若要实现N分频(N为奇数),只需将计数器在待分频时钟上升沿触发下循环计数,从0计数到(N-1)后计数器清零。当计数小于(N-1)/2时输出高电平,其他情况下输出低电平。(尽量避免将分频后时钟用作module内的后级时钟输入,但可以用于其他module的时钟输入)。7分频的Verilog代码如下:
奇分频占空比非50%RTL代码

module Odd_Freq_Div_N(clk, rst_n, div_clk);
/********************参数定义********************/
parameter    DIV_NUM = 3'd7;//分频数
parameter    width = $clog2(DIV_NUM);
parameter    Duty = 3'd4; //用于调整占空比
/*********************IO 说明********************/
input    clk;
input    rst_n;
output   reg   div_clk; 
/***************** 内部信号声明 *****************/   
reg  [width-1:0]   cnt;  //计数寄存器
/*******************功能定义*********************/
//上升沿计数
always@(posedge clk or negedge rst_n)
begin
     if(!rst_n)     
         cnt <= {width{1'b0}};
     else if(cnt == (DIV_NUM-1))
         cnt <= {width{1'b0}};
     else
         cnt <= cnt + 1'b1;
end

//上升沿计数器生成分频时钟
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        div_clk <= 1'b0;
    else if(cnt < Duty)
        div_clk <= 1'b1;  
    else
        div_clk <= 1'b0;           
end

endmodule

功能仿真: 在这里插入图片描述

计数器实现奇分频,占空比50%

  用两个计数器,一个由输入时钟上升沿触发,一个由输入时钟下降沿触发,最后将两个计数器的输出相或,即可得到占空比为50%的方波波形。(尽量避免将分频后时钟用作module内的后级时钟输入,但可以用于其他module的时钟输入)

奇分频占空比50%RTL代码

module Odd_Freq_Div_N(clk, rst_n, div_clk);
/********************参数定义********************/
parameter    DIV_NUM = 3'd7;
parameter    width = $clog2(DIV_NUM);
/*********************IO 说明********************/
input    clk;
input    rst_n;
output   div_clk; 
/***************** 内部信号声明 *****************/   
reg  [width-1:0]   cnt_p;//上升沿计数寄存器
reg  [width-1:0]   cnt_n;//下降沿计数寄存器
reg  clk_p;
reg  clk_n;
/*******************功能定义*********************/
//上升沿计数
always@(posedge clk or negedge rst_n)
begin
     if(!rst_n)     
         cnt_p <= {width{1'b0}};
     else if(cnt_p == (DIV_NUM-1))
         cnt_p <= {width{1'b0}};
     else
         cnt_p <= cnt_p + 1'b1;
end

//下降沿计数
always@(negedge clk or negedge rst_n)
begin
     if(!rst_n)     
         cnt_n <= {width{1'b0}};
     else if(cnt_n == (DIV_NUM-1))
         cnt_n <= {width{1'b0}};
     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 < (DIV_NUM-1)/2)
        clk_p <= 1'b1;  
    else
        clk_p <= 1'b0;           
end

//下降沿计数器生成分频时钟
always@(negedge clk or negedge rst_n)
begin
    if(!rst_n)
        clk_n <= 1'b0;
    else if(cnt_n < (DIV_NUM-1)/2)
        clk_n <= 1'b1;  
    else
        clk_n <= 1'b0;          
end

assign    div_clk = clk_p | clk_n;

endmodule

功能仿真:
在这里插入图片描述

计数器实现奇分频,输出分频标志

  若要实现N分频,只需在计数器计数到N-1(重新从0开始计数)时,输出分频标志即可。

7分频,输出分频标志RTL代码

module Odd_Freq_Div_N(clk, rst_n, div_flag);
/********************参数定义********************/
parameter    DIV_NUM = 3'd7;//分频数
parameter    width = $clog2(DIV_NUM);
/*********************IO 说明********************/
input    clk;
input    rst_n;
output   reg   div_flag; 
/***************** 内部信号声明 *****************/   
reg  [width-1:0]   cnt;  //计数寄存器
/*******************功能定义*********************/
//上升沿计数
always@(posedge clk or negedge rst_n)
begin
     if(!rst_n)     
         cnt <= {width{1'b0}};
     else if(cnt == (DIV_NUM-1))
         cnt <= {width{1'b0}};
     else
         cnt <= cnt + 1'b1;
end

//产生分频标志
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        div_flag <= 1'b0;
    else if(cnt == (DIV_NUM-1))
        div_flag <= 1'b1;  
    else
        div_flag <= 1'b0;           
end

endmodule

功能仿真
在这里插入图片描述

利用基本逻辑单元实现奇分频,占空比50%

  以实现5分频,占空比50%为例
  思路:先使用触发器构成序列生成器,输出00011循环脉冲,实现占空比非50%的5分频,然后用负沿触发器打一拍,再相或。由于00011循环共五个状态,故需3个D触发器。通过列状态表、画卡诺图,得到由三个D触发器及逻辑门构成的00011序列生成器,后接负沿触发器打一拍并将其输出与序列生成器的输出相或,即得到占空比为50%的5分频电路

  1. 确定序列发生器使用D触发器的个数:
    先用2级。则移位状态00-00-01-…,有重复,不可以。
    用3级。移位状态000-001-011-110-100-000,没有重复的回到原始状态,可以。故最少需要3级D触发器。
  2. 通过列状态表、得到由三个D触发器及逻辑门构成的00011序列生成器

在这里插入图片描述
由状态表达式可得逻辑表达式: D = Q 1 + Q 2 ‾ D=\overline{Q_1+Q_2} D=Q1+Q2
3.序列生成器后接负沿触发器打一拍并将其输出与序列生成器的输出相或,电路结构如下
在这里插入图片描述

5分频RTL代码

module Odd_Freq_Div_5(clk_in, rst_n, clk_out);
/********************参数定义********************/

/*********************IO 说明********************/
input    clk_in;
input    rst_n;
output   clk_out; 
/***************** 内部信号声明 *****************/   
reg    Q0;  
reg    Q1;
reg    Q2;
reg    Q3;
wire   D;

/*******************功能定义*********************/
assign D = ~(Q1|Q2);

always@(posedge clk_in or negedge rst_n)
begin
    if(!rst_n)
    begin
        Q0 <= 1'b0;
        Q1 <= 1'b0;
        Q2 <= 1'b0;
    end
    else 
    begin
        Q0 <= D;
        Q1 <= Q0;
        Q2 <= Q1;
    end
end

always@(negedge clk_in or negedge rst_n)
begin
    if(!rst_n)
        Q3<= 1'b0;
    else 
        Q3<= Q2;
end

assign clk_out = Q3 | Q2;
endmodule

功能仿真
在这里插入图片描述

半整数分频

方法一

  半整数分频器的设计思想:计数器是通过上升沿触发,故在计数为N-1时对计数触发进行翻转,时钟的下降沿变为上升沿,因此计数值为0,所以每产生n+0.5分频时钟的周期,触发时钟都要翻转一次。如图所示,采用异或门和2分频模块设计脉冲扣除电路,脉冲扣除是输入频率和2分频输出相异或的结果。
在这里插入图片描述
6.5分频,输出分频标志RTL代码

module Freq_Div6_5(clk_in, rst_n, clk_out);
/********************参数定义********************/
parameter   CNT_NUM = 7;
parameter   WIDTH = $clog2(CNT_NUM);
/*********************IO 说明********************/
input    clk_in;
input    rst_n;
output   reg   clk_out; 
/***************** 内部信号声明 *****************/   
reg  [WIDTH-1:0]   cnt;  //计数寄存器
reg   clk1;
wire   clk2;
/*******************功能定义*********************/
//异或
assign   clk2 = clk1 ^ clk_in;

//D触发器实现2分频
always@(posedge clk_out or negedge rst_n)
begin
     if(!rst_n)     
         clk1 <= 1'b0;
     else
         clk1 <= ~clk1;
end

//上升沿计数
always@(posedge clk2 or negedge rst_n)
begin
     if(!rst_n)     
         cnt <= {WIDTH{1'b0}};
     else if(cnt == (CNT_NUM-1))
         cnt <= {WIDTH{1'b0}};
     else
         cnt <= cnt + 1'b1;
end

//产生分频标志
always@(posedge clk2 or negedge rst_n)
begin
    if(!rst_n)
        clk_out <= 1'b0;
    else if(cnt == (CNT_NUM-1))
        clk_out <= 1'b1;  
    else
        clk_out <= 1'b0;           
end

endmodule

功能仿真:
在这里插入图片描述
  个人觉得这种方法在高速时钟系统不太好,受时钟偏斜和抖动影响,会使分频后的时钟和源时钟相位有所改变,而用分频后的时钟用作module内之后的always的敏感条件,容易出现时钟和数据不同步的现象。个人认为采用PLL分频或以下方法的方法比较好。

方法二

  设K+0.5=N,N为要实现的分频。首先设置了两个计数器cnt_p和cnt_n,分别在时钟上升沿和下降沿改变,同时也设置了两个时钟信号clk_p,clk_n,同样也分别在上升沿和下降沿改变。clk_p信号先持续N+0.5个周期高电平,持续N-0.5个周期低电平,clk_n先持续N-0.5个周期低电平,持续N+0.5个周期高电平,然后将clk_p与clk_n相与,就能得到一个N分频标志的时钟。

5.5分频,RTL实现代码


module Freq_Div5_5(clk_in, rst_n, clk_out);
/********************参数定义********************/
parameter    Div_N = 5; //5+0.5分频
parameter    WIDTH = $clog2(Div_N*2);
/*********************IO 说明********************/
input    clk_in;
input    rst_n;
output   clk_out; 
/***************** 内部信号声明 *****************/   
reg  [WIDTH-1:0]   cnt_p;  
reg  [WIDTH-1:0]   cnt_n;
reg    clk_n;
reg    clk_p;

/*******************功能定义*********************/
//cnt_p计数
always@(posedge clk_in or negedge rst_n)
begin
    if(!rst_n)
        cnt_p <= {(WIDTH){1'b0}};
    else if(cnt_p == Div_N*2) 
        cnt_p <= {(WIDTH){1'b0}};
    else
        cnt_p <= cnt_p + 1'b1;
end
//cnt_n计数
always@(negedge clk_in or negedge rst_n)
begin
    if(!rst_n)
        cnt_n <= {(WIDTH){1'b0}};
    else if(cnt_n == Div_N*2) 
        cnt_n <= {(WIDTH){1'b0}};
    else
        cnt_n <= cnt_n + 1'b1;
end
//clk_p
always@(posedge clk_in or negedge rst_n)
begin
    if(!rst_n)
        clk_p <= 1'b0;
    else if(cnt_p <= Div_N) 
        clk_p <= 1'b1;
    else
        clk_p <= 1'b0;
end

always@(negedge clk_in or negedge rst_n)
begin
    if(!rst_n)
        clk_n <= 1'b0;
    else if(cnt_n < Div_N) 
        clk_n <= 1'b0;
    else
        clk_n <= 1'b1;
end

assign clk_out = clk_n & clk_p;
endmodule

功能仿真
在这里插入图片描述

小数分频

  假设输出clk_out是输入时钟clk_in的N分频,首先要将分频系数N转换为分数形式4.75 → \rightarrow 19 4 \frac{19}{4} 419,3.4 → \rightarrow 34 10 \frac{34}{10} 1034,例如8.7可以化为 87 10 \frac{87}{10} 1087,这意味着87个clk_in周期内可以输出10个clk_out周期就可以实现分频。
在这里插入图片描述
  然后采用若干种(一般是两种)整数分频在87个原周期clk_in内产生10个新时钟周期clk_out。整数分频的分频系数有很多种选择,但要尽可能接近,提高clk_out的均匀度。一般推荐在小数分频系数N的附近选取。因为8<N<9,所以两个整数分频系数是8和9。接着要计算87个clk_out周期分别有多少个是8分频和9分频的。设每10个clk_out中有x个8分频输出和y个9分频输出,则可列出如下方程:
{ x + y = 10 8 x + 9 y = 87 \begin{cases} x+y=10\\ 8x+9y=87\\ \end{cases} {x+y=108x+9y=87
  可得x=3,y=7。也就是3个8分频和7个9分频一组,循环输出,就等效于8.7分频。 最后安排组内8分频和9分频的位置。如下图。
在这里插入图片描述
8.7分频,RTL实现代码

module Odd_Freq_Div_N(clk_in, rst_n, clk_out);
/********************参数定义********************/
parameter M_N = 8'd87; 
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期

/*********************IO 说明********************/
input    clk_in;
input    rst_n;
output   reg   clk_out; 
/***************** 内部信号声明 *****************/   
reg  [3:0]   clk_cnt;  
reg  [6:0]   cyc_cnt;
/*******************功能定义*********************/
//原周期计数
always@(posedge clk_in or negedge rst_n)
begin
     if(!rst_n)    
         cyc_cnt <= 7'b0;
     else if(cyc_cnt == M_N-1)
         cyc_cnt <= 7'b0;
     else 
         cyc_cnt <= cyc_cnt + 1'b1;
end

always@(posedge clk_in or negedge rst_n)
begin
     if(!rst_n)     
         clk_cnt <= 4'b0;
     else if(cyc_cnt < c89)
     begin
         if (clk_cnt == div_e-1)
             clk_cnt <= 4'b0;
         else 
             clk_cnt <= clk_cnt + 1'b1;
     end
     else
     begin
         if (clk_cnt == div_o-1)
             clk_cnt <= 4'b0;
         else 
             clk_cnt <= clk_cnt + 1'b1;
     end
end

always@(posedge clk_in or negedge rst_n)
begin
     if(!rst_n)     
         clk_out <= 1'b0;
     else if(cyc_cnt < c89 && clk_cnt == div_e-1)
         clk_out <= 1'b1;
     else if(cyc_cnt >= c89 && clk_cnt == div_o-1)
         clk_out <= 1'b1;
     else
         clk_out <= 1'b0;
end

endmodule

功能仿真
在这里插入图片描述

参考资料

  1. 牛客网任意小数分频
  2. 常用电路设计——“分频电路”
  3. 数字系统设计与Verlilog HDL(第7版),王金明
  • 6
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值