当前位置:   article > 正文

数字分频器设计(偶数分频、奇数分频、小数分频、半整数分频、状态机分频|verilog代码|Testbench|仿真结果)

数字分频器

在这里插入图片描述



数字IC经典电路设计
经典电路设计是数字IC设计里基础中的基础,盖大房子的第一部是打造结实可靠的地基,每一篇笔者都会分门别类给出设计原理、设计方法、verilog代码、Testbench、仿真波形。然而实际的数字IC设计过程中考虑的问题远多于此,通过本系列希望大家对数字IC中一些经典电路的设计有初步入门了解。能力有限,纰漏难免,欢迎大家交流指正。

快速导航链接如下:

个人主页链接
1.数字分频器设计
2.序列检测器设计
3.序列发生器设计
4.序列模三检测器设计
5.奇偶校验器设计
6.自然二进制数与格雷码转换



一、前言

作为IC设计中经典电路之一,数字分频器在IC(集成电路)设计中有广泛的应用。以下是数字分频器在IC设计中的一些应用:

时钟发生器:时钟发生器的原理是时钟分频,数字分频器可以用来将时钟信号分频为所需的频率。例如,如果需要一个1Hz的时钟信号,可以使用数字分频器将10Hz的时钟信号分频为1Hz,满足模块时序要求外还可以达到降低功耗的作用。时钟发生器是数字系统中非常重要的组件,你就说重不重要!

数字锁相环(DLL):数字分频器可以用于数字锁相环的设计中,以实现时钟的相位同步。在 IC 设计中,时钟同步是非常重要的一部分,因为时钟信号的稳定性和精度直接影响到整个系统的性能和可靠性。数字锁相环是数字系统中的一种重要的时钟同步技术之一。你就说重不重要!

数字频率合成器(DDS):数字分频器可以用于数字频率合成器的设计中,以产生所需的频率。在频率合成器中,数字分频器可以用于将高频信号分频为多个低频信号,然后通过DSP进行数字信号处理和合成,最终生成一个高频信号。虽然分频只能将高频分解成低频信号,但是与DSP结合可以合成高频信号。可分解高频信号亦可合成高频信号,你就说重不重要!

总之,数字分频器在IC设计中有广泛的应用。它是数字系统中重要的组件之一,可以实现各种复杂的数字信号处理和时钟同步技术。它是现代电子技术中不可或缺的一部分。所以掌握数字分频器的设计是十分重要的!

二、偶数分频

2.1 触发器级联法

采用触发器反向输出端连接到输入端的方式,寄存器级联法能实现2^N的偶数分频,具体是采用寄存器结构的电路,每当时钟上升沿到来的时候输出结果进行翻转,以此来实现偶数分频。
根据以上原理,可实现简单的 2 分频电路,以此为基础进行串联,可构成 4 分频和8 分频电路。电路结构如下图所示,用 Verilog 描述时只需使用简单的取反逻辑即可。

在这里插入图片描述

在此基础上可画出2分频、4分频、8分频电路的波形图(图由TimeGen绘制,该软件功能实用,推荐使用),如下图所示。

在这里插入图片描述

2分频设计:只需要使用基准时钟在第1个时钟周期输出高电平(或低电平),在第2个时钟周期输出相反电平。
同理,4分频设计:使用基准时钟在第1、2个时钟周期输出高电平(或低电平),在第3、4个时钟周期输出相反电平。
同理,8分频设计:使用基准时钟在第1、2、3、4个时钟周期输出高电平(或低电平),在第5、6、7、8个时钟周期输出相反电平。

2.2 计数器法

如果偶数分频系数过大或者寄存器级联法无法实现对应的分频,可以采用计数器法进行分频,计数器法可以实现任意偶数分频。在计数周期达到分频系数中间数值 (N/2-1) 时进行时钟翻转,可保证分频后时钟的占空比为 50%。
Tips:中间数值(N/2-1) 需要减1是因为从0开始计数

