奇偶分频的Verilog实现(占空比50%,IC面试必备)


分频器,常用于将输入的某个时钟频率的波形进行分频。分频大概可以分为两类: 奇数倍分频偶数倍分频再细分又可以将占空比作为一个考虑的因素,粗浅的分为 占空比为50%占空比不是50% 这两类。接下来细讲这四种分频的实现。

1 偶数倍分频(占空比为50%)

由于偶数倍分频很容易实现占空比为50%的偶数倍分频,因此不讨论占空比不为50%的偶数倍分频。

1.1用计数器来实现偶数倍的分频。

比如要实现一个分频数为DIV_NUM的偶数倍分频器,则我们将计数器从0计数到DIV_NUM/2-1,判断计数到该设定值后,让clk_div翻转一次,然后将计数器清零继续计数,如此循环,即可实现一个时钟周期相较于原时钟DIV_NUM倍的时钟。(分频即是将原时钟频率分为更慢的频率,倍频则相反)
在这里插入图片描述

代码如下:

module div_even #(
	paramter DIV_NUM = 20
)(
	input clk,
	input rst_n,
	output reg div_clk
);
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt <= 'b0;
	else if(cnt != DIV_NUM/2 - 1)
		cnt <= cnt + 1'b1;
	else
		cnt <= 'b0;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		div_ clk <= 'b0;
	else if(cnt == DIV_NUM/2 - 1)
		div_clk <= ~div_clk;
	else
		div_clk <= div_clk;
end
endmodule

1.2用触发器来实现偶数倍分频

触发器的Q端经过反向器送入D端,即可实现一个简单的2分频
在这里插入图片描述
若要实现 2 n 2^{n} 2n分频,可以通过级联2分频的模块,得到如下所示电路:
在这里插入图片描述
但是该方法只能实现 2 n 2^{n} 2n分频,即2、4、8、16等分频,并不能包含诸如10分频,20分频的情况。
因此还有一个通用的方法,通过移位寄存器来实现偶数倍分频。
若要实现DIV_NUM的分频,则需要DIV_NUM/2个寄存器。具体实现电路图如下所示:
在这里插入图片描述
虽然该方案比较通用,但该方案在分频数很大的时候,需要很多的寄存器,比较浪费资源,因此要酌情使用。(分频数小的时候可以使用触发器,分频数大的时候可以考虑用计数器)

2 奇数倍分频(占空比不为50%)

2.1 用模n计数器来实现奇数倍分频

所谓模n计数器,就是用计数器来实现一个模值为n的计数,从0计数到n-1,然后清零继续计数到n-1,然后清零,如此循环。将计数器的最高位作为分频信号输出,即可得到n分频(该方法奇偶分频都可以使用,但是不能保证50%的占空比)

例如:我们要实现一个11分频,我们设计一个模值为10的计数器,从0开始计数,计数到10后,清零,继续计数到10,清零,如此反复,然后我们将该计数器的最高位的值直接作为分频器的输出,即可得到11分频。

二进制计数值(十进制值)最高位值
0000(0)0
0001 (1)0
0010 (2)0
0011 (3)0
0100 (4)0
0101 (5)0
0110 (6)0
0111 (7)0
1000 (8)1
1001 (9)1
1010 (10)1

代码如下:

module div #(
	parameter N = 11
)(
	input clk,
	input rst_n,
	output clk_div
);

