likes
comments
collection
share

FPGA 之小数分频器的设计并基于vivado进行仿真分析

作者站长头像
站长
· 阅读数 3

设计小数分频器的前提是偶数分频器和奇数分频器,下面先对偶数分频器和奇数分频器作简单的讲解。

1 偶数分频器: 偶数分频器实现比较简单,简单的实现方式是通过计数器实现对系统时钟周期的计数,从而实现偶数分频器,实现方式如下: ` module divider_even #(parameter DIV = 4)( input sys_clk, input sys_rst_n, input [7:0] num, //计算时钟周期的个数 output reg div_clk // 输出分频后的频率 );

    reg  [7:0]cnt;                 //计数器

always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt <= 0;
	else if(cnt == M_N-1)    //0 - n-1为计数次数
		cnt <= 0;
	else 
		cnt <= cnt + 1;
end
//输出频率 ,半个周期实现反转信号。
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		div_clk <= 1'b0;
	else if((cnt == 0) || (cnt == (M_N/2)))
		div_clk <= ~div_clk;
	else
		div_clk <= div_clk;
end		

endmodule `

2 奇数分频器 奇数分频器的实现是在偶数分频器的基础上,首先得到时钟上升沿有效的偶数计数器和时钟下降沿有效的偶数计数器,再将这两个计数器进行与运算,通过assign赋值,即可实现奇数分频器。(此偶数分频器为最接近奇数分频器的偶数分频器,如要实现5分频,偶数分频器为4分频)。具体verilog程序如下:

    `
  module divider_odd #(parameter DIV = 3)(					
input	sys_clk,
input	sys_rst_n,
input	[7:0]M_N,			
input	en,


output	reg clk1,        //上升沿和下降沿 生成的两个时钟   相或运算可以生成奇数
output	reg clk2,

output	div_clk
);

reg		[7:0]		cnt1;    //用两个寄存器来实现 偶数计数器
reg		[7:0]		cnt2;
//实现计数
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) 
		cnt1 <= 4'd0;
	else if(cnt1 == M_N-1) 
		cnt1 <= 4'd0;
	else 
		cnt1 <= cnt1 + 1'b1;	
end
//上升沿有效的偶数分频器
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) 
		clk1 <= 1'd0;
	else if(cnt1 == 0) 
		clk1 <= ~clk1;
	else if(cnt1 == (M_N-1)/2) 
		clk1 <= ~clk1;
	else 
		clk1 <= clk1;	
end
	
//下降沿有效的偶数分频器	
always@(negedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) 
		cnt2 <= 4'd0;
	else if(cnt2 == M_N-1) 
		cnt2 <= 4'd0;
	else 
		cnt2 <= cnt2 + 1'b1;	
end
always@(negedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		clk2 <= 1'd0;
	else if(cnt2 == 0)
		clk2 <= ~clk2;
	else if(cnt2 == (M_N-1)/2)     
  // 两个偶数计数器  相位不同  拼接成奇数计数器    
		clk2 <= ~clk2;
	else 
		clk2 <= clk2;
end

assign div_clk = clk1 | clk2;       //生成奇数计数器
	
endmodule

`
    基于上述所述,即可完成偶数和奇数分频器的设计,但分出的频率如果直接使用的话,因为不在时钟管理单元中,会出现误差,所以一般在分频设计中很少采用计数直接输出的频率的方式,而是计数后通过标志信号位,仍然通过系统时钟的改变来输出所需要的频率,这样误差很小。 
    

3 小数分频器的设计 小数分频器的原理基于双模小数分频原理: 具体的思想为要实现一个53/10,即5.3分频的分频器,即就是在53个时钟周期中输出10个周期的波形。 双模小数分频原理是取该频次两边的两个分频器拼接组成小数分频器,以5.3分频为例,即使用5分频和6分频道分频器拼接可以组成5.3分频分频器。基于此列二元一次方程求出具体的分频器个数:需要a个五分频,b个6分频。 即a+b = 10;5a+6b = 53. 即可求出需要五分频7个6分频3个。 在实际实现中,为了将占空比接近百分之50,通常是将两个分频器波形拼接而成,以5.3分频为例,即就是通过2个5分频、1个6分频、2个5分频、1个6分频、2个5分频、1个6分频、(1个5分频)。 通过上述的拼接方式组成,需要注意的是总周期中最后一个输出为1个五分频,这个就是我们执行2个5分频、1个6分频的结束条件。 即就是计数到时钟周期为53时,即最后一个5分频结束,开始从头循环。 所以设计需要一个cnt_period 用来计数一个大周期里的时钟周期个数,峰值为53。 此外要实现2个5分频、1个6分频,需要去切换奇偶分频器的使用,实现对奇偶分频器的控制需要使能信号en_odd,和en_even。通过将使能信号与奇偶分频器的复位信号相与来实现对奇偶分频器的输出控制。 此外在2个5分频、1个6分频,中需要切换对奇偶分频器的控制,这就要求我们去统计此时的时钟周期数目,引入cnt计数器,两个五分频需要计数25=10,又因为奇偶计数器通过复位信号控制到输出波形会延迟一拍,所以cnt的判断条件是cnt == 10-1,也就是cnt为9时需要切换为6分频即偶数分频,即此时en_even=1,en_odd = 0,这样就可以在计数为10即完成两个5分频时切换为六分频。 以上为5分频切换为6分频,要实现6分频切换为5分频,需要统计cnt计数到10+6=16,此时需要cnt判断条件为 if(cnt == (cnt_odd + cnt_even )) ,但此时是以时钟下降沿作为判断的,再切换5分频时,敏感条件需要为下降沿,因为奇数分频器使能输出相差半拍,即等于cnt计数为16时再开始 en_odd <= 1'b1,此时就可以在计数16之后续上5分频的波形。