以六分频为例,电路需要实现的是:计数器从0开始计数至2,计数器到0时信号翻转,具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用)。
在这里插入图片描述

因为是偶数分频,只要对分频系数中间数值进行循环计数,在对应的地方让信号进行反转即可得到任意分频的分频器。

2.3 verilog代码

//偶数分频电路设计(2分频、4分频、8分频、6分频)
//触发器法实现2分频、4分频、8分频
//计数器法实现6分频
module clk_div_even(
    input 		rst_n,			//复位信号
    input 		clk,			//源时钟信号
    output 		clk_div2,		//输出2分频
    output 		clk_div4,		//输出4分频
    output 		clk_div6,		//输出6分频
    output 		clk_div8		//输出8分频
    );

//定义4个中间寄存器和1个计数器    
reg         clk_div2_r;
reg 		clk_div4_r;
reg 		clk_div6_r;
reg 		clk_div8_r;
reg [3:0] cnt;

//2分频时钟输出模块
//源时钟上升沿触发,低电平异步复位
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		//低电平复位
        clk_div2_r <= 1'b0;
    end
    else begin
        clk_div2_r <= ~clk_div2_r;		//源时钟上升沿信号翻转得到2分频时钟
    end
end

assign clk_div2 = clk_div2_r;		//延时输出,消除亚稳态

//4分频时钟输出模块
//2分频时钟上升沿触发 低电平异步复位
always @(posedge clk_div2 or negedge rst_n) begin
    if (!rst_n) begin
        clk_div4_r <= 1'b0;
    end
    else begin
        clk_div4_r <= ~clk_div4_r;		//2分频时钟上升沿信号翻转得到4分频时钟
    end
end

assign clk_div4 = clk_div4_r;		//延时输出,消除亚稳态

//8分频时钟输出模块
//4分频时钟上升沿触发 低电平异步复位
always @(posedge clk_div4 or negedge rst_n) begin
    if (!rst_n) begin
        clk_div8_r <= 'b0;
    end
    else begin
        clk_div8_r <= ~clk_div8_r;		//4分频时钟上升沿信号翻转得到8分频时钟
    end
end
    
assign clk_div8 = clk_div8_r;		//延时输出,消除亚稳态

//计数器模块
//源时钟上升沿触发,低电平异步复位
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		//低电平复位
        cnt    <= 4'b0 ;
    end
    else if (cnt == 2) begin		//计数器从0计数,到2清零
        cnt    <= 4'b0 ;
    end
    else begin				//计数累加
        cnt    <= cnt + 1'b1 ;
    end
end

//6分频时钟输出模块
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div6_r <= 1'b0;
    end
    else if (cnt == 2 ) begin		//3个周期信号翻转得到6分频时钟
        clk_div6_r <= ~clk_div6_r;
    end
end
    
assign clk_div6 = clk_div6_r;		//延时输出,消除亚稳态

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

2.4 Testbench

