每日工作记录——任意小数分频研究

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/moon9999/article/details/77678549

最近由于准备面试,任意小数分频设计似乎是比较常问的问题。对于任意小数分频,常用的方法有双模前置小数分频和脉冲删除小数分频。前一种方法设计较为复杂,因此主要以第二种方式为主设计了一下。


任意小数均可以化为分数,例如要进行5.3分频即53/10分频,因此之后全部以分数来表示。


以13/4分频为例,我们首先要想明白什么是13/4分频。什么是2分频呢?就是每两个输入时钟得到一个输出时钟,4分频就是4/1即四个输入时钟得到一个输出时钟,因此13/4分频其实就是13个输入时钟得到4个输出时钟,想明白这一点很重要。


双模前置小数分频设计中,虽然这个设计我还没完成不过也提一下,是通过分数值的前后两个正数数分频选择输出得到最终结果的。对于13/4而言:

M = 13/4 = 3 ... 1
这意味着13/4的分频可以通过3分频和4分频选择输出得到,继续计算:

a + b = 4
3a + 4b = 13
得到a=3,b=1。也就是说通过3个3分频和1个4分频可以得到13/4分频。


在不考虑其他情况仅仅做简单选择输出的话,可以画出这样的时序图:

可以看到通过3分频和4分频“凑”出了13/4分频,不过这样的时钟我觉得用处不大。当然了真正这样设计时不能这样简单的先3个3分频再1个4分频,还有一些处理,这里就先不提了。


还是回到刚刚话,13/4分频其实就是13个输入时钟得到4个输出时钟,因此脉冲删除小数分频相对比较简单。意思就是在13个输入时钟里我删掉9个时钟周期,这样不就书粗了4个时钟周期了么,就是这样。那应该怎么删呢,查了一些论文后得到结论:

1.设置寄存器cnt位宽自定,我用的[7:0],初始值为0;

2.在clk_in的上升沿+4,并判断是否大于13,若大于13在下一周期-13;

3.cnt小于13时候,删除脉冲信号delete=1,大于13时候delete=0;

说起来比较乱,画个表表示下,每12个周期我们作为一个循环来看:

时钟序号 cnt值 是否删除
0 4 Y
1 8 Y
2 12 Y
3 (16->)3 N
4 7 Y
5 11 Y
6 (15->)2 N
7 6 Y
8 10 Y
9 (14->)1 N
10 5 Y
11 9 Y
12 (13->)0 N
0 4  
从表中可以看到每13个周期有4个周期没有被删除,刚好满足要求。

我们再来试一个,11/9吧:
时钟序号 cnt值 是否删除
0 9 Y
1 18->7 N
2 16->5 N
3 14->3 N
4 12->1 N
5 10 Y
6 19->8 N
7 17->6 N
8 15->4 N
9 13->2 N
10 11->0 N
0 9  
可以看到11个时钟里有删除了2个,输出了9个,完美。

下面以代码用进行测试设计,代码中fraction是分频的分子,denominator 是分母,以参数形式设置,testbench中进行传入。
module DIV(
	input clk_in			,
	input rst				,

	output clk_out
	);
parameter fraction = 1;
parameter denominator = 1;

reg  [7:0]cnt;
reg delete;
always @(posedge clk_in or posedge rst) begin
	if (rst) begin
		// reset
		cnt <= 0;
		delete <= 0;
	end
	else if (cnt > fraction) begin
		cnt <= cnt + denominator - fraction;
		delete <= 0;
	end
	else begin
		cnt <= cnt + denominator;
		delete <= 1;
	end
end

assign clk_out = delete ? 1 : clk_in;
endmodule

testbench文件,测试21/8分频。cnt_in和cnt_out仅仅是为了方便数输入时钟和输出时钟而设置的,没有实际意义:
module tb;

	// Inputs
	reg clk_in;
	reg rst;


	// Outputs
	wire clk_out;
	
	parameter fraction = 21;
	parameter denominator = 8;
	// Instantiate the Unit Under Test (UUT)
	DIV #(
		.fraction(fraction),
		.denominator(denominator)
		)
	uut (
		.clk_in(clk_in),
		.rst(rst),
		.clk_out(clk_out)
	);

	initial begin
		clk_in = 0;
		forever #2 clk_in = ! clk_in;
	end
	
	initial begin
		rst = 1;
		forever #50 rst = 0;
	end
   
reg [7:0]cnt_in, cnt_out;
always @(posedge clk_in or posedge rst) begin
	if (rst) begin
		// reset
		cnt_in <= 0;
	end
	else if (cnt_in == fraction) begin
		// reset
		cnt_in <= 1;
	end
	else begin
		cnt_in <= cnt_in + 1;
	end
end

always @(posedge clk_out or posedge rst) begin
	if (rst) begin
		// reset
		cnt_out <= 0;
	end
	else if (cnt_out == denominator) begin
		// reset
		cnt_out <= 1;
	end
	else begin
		cnt_out <= cnt_out + 1;
	end
end
	
endmodule

仿真波形图:

从红框区域可以看得出,21个输入周期刚好输出8个输出周期,当然这计数可以不怎么看,我们自己会数嘛。

ps.
在网上还看到了另外一种方式,感觉思路有点相似,也是相加超过分子后就减去分子再相加,把实现的代码贴在这里:
module DIV(
	input clk_in			,
	input rst				,

	output reg clk_out
	);
parameter fraction = 1;
parameter denominator = 1;

reg  [8:0]cnt;
wire vld;

assign vld 	 = ((cnt >= fraction>>1) && (cnt < fraction)) ? 1 : 0;

always @(posedge clk_in or posedge rst) begin
	if (rst) begin
		// reset
		cnt <= 0;
	end
	else if(cnt > fraction) begin
		cnt <= cnt + denominator - fraction;
	end
	else begin
		cnt <= cnt + denominator;
	end
end

always @(posedge clk_in or posedge rst) begin
	if (rst) begin
		// reset
		clk_out <= 0;
	end
	else if (vld) begin
		clk_out <= 1;
	end
	else begin
		clk_out <= 0;
	end
end

endmodule

同样用之前的testbench做21/8的分频,看波形:

的确是实现了目的,不过好乱的波形呀。

猜你喜欢

转载自blog.csdn.net/moon9999/article/details/77678549