控制奇偶分频器按时序要求输出波形程序如下:

`   //切换奇数分频器和  偶数分频器的状态。 通过使能信号 en_odd   en_even
        always@(negedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            en_odd <= 1'b1;  
        else if(cnt == (cnt_odd - 1))
            en_odd <= 1'b0;
        else if(cnt == (cnt_odd + cnt_even ))
            en_odd <= 1'b1;
        else 
            en_odd <= en_odd;
 
    always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            en_even <= 1'b0;
        else if(cnt == (cnt_odd - 1))    
            en_even <= 1'b1;         
        else if(cnt == (cnt_odd + cnt_even ))
            en_even <= 1'b0;
        else 
            en_even <= en_even;  `

计数器的控制程序如下:

        ` 
        always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            cnt <= 8'd0;
        else if((cnt == cnt_odd+cnt_even) || (cnt_period == period_max))
            cnt <= 8'd1;
        else 
            cnt <= cnt + 1'b1;
       
      always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            cnt_period <= 1'b0;
        else if(cnt_period == 53)
            cnt_period <= 8'd1;
        else 
            cnt_period <= cnt_period + 1'b1;
        `

基于此总的控制程序为:

` module dec_divider 
(
input sys_clk,
input sys_rst_n,

output reg [7:0]cnt,            //cnt 用来计算五分频或者6分频的结束。用来切换偶数分频器和奇数分频器。
output reg [7:0]cnt_period,    //计算占用的时钟周期个数  当为53时置为1.

output reg en_odd,
output reg en_even,
output clk_odd,
output clk_even,
output  clk_out

);
 //  采用分频交叉方法将5分频和6分频组合,即2个五分频,1个六分频,循环3次,
 //最后一个五分频,计算周期值此时为period_max = 53,此时重新开始计数。   
//其中odd_period和even_period 分别为奇数周期数和偶数周期数。
  parameter odd_period =5;
  parameter even_period = 6;
  parameter period_max = 53;
  parameter [7:0]cnt_odd = 2*odd_period;
  parameter [7:0]cnt_even = even_period;

divider_odd  divider_odd(
	.M_N(odd_period),
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n && en_odd),
	.div_clk(clk_odd)
	);
divider_even divider_even(
	.sys_clk(sys_clk),
	.M_N(even_period),
	.sys_rst_n(sys_rst_n && en_even),
	.div_clk(clk_even)
	);
	

    assign clk_out = (en_odd == 1)?clk_odd:clk_even;
    //切换奇数分频器和  偶数分频器的状态。 通过使能信号 en_odd   en_even
    always@(negedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            en_odd <= 1'b1;  
        else if(cnt == (cnt_odd - 1))
            en_odd <= 1'b0;
        else if(cnt == (cnt_odd + cnt_even ))
            en_odd <= 1'b1;
        else 
            en_odd <= en_odd;
 
    always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            en_even <= 1'b0;
        else if(cnt == (cnt_odd - 1))    
            en_even <= 1'b1;         
        else if(cnt == (cnt_odd + cnt_even ))
            en_even <= 1'b0;
        else 
            en_even <= en_even;
     //
     // 此处为管理 cnt信号使其在52个周期里  切换计数实现2奇数1偶数,并在最后一个53周期后重新开始计数。
     always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            cnt <= 8'd0;
        else if((cnt == cnt_odd+cnt_even) || (cnt_period == period_max))
            cnt <= 8'd1;
        else 
            cnt <= cnt + 1'b1;
       
      always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n)
            cnt_period <= 1'b0;
        else if(cnt_period == 53)
            cnt_period <= 8'd1;
        else 
            cnt_period <= cnt_period + 1'b1;
        

endmodule
`

仿真分析:

testbench程序如下:

`
   module dec_divider_tb();
    reg sys_clk;
    reg sys_rst_n;
    wire [7:0]cnt;         
    wire [7:0]cnt_period;   
    wire en_odd;
    wire en_even;
    wire clk_odd;
    wire clk_even;
    wire  clk_out;

dec_divider dec_divider(
     .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
    .cnt(cnt),          
    .cnt_period(cnt_period),    
    .en_odd(en_odd),
    .en_even(en_even),
    .clk_odd(clk_odd),
   .clk_even(clk_even),
   .clk_out(clk_out)
    );

initial 
    begin
        sys_rst_n = 1'b0;
        #10;
        sys_rst_n = 1'b1;
        #5000000;
    $stop;
    end

initial sys_clk = 1'b0;
always #10 sys_clk = ~sys_clk;  

 
endmodule

`

由下图可以清晰看到从10ns到1070ns (时钟周期为20ns),即53个周期内输出了10个周期。

FPGA 之小数分频器的设计并基于vivado进行仿真分析

FPGA 之小数分频器的设计并基于vivado进行仿真分析