`timescale 1ns/1ns		//时间刻度:单位1ns,精度1ns

module clk_div_even_tb;

//信号申明
 reg 		clk ;
 reg 		rst_n;
 wire 		clk_div2;
 wire 		clk_div4;
 wire 		clk_div6;
 wire 		clk_div8;

//定义源时钟信号一周期时间
parameter DIV_CLK = 10;

//复位信号生成
initial begin
    clk = 0;		//时钟信号赋初值
    rst_n = 1;		//复位信号赋初值
    #(1.5*DIV_CLK) rst_n = 0;
    #DIV_CLK rst_n = 1;
    #(30*DIV_CLK);
end

//源时钟信号生成
always #(DIV_CLK/2) clk = ~ clk;

//模块例化
clk_div_even u_clk_div_even
    (
    .clk         (clk),
    .rst_n       (rst_n),
    .clk_div2    (clk_div2),
    .clk_div4    (clk_div4),
    .clk_div6    (clk_div6),
    .clk_div8    (clk_div8)
    );

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2.5 仿真结果

在这里插入图片描述

三、奇数分频

3.1 占空比非50%奇数分频

若要实现N分频(N为奇数),只需将计数器在待分频时钟上升沿触发循环计数,计数到0时输出时钟翻转,当计数到(N-1)/2后再次将输出时钟翻转。

以三分频为例,电路需要实现的是:计数器从0开始计数至2,计数器到0时且在上升沿信号翻转,计数器到1时且在上升沿信号清零,具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用)。

在这里插入图片描述

3.2 占空比50%奇数分频

如果对于占空比要求不高的话,只需要简单地对信号计数并且在对应的计数器位置上升沿触发信号翻转即可以得到一个奇数分频,此奇数分频往往占空比达不到50%的要求。

那么如何得到一个50%占空比的奇数分频呢?

从50%占空比奇数分频波形看,信号的翻转对应的源时钟信号分别是上升沿和下降沿,但是双边沿触发在电路设计的时候是不允许的。

那么如何实现这种“类双边沿触发”的效果呢?

对于50%占空比奇数分频,就是分别利用待分频时钟的上升沿触发生成一个时钟,然后用下降沿触发生成另一个时钟,然后将两个时钟信号进行或/与运算得到占空比为50%的奇数分频。

以三分频为例,电路需要实现的是:设计2个分别用上升、下降沿触发的计数器cnt_p和cnt_n,设计2个分别用上升、下降沿触发的计数器clk_p和clk_n,利用clk_p和clk_n通过或逻辑运算生成占空比为50%的分频时钟,具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用)。

在这里插入图片描述

此处我们通过两个计数器分别对上升沿和下降沿信号进行翻转,最后通过或运算得到占空比50%的分屏信号。

Tips:此处亦可借用与逻辑运算,对比上面的clk_p和clk_n稍稍不同,大家可以试着自己画出对应时序图。

3.3 Verilog代码

//奇数分频电路设计(占空比非50%的3分频和占空比50%的3分频)
module clk_div_odd (  
    input 		clk,			//时钟信号
    input 		rst_n,			//复位信号
    output 		clk_div3_1,		//占空比非50%的3分频时钟信号输出
    output 		clk_div3_2		//占空比50%的3分频时钟信号输出
    );

//定义分频的数目
parameter N = 3;

reg [3:0] 		cnt_p;		//上升沿触发计数器计数
reg [3:0]		cnt_n;		//下降沿触发计数器计数
reg       		clk_p;		//上升沿触发生成的时钟信号
reg       		clk_n;		//下降沿触发生成的时钟信号

//上升沿触发计数器模块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
       cnt_p <= 4'b0000;
    else if (cnt_p == N-1)		//计数器从0计数,到2清零
       cnt_p <= 4'b0000;
    else
       cnt_p <= cnt_p + 1'b1;		//计数器累加
end

//下降沿触发计数器模块
always @(negedge clk or negedge rst_n) begin
    if(!rst_n)
       cnt_n <= 4'b0000;
    else if(cnt_n == N-1)		//计数器从0计数,到2清零
      cnt_n <= 4'b0000;
    else
      cnt_n <= cnt_n + 1'b1;		//计数器累加
end

//上升沿触发生成的时钟信号模块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
       clk_p <= 1'b0;
    else if(cnt_p == (N-1)/2)		//计数器到1且在上升沿,时钟信号翻转
       clk_p <= ~clk_p;
    else if (cnt_p <= 0)			//计数器到0且在上升沿,时钟信号翻转
       clk_p <= ~clk_p;
    else
       clk_p <= clk_p;				//防止latch产生
end

//下降沿触发生成的时钟信号模块
always @(negedge clk or negedge rst_n)
begin
   if(!rst_n)
      clk_n <= 1'b0;
   else if (cnt_n == (N-1)/2)		//计数器到1且在上升沿,时钟信号翻转
      clk_n <= ~clk_n;
   else if (cnt_n == 0)				//计数器到0且在上升沿,时钟信号翻转
      clk_n <= ~clk_n;
   else
      clk_n <= clk_n;				//防止latch产生
end

//延时输出,消除亚稳态
assign clk_div3_1 = clk_p;				//得到占空比非50%的3分频时钟信号
assign clk_div3_2 = clk_p | clk_n;		//或逻辑运算得到占空比50%的3分频时钟信号

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

3.4 Testbench

`timescale 1ns/1ps		//时间刻度:单位1ns,精度1ps
module clk_div_odd_tb;

