小数分频之任意小数分频(一)(占空比50%)


写在前面(重要,必读):本次设计只是提供一个任意小数分频的思路,最后设计出来的并不适用于对时序要求很高的设计。

在设计任意小数分频中,如果只是使用FPGA逻辑资源 (即输入时钟clk_in,计数器,线网资源),不使用包括PLL锁相环等在内的外部资源和其他用于分频的外部电路,最终设计得到的时钟抖动都很大,就是高电平持续时间不一样,低电平持续时间也不一样。

比如说,要设计一个5.3分频的时钟,假设源时钟周期为10ns,那么最后设计出来的时钟一定不可能有任何一个周期为53ns(就是说不可能有任何两个上升沿之间的时间差为53ns)。只能说输出时钟clk_out 10个周期的时间持续为530ns,具体哪些周期是50ns,哪些是60ns,就看自己设计方式了

所以如果需要对时钟要求高的小数分频,最好使用PLL以及其他电路来得到所需时钟。

如果是需要N.5分频的话,可以看下面这个文章
小数分频之N.5分频原理及其实现(占空比非常接近0.5)

一、小数分频原理

假设clk_in的周期为10ns。
设计一个5.3分频的小数分频,上面也说了,不使用倍频等方式的话,不可能设计出每个周期都是53ns的时钟。大多数小数分频都是指一段时间内,平均周期达到53ns。那肯定有的周期小于60ns,有的大于50ns。当然,也可以一些周期10ns,一些周期100ns,只是时钟抖动有点大,也不符合小数分频设计的原则。

小数分频两个设计原则:时钟抖动小;占空比接近50%

怎样减小时钟抖动呢?
假设clk_in的周期为10ns。
减小时钟抖动就是让每个周期持续时间的差尽量小,比如设计5.3分频,设计不出53ns,那就只能退而求次,让周期靠近53ns,如果全是50ns或者60ns,那肯定连平均周期都达不到53ns了,所以我们需要50ns和60ns交替出现的方式来设计。

为了不会让时钟抖动很大,所以目前大多设计都采用m分频和m+1分频交叉的方式,这种方式大多被称为双模前置小数分频

1、双模前置小数分频原理:

小数分频也是分数分频,假设现在要设计一个36/8的小数分频,其实就是在36个clk_in的时间内输出8个周期的clk_out

36 / 8 = 4 (余数不计)

所以我们需要用4分频和5分频交叉,那4分频和5分频分别有多少个呢?

令4分频的周期要a个,5分频的周期要b个

总共需要输出8个周期,所以a+b = 8;
同时,我们要在36个clk_in内输出,所以 4a+5b = 36

解出来 a = 4,b = 4,就是说输出的8个周期内,我们需要输出4个4分频,4个5分频。

具体怎么输出,有几种方式:

      1、先输出4个4分频,再输出4个5分频;
      2、将4个4分频,再输出均匀的插入到4个5分频中输出;

总体来说,第二种的时钟抖动会更小,但是设计出来的时钟占空比大多与50%相差很大大多数不是低电平为一个脉冲就是高电平为一个脉冲
我看到华为有一个专利,可以让占空比接近50%,但是实现很难。

2、脉冲删除法

还有一种小数分频的设计方法,脉冲删除法,就是通过计数,删除一些不需要的脉冲,这种设计出来的和双模前置法的第二种输出很像,但是占空比也和50%相差较大。这个论坛上也有一些博客说这个的,不过不太多。

基于以上,我通过一天左右时间的研究,在双模前置法的基础上设计并实现了一种占空比为50%的小数分频方法:波形拼接

二、波形拼接

1、波形拼接原理

为了让输出波形能够有更低的抖动,我们需要m分频和m+1分频交替,这里我们注意到m分频和m+1分频必然一个是偶数分频,一个是奇数分频

还有一点更重要的是:不管是偶数分频还是奇数分频,他们一整个周期的长度总是一个clk_in周期的整数倍,这样我们的后续设计有了可能:因为在一个时钟的上升沿和下降沿进行操作时,总是很难把握。现在我们只需要在上升沿或者下降沿进行操作,这样难度大大降低。

目前有两个思路,一种稍微简单的,已经程序仿真验证了的,思路如下:(思路有点复杂,不难,就是很多细节,不好写,所以就放个大致思路,细节就不写了)

还是假设设计 36/8的小数分频

需要输出4个4分频,4个5分频

对m和m+1分别进行奇数分频和偶数分频(或者偶数分频和奇数分频)得到clk_o,和clk_j。我们需要它们的起始相位一样,这样我们就可以在上升沿或者下降沿对它们进行操作。

我们先输出所有次数的奇数分频(假如输出4个周期的奇数分频),然后再下一个要输出波形的时候,再输出从头开始的偶数分频(这里的从头开始的意思就是,当奇数分频的低电平结束,要上升之前,把偶数分频的高电平接在奇数分频后面)。具体原理如下:

在这里插入图片描述
上面的图不是36/8的分频图,只是解释波形拼接
如上图,先输出1-4个奇数分频,然后在5来之前,直接从6开始输出四个偶数分频,再然后,在9到来之前,再从1输出重复上述操作,这样就可以在clk_out的8个周期内输出36个clk_in,50%占空比。

得到了占空比为50% 的小数分频,这是目前已经实现了。

第二种思路是第一种的改进思路:就是让4个4分频和4个5分频均匀输出,这个实现就是稍微复杂一点

三、功能仿真效果截图

1、(13/5)

在这里插入图片描述

在这里插入图片描述

2、(53/10)

在这里插入图片描述
在这里插入图片描述

3、(36/8)

在这里插入图片描述
在这里插入图片描述

其他的小数分频,我就没有一一仿真了,最主要是vivado的自带仿真软件是真的难用(但是谁叫它更方便呢),modelsim还没关联(主要是懒得添加环境变量)
还有就是,我有个仿真用quartus 和modelsim仿真出来的是复合我预期的,但是用vivado自带的就完全不对,可能是我设计的问题

其他小数就自己下载程序仿真吧

程序代码

`timescale 1ns / 1ps

/*
思路如下:假设是53/10的分频,那么需要7次5分频,3次6分频。
1、我们先输出7/3=2个5分频,再进行波形拼接,输出1个6分频,这样我们需要cnt计数2*5+6 = 16,置1;
   重复以上操作,直到我们输出了6个5分频,3个6分频
2、具体顺序如下:2(5分频) 1(6分频) 2(5分频) 1(6分频)2(5分频) 1(6分频)
3、这时候,我们还差一个5分频,如果继续重复1而不加限制的话,那就会又输出2个5分频和1个6分频,最终导致波形错误
4、这时候我们就要加上限制条件,使用另一个计数器cnt_1,从1计数到53,限制条件就是:
   当cnt_1 == 53,就让cnt置1,重新开始,这样我们就只用输出最后一个5分频,然后又重复1.
   
5、因为我默认是让奇数分频先输出,这个在奇数分频次数大于等于偶数分频是没关系,但是,
   当奇数分频次数小于偶数分频次数的时候,一、我们就需要调整输出顺序,即先输出偶数分频,再输出奇数分频,这种较麻烦;
   	二、我们采用先输出一个奇数,在输出偶数
*/

module divider_anypoint_5 #(
		//parameter 			M = 53, 			
		//parameter 			N = 10,
		parameter 			a = 7,			// 进行奇数分频的次数
		parameter 			b = 3			// 进行偶数分频的次数
		)
		(
		input 				clk_in,
		input       		rst_n,
		input	[7:0]		M,
		input	[7:0]		N,
		input	[7:0]		M_N_1,			// 奇数
		input	[7:0]		M_N_2,			// 偶数
		
		output	reg		[7:0]			cnt,
		output	reg		[7:0]			cnt_1,			// 保证计数到M时,能够重头开始
		output				clk_m,
		output				clk_m_1,
		output	reg			en,	
		output	reg			en_1,
		
		output	[7:0]			a_b,
		
		output				clk_out
	);

	reg		[7:0]			cnt_a;			// 用于计算m分频次数
	reg		[7:0]			cnt_b;			// 用于计算m+1分频次数
	
	
	//wire	[7:0]			a_b ;
	//assign a_b = a/b;
	
	//wire					clk_out_1;
	//wire					clk_out_2;
	
	//wire					en_g;
	//wire					en_1_g;
	
	wire	[7:0]			cnt_a_b;		// 用于将m+1分频均匀插入到m分频中
	wire	[7:0]			cnt_b_a;		// 用于将m+1分频均匀插入到m分频中
	
	assign	cnt_a_b = (a>=b)?(a/b)*M_N_1:M_N_1;	// a>=b先计数两个5(奇数)分频,a<b先计数两个(偶数)分频
	assign	cnt_b_a = (a>=b)?M_N_2:(b/a)*M_N_2;	// b>a
	
	//wire					clk_m;			// m分频时钟
	//wire					clk_m_1;		// m+1分频时钟
	//wire					en;				// 使能分频信号	
	
	
	assign clk_out = (en_1 == 1 )?clk_m:clk_m_1;	// a>=b先输出clk_m奇数分频;a<b先输出clk_m_1偶数分频
	
	always@(posedge clk_in or negedge rst_n) begin
		if(!rst_n)
			en <= 1'b0;
		//else if(cnt_1 == M-1)
		//	en <= 1'b0;
		else if(cnt ==(cnt_a_b-1))			
			en <= 1'b1;
		//else if(cnt ==(cnt_a_b+M_N_2))		这是专用于奇数分频次数大于等于偶数分频次数
		else if(cnt ==(cnt_a_b+cnt_b_a))			
			en <= 1'b0;
		else 
			en <= en ;
	end
	
	always@(negedge clk_in or negedge rst_n) begin
		if(!rst_n)
			en_1 <= 1'b1;
		//else if(cnt_1 == M-1)
		//	en_1 <= 1'b1;
		else if(cnt ==(cnt_a_b-1))			
			en_1 <= 1'b0;
		//else if(cnt ==(cnt_a_b+M_N_2))
		else if(cnt ==(cnt_a_b+cnt_b_a))			
			en_1 <= 1'b1;
		else 
			en_1 <= en_1 ;
	end

		
	always@(posedge clk_in or negedge rst_n) begin
		if(!rst_n)
			cnt <= 8'd0;
		//else if((cnt == cnt_a_b+M_N_2) || (cnt_1 == M))		// cnt_1 == M  保证计数到53会重新开始(假设53/10)
		else if((cnt == cnt_a_b+cnt_b_a) || (cnt_1 == M))		// cnt_1 == M  保证计数到53会重新开始(假设53/10)
			cnt <= 8'd1;
		else 
			cnt <= cnt + 1'b1;
	end
	always@(posedge clk_in or negedge rst_n) begin
		if(!rst_n)
			cnt_1 <= 8'd0;
		else if(cnt_1 == M)
			cnt_1 <= 8'd1;
		else 
			cnt_1 <= cnt_1 + 1'b1;
	end
	
	
	 //如果m为奇数,则进行奇数分频
	divider_odd 	
	divider_odd(
		.M_N				(M_N_1),
		.sys_clk			(clk_in),
		.sys_rst_n			(rst_n && en_1),
		.div_clk			(clk_m)
		);
		
	//如果m为偶数,则进行偶数分频
	divider_even 	
	divider_even(
		.sys_clk			(clk_in),
		.M_N				(M_N_2),
		.sys_rst_n			(rst_n && en),
		.div_clk			(clk_m_1)
		);

endmodule
`timescale 1ns / 1ps
/********** 任意偶数分频器************/


