三、分频电路
(1)简单的计数器
计数器实质是对输入的驱动时钟进行计数,所以计数器在某种意义上讲,等同于对时钟进行分频。例如一个最大计数长度为N=2^n(从0计数到N-1)的计数器,也就是寄存器位数位n,那么寄存器最高位的输出为N=2^n分频,次高位为N/2分频...例如下面的代码:
module test#(parameter N=3)(
input clk,
input rst_n,
output clk_div
);
reg [N-1:0] div_reg ;//分频计数器
always @(posedge clk or negedge rst_n)
if (rst_n == 1'b0 )
div_reg <= 0 ;
else
div_reg <= div_reg + 1'b1 ;
assign clk_div = div_reg[N-1] ;
endmodule
该代码描述的将一个3位的计数器最高位输出,也就是计数长度为8(计数从0~7)波形如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuaHlIdGNpYW1UaWMyTXBudTJJWmxRTldjY0s1b2JyV1ZwQ1NpYlppYXdsd2xLaWNPM2ZJV2dYQVlFOEEvNjQw?x-oss-process=image/format,png)
可以看到最高位的输出为输入时钟的8分频。
当N不是2的整数次幂时,即N≠2^n时,从0计数到N-1,其最高位作为时钟输出(占空比不一定为 1:1)是输入时钟的1/N,也就是N分频。我们来举个例子,比如最大计数长度为5的计数器,即从0计数到4后又返回0,那么需要定义一个三位的寄存器。寄存器的计数过程为:
000-001-010-011-100-000-001-010-011-100-000-001-010-011-100-000-001-010-011-100······
我们取最高位,得到的信号变化就是:
0-0-0-0-1-0-0-0-0-0-1-0-0-0-0-1-0-0-0-0-1···
代码如下所示:
module test#(parameter N=3)(
input clk,
input rst_n,
output clk_div
);
reg [N-1:0] div_reg ;//分频计数器
always @(posedge clk or negedge rst_n)
if (rst_n == 1'b0 )
div_reg <= 0 ;
else if(div_reg == 3'd4)//从0计数到4,然后返回到0,5分频
div_reg <= 0;
else
div_reg <= div_reg + 1'b1 ;
assign clk_div = div_reg[N-1] ;
endmodule
仿真波形如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZubndqdk1hQVByZ1kwcWlhN2E2V2ljdU8yVnNsWjZSY2lhc3h4TDc5WXZWYWowQ01DcWdsaDR2clFRLzY0MA?x-oss-process=image/format,png)
由此可以看到,每一个分频后的时钟周期=5倍原来的时钟周期,因此是5分频。
那么这个情况是不是也可以包含第一种情况呢?我们那设置为8分频看看,即前面的3'd4改成3'd7,得到的仿真波形如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuZkZmQWtMM25pY1ZYbjVudkxEZkdwNmJqOFRxeXZBbWsxTmo4SUpuVmxsZ2liZG5sWk4zVEl5ckEvNjQw?x-oss-process=image/format,png)
可以看到,计数器的最高位输出也是输入频率的1/N。
因此我们得到结论:一个最大计数长度为N(从0计数到N-1)的计数器,其最高位的输出,是输入频率的N分频。
通常 ASIC 和 FPGA 中,时钟都是全局信号,都需要通过 PLL 处理才能使用,但某些简易场合,采用计数器输出时钟也是能够使用的,只是需要注意时序约束。
(2)偶数倍分频(占空比50%)
偶数分频,也就是2分频、4分频、6分频...这个还是比较简单的,N(N当然是2的倍数)分频,那么计数到N/2-1,然后时钟翻转:
例如N=6时,代码如下所示:
module test#(parameter N=6)(
input clk,
input rst_n,
output clk_div
);
reg div_reg ;
reg [N-1:0] div_cnt ;//分频计数器
always @(posedge clk or negedge rst_n)
if (rst_n == 1'b0 )begin
div_cnt <= 0 ;
div_reg <= 0 ;
end
else if(div_cnt == (N/2 - 1))begin
div_cnt <= 0;
div_reg <= ~div_reg ;
end
else
div_cnt <= div_cnt + 1'b1 ;
assign clk_div = div_reg ;
仿真波形如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuZnAwazFBZ0ZIalJkemliM0VMZ0RFN1JaOXZSeHdhVm4xV1poZGhDMFFXaWFhOHZ0MU9kcm1mancvNjQw?x-oss-process=image/format,png)
当N=2的仿真波形如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuS2liRzRpYmY3NUhiZGlhMG5RSGg2T2ZEMnh5cmFkcFF2cjY4dzhVb2ljSWhQekhGVDBVcXBNdDNyQS82NDA?x-oss-process=image/format,png)
(3)奇数倍分频
①占空比接近50%
对于占空比不是50%的计数分频,我们可以直接用上面的计数器方法,这里就不说了,我们介绍其他接近50%的占空比的方法,比如下面使用的状态机分频:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZueGVpYjZsMk5DdEZzY2h1RHNXMFZ5YWdTZjEwRDkySFBxaWNHODF3TElET2RsM0VtREtlOGVGZmcvNjQw?x-oss-process=image/format,png)
上图的状态机除了用一般的状态机设计方式之外,我们也可以用简单的计数器实现,这种方法如下所示:
假设时钟分频是N,则设置一个计数器,计数长度是N(即从0计数到N-1),然后在计数器为计数到(N-1)/2的时候,翻转一下分频时钟信号;在计数器计数到为N-1的时候,再翻转一下时钟。
代码如下所示:
module test#(parameter N=3)(//N分频,这里是3分频
input clk,
input rst_n,
output clk_div
);
reg [N-1:0] div_cnt ;//分频计数器
reg div_reg ;
always @(posedge clk or negedge rst_n)begin
if (rst_n == 1'b0 )begin
div_cnt <= 0 ;
div_reg <= 1 ;
end else if (div_cnt == (N-1)/2)begin//计数到(N-1)/2,进行翻转和继续计数
div_reg <= ~div_reg;
div_cnt <= div_cnt + 1'b1 ;
end else if ( div_cnt == (N-1) )begin//计数到N-1,进行清零和翻转
div_cnt <= 0 ;
div_reg <= ~div_reg;
end else
div_cnt <= div_cnt + 1'b1 ;
end
assign clk_div = (N == 1)?clk:div_reg ;//注意这里
endmodule
代码中我们需要注意,在N= 1的情况,也就是不分频的情况。仿真电路如下图所示:
3分频,N = 3:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuTVpGSWdpYmliVGdOdzJ3QW9hdUxjeUZ4NGxMVDNNUkJoT1dlMzFGZmFjZEJLR1JLV1JBQjdEekEvNjQw?x-oss-process=image/format,png)
5分频,N= 5 :
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuTFp0ejZBUzd6b3VHaWJlWHdOQ0dwQWljc3Bqc2ljdWNYQmV0VDJpYWoxb1c0VkxpYWVHYjdOOUZTTlEvNjQw?x-oss-process=image/format,png)
不分频,即N=1的仿真如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuY2QwSnE1cGR5RlJNTWQ5Q2lhR0xyTmxzQW4yZ1pNNmFHQmx2cHg0UmljbktkWmtkUWlhNnRpYlpRQS82NDA?x-oss-process=image/format,png)
②占空比50%
产生具有50%占空比的奇数分频时钟的算法如下所示,假设N分频(N是计数):
设置一个计数长度为N的上升沿计数器,和一个信号寄存器;信号寄存器在上升沿计数器为(N-1)/2的时候进行翻转,然后再在计数到N-1的时候进行翻转(这里相当于得到一个N分频信号A)。
再设置一个计数长度为N的下降沿计数器,和另一个信号寄存器;信号寄存器在下降沿计数器为(N-1)/2的时候进行翻转,然后再在计数到N-1的时候进行翻转(这里相当于得到一个N分频信号B)。
将A和B相或就可以得到占空比50%的奇数分频信号;代码实现如下:
module test#(parameter N=5)(//N分频
input clk,
input rst_n,
output clk_div
);
reg sig_r ;//定义一个上升沿翻转的信号
reg sig_f ;//定义一个下降沿翻转的信号
reg [N-1:0] cnt_r;//上升沿计数器
reg [N-1:0] cnt_f;//下降沿计数器
wire clk_f ;
assign clk_f = ~clk ;//用来触发下降沿计数器的时钟
//由于同时使用上升沿和下降沿触发器不好,因此我们为同一边沿,都使用上升沿触发
//只不过是将时钟进行反向
always @(posedge clk or negedge rst_n)begin//上升沿计数
if(rst_n == 1'b0)begin
sig_r <= 0 ;
cnt_r <= 0 ;
end else if( cnt_r == (N-1)/2 )begin
sig_r <= ~sig_r ;
cnt_r <= cnt_r + 1 ;
end else if ( cnt_r == (N-1) )begin
sig_r <= ~sig_r ;
cnt_r <= 0 ;
end else
cnt_r <= cnt_r + 1 ;
end
always @(posedge clk_f or negedge rst_n)begin//下降沿计数
if(rst_n == 1'b0)begin
sig_f <= 0 ;
cnt_f <= 0 ;
end else if( cnt_f == (N-1)/2 )begin
sig_f <= ~sig_f ;
cnt_f <= cnt_f + 1 ;
end else if ( cnt_f == (N-1) )begin
sig_f <= ~sig_f ;
cnt_f <= 0 ;
end else
cnt_f <= cnt_f + 1 ;
end
assign clk_div = sig_f || sig_r ;
endmodule
仿真波形如下所示:
3分频:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuckFPZU4zRXoyMzZVQkNsQUVuZ2Z2b244M3BiUHRrQWtObTVCaWFtN1pRUHg0MExpY245dGRUeWcvNjQw?x-oss-process=image/format,png)
5分频:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuZ3RrOUNvZExqMUdvc1pxdnNqYWVvZ2JGMFh1eU5UQjZnNlRCbU9yZGZXeFYwS01KeGU3V013LzY0MA?x-oss-process=image/format,png)
(4)任意整数倍分频(接近50%)
在前面中,我们知道了一个最大计数长度为N(从0计数到N-1)的计数器,其最高位的输出,是输入频率的N分频,因此最简单的任意分频电路就是设计一个计数器,然后最高位输出就是分频的频率了。虽然这这种方法很简单,但是很显然,这种方法的占空比是很糟糕的。因此我们要用其他的方法,也就是用其他的组合方式。
①占空比接近50%任意整数分频
这种方法是取自偶数分频和奇数分频里面的接近50%占空比,实现的代码如下所示:
module test #( parameter cfactor= 5)(
input clk,
input rst_n,
output clk_div
);
reg clk_loc;
//reg [15:0] cnt;//allowed maximum clock division factor is 65536
reg [7:0] cnt;//allowed maximum clock division factor is 256
assign clk_div = (cfactor==1)? clk : clk_loc;
//assign clk_div = ((rst==1) || (cfactor==1))? clk : clk_loc;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
cnt <= 'd0;
clk_loc = 1;
end
else begin
cnt <= cnt + 1'b1;
if(cnt==cfactor/2-1)
clk_loc = 0;
else if(cnt==cfactor-1) begin
cnt <= 'd0;
clk_loc = 1;
end
end
endmodule
2分频的仿真图,如下所示: ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuWE5nQlVraWJLSzZZQUlBU3FQZ1RPaWE2RlN1ejVFZWRsT3lKMzV5dzNXZUtMb2ljNFJRZjJKRlVRLzY0MA?x-oss-process=image/format,png)
5分频的仿真波形如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZucUV4ZHNuY3ZINmtnZmU2M3dXb1NzcjZpY3hhTEtzZ3lXNzk1ejRNS0pCenJ6QUd1a1JjQ1hIdy82NDA?x-oss-process=image/format,png)
②占空比50%的任意整数分频(重点)
这种方法是取自偶数分频和奇数分频都是50%占空比的组合,代码如下所示:
module test#(parameter N=1)(//N分频
input clk,
input rst_n,
output clk_div
);
//奇数分频
reg sig_r ;//定义一个上升沿翻转的信号
reg sig_f ;//定义一个下降沿翻转的信号
reg [N-1:0] cnt_r;//上升沿计数器
reg [N-1:0] cnt_f;//下降沿计数器
wire clk_f ;
assign clk_f = ~clk ;//用来触发下降沿计数器的时钟
//由于同时使用上升沿和下降沿触发器不好,因此我们为同一边沿,都使用上升沿触发
//只不过是将时钟进行反向
always @(posedge clk or negedge rst_n)begin//上升沿计数
if(rst_n == 1'b0)begin
sig_r <= 0 ;
cnt_r <= 0 ;
end
else begin
cnt_r <= cnt_r + 1 ;
if( cnt_r == (N-1)/2 )begin
sig_r <= ~sig_r ;
end else if ( cnt_r == (N-1) )begin
sig_r <= ~sig_r ;
cnt_r <= 0 ;
end
end
end
always @(posedge clk_f or negedge rst_n)begin//下降沿计数
if(rst_n == 1'b0)begin
sig_f <= 0 ;
cnt_f <= 0 ;
end
else begin
cnt_f <= cnt_f + 1 ;
if( cnt_f == (N-1)/2 )begin
sig_f <= ~sig_f ;
end else if ( cnt_f == (N-1) )begin
sig_f <= ~sig_f ;
cnt_f <= 0 ;
end
end
end
//偶数分频
reg div_reg ;
reg [N-1:0] div_cnt ;//分频计数器
always @(posedge clk or negedge rst_n)begin
if (rst_n == 1'b0 )begin
div_cnt <= 0 ;
div_reg <= 0 ;
end
else begin
div_cnt <= div_cnt + 1'b1 ;
if(div_cnt == (N/2 - 1))begin
div_cnt <= 0;
div_reg <= ~div_reg ;
end
end
end
assign clk_div = (N == 1)?clk:
( N%2 == 1)?(sig_f || sig_r ): div_reg;//这里用来输出分频值。对2的取余操作是综合的
endmodule
仿真波形如下所示:
5分频:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuRjlWYnNKd1dTamljMGNTdHQ5clN2QUh6dUZKelh6aWNFZ2ppY2V5aWFOYjlXdk95aWN6bW53czJXaWFnLzY0MA?x-oss-process=image/format,png)
6分频: ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuRjIwRkNNaWNDZFNzR2J2bkZLbjdqcHJKeENvUnRXS1AzMjdHZ2NhZUtOWjRpYjR6aWNFRVRpY0VpY3cvNjQw?x-oss-process=image/format,png)
总结:本文介绍了计数器及其功能,主要是介绍了作为分频器的功能。对于分频器,如下所示:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9lNHZubkdyZlBmS2EzS2g5aWFYTTFjaWNHcHEwMG9aYWZuYTFCbEd3QUZvcWI4WVJ1dnhMQ1dpY1RkTkdMc01RaWJ5aWJGaWEwS0hDZDNOeTRMVGliS1F4eFg5MXcvNjQw?x-oss-process=image/format,png)
作者:IC_learner
博客链接:https://www.cnblogs.com/IClearner/p/7208871