//信号申明
reg clk;
reg rst_n;
wire clk_div3_1;		//占空比非50%的3分频时钟信号
wire clk_div3_2;		//占空比50%的3分频时钟信号

parameter DIV_CLK = 5;		//定义源时钟信号一周期时间

//复位信号生成
initial begin
	clk = 0;		//时钟信号赋初值
    rst_n = 1;		//复位信号赋初值
	#(3*DIV_CLK)
	rst_n = 0;
	#(6*DIV_CLK)
	rst_n = 1;
	#(20*DIV_CLK);
end

//源时钟信号生成
always #DIV_CLK clk = ~clk;

//模块例化
clk_div_odd u_clk_div_odd
    (.clk           (clk),
     .rst_n         (rst_n),
     .clk_div3_1    (clk_div3_1),
     .clk_div3_2    (clk_div3_2)
    );

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3.5 仿真结果

在这里插入图片描述

四、小数分频

4.1 双模前置分频法

不规整的小数分频不能做到分频后的每个时钟周期都是源时钟周期的小数分频倍,更不能做到分频后的时钟占空比均为 50%,因为 Verilog 不能对时钟进行小数计数。

小数分频是基于可变分频和多次平均的方法实现的。

例如进行5.4倍分频,则保证源时钟54个周期的时间等于分频时 10个周期的时间即可。此时需要在54个源时钟周期内进行6次5分频,4次6分频。

T = ( Ma+(M+1)b )/ a+b,这里我们发现组成小数分频使用了a个M分频和b个M+1分频的整数分频电路。

以 5.4 倍分频为例:
基本思想是在54个源时钟周期里完成10个5.4分频,根据前面的公式可知:有6的5分频和4个6分频。只要将5分频和6分频插入在54个源时钟周期即可。

同时我们应当考虑分频信号的实现顺序

5分频和6分频的实现顺序一般有以下 4 种:
(1)先进行 6 次 5 分频,再进行 4 次 6 分频;
(2) 先进行 4 次 6分频,再进行 6 次 5 分频;
(3) 将 6 次 5 分频平均的插入到 4 次 6 分频中;
(4) 将 4 次 6 分频平均的插入到 6 次 5 分频中。

前两种方法时钟频率不均匀,相位抖动较大,所以一般会采用后两种平均插入的方法进行小数分频操作。

那又如何平均插入呢?

平均插入可以通过分频次数差累计的方法实现,5.4 分频的实现过程如下:
(1) 第一次分频次数差值 54 - 10×5 = 4 < 10,第一次进行 5 分频。
(2) 第二次差值累加结果为 4+4=8 < 10,第二次使用 5 分频,同时差值修改为(54-10×5) + (54 -10×5) = 8 。
(3) 第三次差值累加结果为 4 + 8 = 12 > 10,第三次使用 6分频。
(4) 第四次差值累加结果为 12 + (54-10×6) < 10,第四次使用 5 分频。
以此类推,完成将 6 次 5分频平均插入到 4 次 6分频的过程

具体的时序图如下(图由TimeGen绘制,该软件功能实用,推荐使用),此时相位抖动相对较小。

Tips:每一段并不是严格的5.4分频(因为信号翻转只在边沿触发),而是在54个源时钟周期平均下来有10个分频,而且时序难以保证。且占空比几乎达不到50%。

在这里插入图片描述

4.2 Verilog代码

//小数分频电路设计
//双模前置法实现5.4分频
module clk_div_fraction
   (
    input				rst_n,		//复位信号
    input               clk,		//时钟信号
    output              clk_frac	//小数分频输出信号
    );
 
 //定义介于5.4分频的5分频和6分频