module divider_even	#(parameter DIV = 4)(					
	input			sys_clk,			
	input			sys_rst_n,
	input	[7:0]	M_N,			// 取代DIV了

	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)
			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

/********** 任意奇数分频器************/

module divider_odd #(parameter DIV = 3)(					
	input					sys_clk,
	input					sys_rst_n,
	input	[7:0]			M_N,			// 取代DIV了
	input					en,
	
	
	output	reg		clk1,
	output	reg		clk2,
	
	output				div_clk
	);
	
	reg		[7:0]		cnt1;
	reg		[7:0]		cnt2;
	
	//reg					clk1;
	//reg					clk2;
	
	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
	

仿真程序

`timescale 1ns/1ns
`define clk_period 20


module divider_anypoint_5_tb();

//parameter 			M = 53; 			
//parameter 			N = 10;

/*  a 和   b一定要自己算出来  .这改变这四个就可以了 */
parameter 			a = 9;			// 进行m分频的次数,这个手算
parameter 			b = 4;			// a为算出来奇数分频的个数,b为偶数分频的个数
wire	[7:0]	M = 35;				// 用这个方便知道M/N是奇数还是偶数,分子
wire	[7:0]	N = 13;				// 分母
	

reg				clk;
reg				rst_n;
wire	[7:0]	M_N_1;
wire	[7:0]	M_N_2;
wire	[7:0]	M_N;

assign M_N = M/N;				// 判断M/N是奇数还是偶数


assign M_N_1 = (M_N[0] == 1)? (M_N): (M_N+1);		 // 如果M/N为奇数
assign M_N_2 = (M_N[0] == 1)? (M_N+1): (M_N);		 // 如果M/N为奇数


wire			clk_out;

wire	[7:0]	cnt;
wire	[7:0]	cnt_1;
wire			clk_m;
wire			clk_m_1;
wire			en;
wire			en_1;

wire	[7:0]			a_b;



initial clk = 0;
	always #(`clk_period/2) clk = ~clk;
	
	initial begin
		rst_n = 1;
		#20;
		rst_n = 0;
		#30;
		rst_n = 1;
		//$stop;
	end
  
   divider_anypoint_5  #(
   			.a(a),
   			.b(b)
   			)
   		divider_anypoint_5(
			.clk_in 	(clk),
			.rst_n 		(rst_n),
			.cnt		(cnt),
			.cnt_1		(cnt_1),
			.M			(M),
   			.N			(N),
			.clk_m		(clk_m),
			.clk_m_1	(clk_m_1),
			.en			(en),
			.en_1		(en_1),
			.M_N_1		(M_N_1),
			.M_N_2		(M_N_2),
			//.a_b		(a_b),
			.clk_out   	(clk_out )
        );
endmodule

改进版链接:小数分频之任意小数分频(二)(占空比50%,时钟抖动较小)

0 C币程序下载链接:任意小数分频(占空比50%)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值