reg [3:0] div_cnt        ;//分频计数器
always @(posedge clk or negedge rst_n)begin
    if (rst_n == 1'b0 )
        div_cnt <= 0 ;
    else if(div_cnt == N-1)//从0计数到N-1,然后返回到0,即N分频
        div_cnt <= 0;
    else
        div_cnt <= div_cnt + 1'b1 ;
end

assign clk_div = div_cnt[3] ;
endmodule

原时钟周期10ns,经过11分频后,可以看到分频后的时钟周期为110ns。
在这里插入图片描述
因此我们得到一个通用的结论:一个最大计数长度为N(从0计数到N-1)的计数器,其最高位的输出,是输入频率的N分频。(奇偶分频通用,不能保证占空比)

3 奇数倍分频(占空比为50%)

3.1 用待分频时钟的上升沿和下降沿和改进的模n计数器,再或

如果要实现占空比为50%的奇数倍分频,可以通过待分频时钟的上升沿和下降沿触发分别计数(不是简单的计数),然后将上升沿和下降沿产生的时钟进行相或运算,即可得到占空比为50%的时钟。
具体的计数规则总结如下:
正沿和负沿分别都计数,计数到(N-1)/2时,翻转,然后继续计数,计数到N-1时,再翻转。然后计数器清零,继续计数,如此循环。(其实跟模n计数器一样,不过穿插了一个中间步骤:计数到(n-1)/2时,要翻转一次)
具体verilog实现代码为:

module div_odd#(

parameter DIV_NUM = 11 //分频数
)(

input clk,
input rst_n,
output clk_out

);

reg clk_p,clk_n;
reg [7:0] cnt_n,cnt_p;

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		clk_p <= 1'b0;
	else if(cnt_p == (DIV_NUM-1)/2)  //穿插了一个中间步骤:计数到(n-1)/2时,要翻转一次
		clk_p <= ~clk_p;
	else if(cnt_p == DIV_NUM -1)
		clk_p <= ~clk_p;
end

always@(negedge clk or negedge rst_n)begin
	if(!rst_n)
		clk_n <= 1'b0;
	else if(cnt_n == (DIV_NUM-1)/2)//穿插了一个中间步骤:计数到(n-1)/2时,要翻转一次
		clk_n <= ~clk_n;
	else if(cnt_n == DIV_NUM -1)
		clk_n <= ~clk_n;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt_p <= 'b0;
	else if(cnt_p == DIV_NUM-1)//计数器计数到n-1时,进行清零
		cnt_p <= 'b0;
	else
		cnt_p <= cnt_p + 1'b1;
end

always@(negedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt_n <= 'b0;
	else if(cnt_n == DIV_NUM-1)//计数器计数到n-1时,进行清零
		cnt_n <= 'b0;
	else
		cnt_n <= cnt_n + 1'b1;
end

assign clk_out = clk_p | clk_n; //将上沿和下沿得到的两个波形进行相或运算

endmodule

tb如下:

module div_tb();
parameter PERIOD = 10;
reg clk;
reg rst_n;
initial begin
	clk = 0;
	rst_n = 0;
	#10 rst_n =1;	
end
always #(PERIOD/2) clk = ~clk;

wire div_clk;
div_odd #(
.DIV_NUM(11)
)inst1(
	.clk(clk),
	.rst_n(rst_n),
	.clk_out(div_clk)
);
endmodule

还是进行11分频,得到的波形如下:
在这里插入图片描述
可以看到分频后时钟周期为110ns。

至于某些情形是:将上升沿和下降沿产生的时钟进行相与运算
其实这个方法和或的方法是一样,也是分别采样上升沿和下降沿,然后在等于n-1和(n-1)/2时翻转,得到两个信号。那为什么这个是与,而刚才的是或呢?主要是复位信号到来后,clk_p和clk_n的复位值为1,而不是0。

3.2 上沿和下沿错开计数,再异或

在讨论错开计数之前,我们先了解一下不错开计数会产生什么影响。(答案是:达不到占空比为50%)
用待分频时钟的上升沿和下降沿分别进行模DIV_NUM的计数,计数到DIV_NUM-1时,翻转。
最后再将两者的波形进行异或。
在这里插入图片描述

具体代码实现如下:

module div_odd #(
parameter DIV_NUM = 11
)(

input clk,
input rst_n,
output clk_out

);

reg [3:0] div_cnt1;
reg div_clk1;
always@(posedge clk or negedge rst_n)begin  //上升沿
	if(!rst_n)begin
		div_cnt1 <= 'b0;
		div_clk1 <= 'b0;
	end
	else if(div_cnt1 != DIV_NUM - 1)//模DIV_NUM计数器
		div_cnt1 <= div_cnt1 + 1;
	else begin
		div_cnt1 <= 0;
		div_clk1 <= ~div_clk1;//当计数值达到DIV_NUM-1时,信号进行翻转
	end