parameter		CLK_DIV_1  =  5;
parameter    	CLK_DIV_2  =  6;
parameter    	DIFF       =  4;		//10个周期内5分频与5.4分频的差值


reg [3:0]            cnt_end;			//分频插入计数器	(用于判断插入什么分频)	
reg [3:0]            cnt;				//总计数器
reg                  clk_frac_r; 		//小数分频中间寄存器信号
reg [4:0]            diff_cnt_r;		//差值信号
reg [4:0]            diff_cnt;			//差值信号
wire                 diff_cnt_en= cnt == cnt_end;		//使能信号

//差值累加逻辑模块
always @(*) begin
    if(diff_cnt_r >= 10) begin
        diff_cnt = diff_cnt_r -10 + DIFF;	//差值大于10,插入6分频,差值减6
    end
    else begin
        diff_cnt = diff_cnt_r + DIFF;		//差值小于10,插入5分频,差值加4
    end
end
  
// 借用寄存器延迟输出diff_cnt_r                           
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin				//复位差值清零
        diff_cnt_r <= 0;
    end
    else if(diff_cnt_en) begin		//使能信号高电平时,差值信号延迟输出
        diff_cnt_r <= diff_cnt;
    end
end

//5分频和6分频插入逻辑模块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_end <= CLK_DIV_1-1 ;		//复位先插入5分频
    end
    else if(diff_cnt >= 10) begin
        cnt_end <= CLK_DIV_2-1 ;		//差值大于10,插入6分频
    end
    else begin
        cnt_end <= CLK_DIV_1-1 ;		//差值小于10,插入5分频
    end
end

//
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin					//总计数器、分频信号信号复位
        cnt <= 1'b0;
        clk_frac_r <= 1'b0;
    end
    else if(cnt == cnt_end) begin		//计数器到分频插入界限点
        cnt <= 1'b0;					//总计数器清零
        clk_frac_r <= 1'b1;				//时钟分频信号电平置"1"
    end
    else begin			//其他情况下,计数器累加计数、时钟分频信号电平保持"0"
        cnt <= cnt + 1'b1;		
        clk_frac_r <= 1'b0;
    end
end

//延时输出,消除亚稳态  
assign clk_frac = clk_frac_r;

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

4.3 Testbench

`timescale 1ns/1ps		//时间刻度:单位1ns,精度1ps
module clk_div_fraction_tb;

//信号申明
reg			clk;				
reg			rst_n;
wire		clk_frac;			

parameter DIV_CLK = 5;			//定义源时钟信号一周期时间

//复位信号生成 
initial begin
    clk = 0;			//时钟信号赋初值
	rst_n = 1;			//复位信号赋初值
	#(3*DIV_CLK)
	rst_n = 0;
	#(6*DIV_CLK)
	rst_n = 1;
	#(20*DIV_CLK);
end

//源时钟信号生成
always #DIV_CLK clk = ~clk;

//模块例化
clk_div_fraction u_clk_div_fraction
    (.clk             (clk),
     .rst_n           (rst_n),
     .clk_frac        (clk_frac)
    );

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

4.4 仿真结果

在这里插入图片描述

在这里插入图片描述

把5.4小数分频和2.6小数分频进行比较,图一是5.4小数分频仿真时序图,可以看到波形较为整齐;图二是2.6小数分频仿真时序图,可以看到波形较为杂乱;

Tips:5.4小数分频并不是每一段都是均匀的长度(即局部不满足小数分频,总体满足小数分频)

那么是什么原因造成的呢?

从前面的基本原理可以知道,通过双模前置法得到的小数分频波形是差强人意的,5.4小数分频通过5分频和6分频差值得到,2.6小数分频通过2分频和3分频差值得到,同样差一个cnt,对于2.6小数分频的波形破坏要比5.4小数分频要严重。总而言之就是:局部不满足小数分频,总体满足小数分频。

五、半整数分频

5.1 半整数分频

对于使用小数分频法得到的,以3.5分频为例,需要使用一个四分频和一个三分频,七个周期内,输出两个1,但是信号时序难以得到保障,时钟信号的质量得不到保证。

那有没有新的方法可以优化半整数分频呢?

可以这样实现半整数分频:
(1)在源时钟上升沿分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。
(2)在源时钟下降沿分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。
(3)两个分频时钟做相位一个延迟半个源时钟周期,一个提前半个源时钟周期。将两次产生的时钟进行“或”操作,便可以得到周期均匀的 3.5 倍分频时钟。分频波形示意图如下所示。

此处时序图画错了,下降沿时钟clk_n应该在计数到4时就拉高,具体可以查看仿真图
在这里插入图片描述

5.2 Verilog代码

//半整数分频电路设计
module clk_div_half
    (
    input               rst_n,
    input               clk,
    output              clk_div
    );

parameter            DIV_CLK = 7;			//3.5分频的高低电平总个数
reg [3:0]            cnt;					//总计数器
reg                  clk_p;					//上升沿触发生成的时钟信号
reg                  clk_n;					//下降沿触发生成的时钟信号

//计数器模块
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt <= 1'b0 ;
     end
     else if (cnt == DIV_CLK-1) begin 		//从0计数,到6清零
        cnt <= 'b0 ;
     end
     else begin
         cnt <= cnt + 1'b1 ;
     end
end

//上升沿触发生成的时钟信号模块
//计数器到0和4并且在上升沿触发信号翻转
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
          clk_p <= 1'b0;
     end
      else if (cnt == 0) begin					//计数器到0信号翻转
          clk_p <= 1;
      end
      else if (cnt == (DIV_CLK/2)+1) begin		//计数器到4信号翻转
          clk_p <= 1 ;
      end
      else begin
          clk_p <= 0 ;
      end 
end

//下降沿触发生成的时钟信号模块
//计数器到1和4并且在下降沿触发信号翻转
always@(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
         clk_n <= 1'b0 ;
    end
    else if(cnt == 1) begin						//计数器到1信号翻转
         clk_n <= 1 ;
    end
    else if (cnt == (DIV_CLK/2)+1 ) begin		//计数器到4信号翻转
         clk_n<= 1 ;
    end
    else begin
         clk_n <= 0 ;
    end
end

//或逻辑运算得到占空比50%的3.5半整数分频信号
assign clk_div = clk_p | clk_n;

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

5.3 Testbench

`timescale 1ns/1ps			//时间刻度:单位1ns,精度1ps
module clk_div_half_tb;

//信号申明
reg 			clk;
reg 			rst_n;
wire 			clk_div;
  
parameter DIV_CLK0 = 5;		//定义源时钟信号一周期时间

//复位信号生成 
initial begin
    clk = 0;				//时钟信号赋初值
	rst_n = 1;				//复位信号赋初值
	#(3*DIV_CLK0)
	rst_n = 0;
	#(6*DIV_CLK0)
	rst_n = 1;
	#(20*DIV_CLK0);
end

//源时钟信号生成
always #DIV_CLK0 clk = ~clk;

//模块例化
clk_div_half u_clk_div_half
    (.clk             (clk),
     .rst_n           (rst_n),
     .clk_div         (clk_div)
    );

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

5.4仿真结果

在这里插入图片描述

六、状态机分频

6.1状态机分频

Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。状态机可分为 2 类:Moore 状态机和 Mealy 状态机。

例如完成一个四分频且占空比为25%的分频器,此时可以列出四种状态,状态机在四种状态不断切换,根据下一个输出状态只与当前状态有关而与输出无关,可以知道此分频器可根据Moore状态机完成。

在这里插入图片描述

6.2 verilog代码

module clk_div_FSM
    (
    input wire clk,
    input wire rst_n,
    output reg clk_FSM
    );

//定义四种状态 
parameter	S0 = 2'b00;
parameter	S1 = 2'b01;
parameter	S2 = 2'b10;
parameter	S3 = 2'b11;
 