end

reg [3:0] div_cnt2;
reg div_clk2;
always@(negedge clk or negedge rst_n)begin	//下降沿
	if(!rst_n)begin
		div_cnt2 <= 'b0;
		div_clk2 <= 'b0;
	end
	else if(div_cnt2 != DIV_NUM - 1 )//模DIV_NUM计数器
		div_cnt2 <= div_cnt2 + 1;
	else begin
		div_cnt2 <= 0;
		div_clk2 <= ~div_clk2;//当计数值达到DIV_NUM-1时,信号进行翻转
	end
end

assign div_clk = div_clk1 ^ div_clk2;//两个波形进行异或

endmodule

还是以11分频为例,可以发现,两个模DIV_NUM计数器实现了奇分频,但是占空比不为50%
在这里插入图片描述
怎样才能在该方法的基础上得到占空比为50%呢?

其实只需要让上升沿计数的计数器与下降沿计数的计数器错开(DIV_NUM-1)/2拍即可实现。即让div_clk1与div_clk2错开(DIV_NUM-1)/2拍。我们知道错开拍子可以通过寄存器直接对div_clk1或者div_clk2打(DIV_NUM-1)/2拍,以让两个信号错开(DIV_NUM-1)/2拍,但是如果分频数越高,这样就会需要更多寄存器。因此不推荐使用。

我推荐的方式是让两个计数器错开计数,比如让div_cnt1复位一结束刚开始从0计数到DIV_NUM+(DIV_NUM-1)/2-1,然后再回到(DIV_NUM-1)/2,之后便一直从(DIV_NUM-1)/2计数到DIV_NUM-1+(DIV_NUM-1)/2循环。同时让div_clk1每次当div_cnt1等于DIV_NUM-1+(DIV_NUM-1)/2时进行翻转。这就做到了div_clk1与div_clk2错开(DIV_NUM-1)/2拍,从而使得最后得到的分频时钟占空比为50%。

代码:

module div_odd #(
parameter DIV_NUM = 11
)(

input clk,
input rst_n,
output clk_out

);

reg [3:0] div_cnt1;
reg div_clk1;
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		div_cnt1 <= 'b0;
		div_clk1 <= 'b0;
	end
	else if(div_cnt1 != DIV_NUM - 1 + (DIV_NUM-1)/2) //多加了一个 (DIV_NUM-1)/2
		div_cnt1 <= div_cnt1 + 1;
	else begin
		div_cnt1 <= (DIV_NUM-1)/2;                  //复位的值为 (DIV_NUM-1)/2
		div_clk1 <= ~div_clk1;
	end
end

reg [3:0] div_cnt2;
reg div_clk2;
always@(negedge clk or negedge rst_n)begin
	if(!rst_n)begin
		div_cnt2 <= 'b0;
		div_clk2 <= 'b0;
	end
	else if(div_cnt2 != DIV_NUM - 1 )
		div_cnt2 <= div_cnt2 + 1;
	else begin
		div_cnt2 <= 0;
		div_clk2 <= ~div_clk2;
	end
end

assign div_clk = div_clk1 ^ div_clk2;

endmodule

仿真波形如下(11分频示例),分频输出时钟为div_clk:

在这里插入图片描述

总结

如果想要直接进行分频,不要求占空比的话,直接上模n计数器即可。
如果想要进行占空比50%的偶数倍分频,直接用计数器计数到DIV_NUM/2-1,然后信号翻转即可。
如果想要进行占空比50%的奇数倍分频,有两个方法可供选择,不过我还是推荐用3.1的方法。两个沿的计数器计数到 (DIV_NUM-1) / 2DIV_NUM-1 时,信号分别翻转一次,最后将两个信号进行或操作即可。

注:部分方法参考公众号:小鱼学IC

  • 13
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值