reg [1:0]	state;			//定义目前状态
reg [1:0]	next_state;		//下一状态

//信号复位模块
always @(posedge clk,negedge rst_n) begin
    if(!rst_n) begin 
        state <= S0;
    end
    else begin
        state <= next_state;
    end
end

//状态转换模块(相当于用状态机写计数器)
always @(*) begin
    case (state)
        S0:	next_state = S1;
        S1:	next_state = S2;
        S2:	next_state = S3;
        S3:	next_state = S0;
    endcase
end

//信号输出模块
always @(*) begin
    if(state == S0) begin
        clk_FSM = 1'b1;
    end
    else begin
        clk_FSM = 1'b0;
    end
end

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

6.3 Tsetbench

`timescale 1ns/1ps
module clk_div_FSM_tb;

//信号申明
reg				 clk;
reg 			 rst_n;
wire			 clk_FSM;

parameter DIV_CLK = 5;			//定义源时钟信号一周期时间

//复位信号生成 
initial begin
    clk = 0;				//时钟信号赋初值
    rst_n = 1;				//复位信号赋初值
    #(3*DIV_CLK)
    rst_n = 0;
    #(6*DIV_CLK)
    rst_n = 1;
    #(20*DIV_CLK);
end

//源时钟信号生成
always	#DIV_CLK clk = ~clk;

//模块例化
clk_div_FSM u_clk_div_FSM
    (.clk             (clk),
     .rst_n           (rst_n),
     .clk_FSM         (clk_FSM)
    );

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

6.4仿真结果

在这里插入图片描述

七、总结

偶数分频:无论是通过D触发器还是计数器实现,这类分频都是最容易得到的,并且占空比容易控制在50%。对于D触发器实现偶数分频来说,分频数只能得2^n,其余分频数只能由计数器法等其他方法实现。除此以外,随着分频的数目不断增大,通过D触发器实现触发器数目会增多,在电路设计的过程中应当考虑面积因素。对于计数器实现偶数分频,占空比和分频数都可以得到极大的控制,是实现偶数分频最灵活的一种方式。

奇数分频:计数分频基本原理也是通过计数器实现的,主要分为占空比非50%的奇数分频和占空比50%的奇数分频,后者实现简单而后者稍稍复杂一些。占空比非50%的情况下,时钟信号在上升沿(N-1)/2翻转和0翻转即可得到需要的分频信号。占空比50%的情况下,一个时钟信号在上升沿而一个时钟信号在下降沿,触发(N-1)/2翻转和0翻转,然后将clk_p和clk_n做或逻辑运算即可得到占空比50%的计数分频信号。从以上可以看出,占空比50%的奇数分频只是在占空比非50%的奇数分频的基础上多做了一个逻辑运算。

小数分频:目前小数分频使用较多的方法是双模前置分频法,基本原理是在小数分频的两侧寻找相近的分频去插入,营造在一定的源时钟周期走过与小数分频相当的时钟周期。但是往往分频的时序波形比较乱,占空比几乎达不到50%,效果差强人意,究其根本原因是信号只在源时钟的边沿触发。

半整数分频:半整数分频是小数分频的特殊情况,之所以会拎出来单独讲,是因为根据小数分频的双模前置法做出来的波形时序较差。如果需要得时序更优的半整数分频怎么办?首先做出两个上升沿下降沿二分频信号,通过在半整数两边寻找相邻的奇数和偶数(决定信号电平周期数),然后做逻辑运算即可以得到时序更优的半整数分频。

状态机分频:可以实现分频的方式之一,对于简单的分频器可以采用状态机来实现,大部分情况状态机用来处理较为复杂的情况与问题,在分频电路里有大材小用的感觉,电路分频时可以采用更简单的计数器代替状态机的状态转换。


更多可查看个人主页链接
软件版本:仿真软件:Modelsim 10.6c
不定期纠错补充,欢迎随时交流
最后修改日期:2023.6.2

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/648931
推荐阅读
相关标签
  

闽ICP备14008679号