verilog手撕代码1——分频计数器——偶数、奇数、半整数、任意小数分频


前言

2023.4.8


一、偶数分频

1、使用D触发器设计一个同时输出2/4/8分频的50%占空比的时钟分频器

在这里插入图片描述

module even_div
    (
    input     wire rst ,
    input     wire clk_in,
    output    wire clk_out2,
    output    wire clk_out4,
    output    wire clk_out8
    );

    reg clk_out2_r, clk_out4_r, clk_out8_r;
    
    always@(posedge clk_in or negedge rst) begin  //二分频是最简单的,直接把~Q接到D端就可以了
        if(~rst)
            clk_out2_r <= 0;
        else
            clk_out2_r <= ~clk_out2_r;
    end
    
    //四分频是以二分频为时钟,就可以在上升沿的时候四分频也是从0跳转到1
    //使得二四八分频同时为高电平
    always@(posedge clk_out2 or negedge rst) begin  
        if(~rst)
            clk_out4_r <= 0;
        else
            clk_out4_r <= ~clk_out4_r;
    end
    
    always@(posedge clk_out4 or negedge rst) begin
        if(~rst)
            clk_out8_r <= 0;
        else
            clk_out8_r <= ~clk_out8_r;
    end
    
    assign clk_out2 = clk_out2_r;
    assign clk_out4 = clk_out4_r;
    assign clk_out8 = clk_out8_r;
endmodule

在这里插入图片描述

2、用D触发器带同步高置数和异步高复位端的二分频的电路,画出逻辑电路

在这里插入图片描述

reg q;
always@(posedge clk or posedge rst)begin
	if(rst)
		q <= 0;
	else if(set)
		q <= 1;
	else
		q <= ~q;
end

3、输入频率10MHz,输出频率1MHz,进行分频

分频系数:10,偶数分频,用计数器来实现

reg [3:0] cnt;
parameter CNT_DIV = 5, CNT_MAX = 10;

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt <= 0;
	else 
		cnt <= (cnt==CNT_MAX-1) ? 0 : cnt+1;  //注意计数器最大值为9,不能为10,为10的话下个周期会有问题
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		q <= 0;
	else if(cnt<CNT_DIV)
		q <= 1;
	else
		q <= 0;
end

在这里插入图片描述

二、奇数分频

1、不要求占空比为50%

原理和偶数分频一样,只不过由于是奇数个周期,可以决定高低电平的占比。例如三分频,占空比可以为1/3或者2/3

在这里插入图片描述

//下面是5分频
reg [2:0]cnt;
reg clk_out5_r;

always@(posedge clk_in or negedge rst)begin
    if(!rst)
        clk_out5_r<=0;
    else if(cnt==0||cnt==2)
        clk_out5_r<=~clk_out5_r;
    else 
        clk_out5_r<=clk_out5_r;
end

always@(posedge clk_in or negedge rst)begin
    if(!rst)
        cnt<=0;
    else
        cnt<=(cnt==4)?0:cnt+1;
end

在这里插入图片描述

2、要求占空比接近50%

2.1 法一:上升沿和下降沿都计数

这种写法应该要避免,无法综合,前面学习verilog语法的时候说过,混合使用上升沿和下降沿触发的触发器。

parameter MAX = 7;
always@(posedge clk_in or negedge clk_in)begin
    if(!rst)begin
        cnt<=0;
        clk_out7_r<=0;
    end
    else if(cnt==MAX-1)begin
        cnt<=0;
        clk_out7_r<=~clk_out7_r;
    end
    else
        cnt<=cnt+1;
end

2.2 法二:上升沿下降沿分开计数再组合逻辑输出

如果奇数分频输出时钟的高低电平只差一个cycle ,则可以利用源时钟双边沿特性并采用"与操作"或"或操作"的方式将分频时钟占空比调整到50%。

如果是高电平少,就是相或;如果高电平多,就是相与。

module oushu_div(
	input clk_in,
	input rst_n,
	output clk_out   //这里输出默认是wire类型
	);
	parameter MAX = 7;
	reg [2:0] cnt1;
	reg clk_div1, clk_div2;
	
	always@(posedge clk_in or negedge rst_n)begin  //用一个计数器计数
		if(!rst_n)
			cnt <= 0;
		else
			cnt <= (cnt==MAX-1) ? 0 : cnt+1;
	end
	
	always@(posedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div1 <= 0;
		else if(cnt<(MAX-1)/2)
			clk_div1 <= 1;
		else
			clk_div1 <= 0;
	end		
	
	always@(negedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div2 <= 0;
		else if(cnt<(MAX-1)/2)
			clk_div2 <= 1;
		else
			clk_div2 <= 0;
	end	
	
	assign clk_out = clk_div1 | clk_div2 ;
endmodule

在这里插入图片描述

	always@(posedge clk_in or negedge rst_n)begin
		if(!rst_n)
			cnt <= 0;
		else
			cnt <= (cnt==MAX-1) ? 0 : cnt+1;
	end
	
	always@(posedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div1 <= 0;
		else if(cnt==(MAX-1)/2)   //这样写就使得0-3为高电平,4-8为低电平
			clk_div1 <= 0;
		else if(cnt==(MAX-1))
			clk_div1 <= 1;
	end		
	
	always@(negedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div2 <= 0;
		else if(cnt==(MAX-1)/2)
			clk_div2 <= 0;
		else if(cnt==(MAX-1))
			clk_div2 <= 1;
	end	
	
	assign clk_out = clk_div1 & clk_div2 ;
endmodule

在这里插入图片描述

三、小数分频

1、半整数分频

利用时钟的双边沿逻辑,可以对时钟进行半整数的分频。半整数分频的占空比不可能是50%
例如5.5分频,2.75个周期为高电平,2.75个周期为低电平,不可能实现占空比为50%。

module banzhengshushu_div(
	input clk_in,
	input rst_n,
	output clk_out
	);

	parameter MAX = 11;
	reg [3:0] cnt;
	reg clk_div1, clk_div2;

	always@(posedge clk_in or negedge rst_n)begin
		if(!rst_n)
			cnt <= 0;
		else
			cnt <= (cnt==MAX-1) ? 0 : cnt+1;
	end
	
	always@(posedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div1 <= 0;
		else if(cnt==0)
			clk_div1 <= 1;
		else if(cnt==2)
			clk_div1 <= 0;
		else if(cnt==6)
			clk_div1 <= 1;
		else if(cnt==8)
			clk_div1 <= 0;
	end		

	always@(negedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div2 <= 0;
		else if(cnt==1)
			clk_div2 <= 1;
		else if(cnt==3)
			clk_div2 <= 0;
		else if(cnt==6)
			clk_div2 <= 1;
		else if(cnt==8)
			clk_div2 <= 0;
	end	

	assign clk_out = clk_div1 | clk_div2 ;
endmodule

画波形图然后写的代码,可以实现接近50%占空比的5.5分频(11个半周期,5个高电平,6个低电平)

先画出目标波形,再调整clk1和clk2。(从计数值1开始画波形比较方便写代码)
在这里插入图片描述

在这里插入图片描述

下面代码这样写,占空比就不是接近50%,但是是5.5分频

always@(posedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div1 <= 0;
		else if(cnt==0)
			clk_div1 <= 1;
		else if(cnt==(MAX+1)/2)
			clk_div1 <= 1;
		else 
			clk_div1 <= 0;
	end		

	always@(negedge clk_in or negedge rst_n)begin
		if(!rst_n)
			clk_div2 <= 0;
		else if(cnt==1)
			clk_div2 <= 1;
		else if(cnt==(MAX+1)/2)
			clk_div2 <= 1;
		else 
			clk_div2 <= 0;
	end	

在这里插入图片描述

2、任意小数分频

8.7分频 :1个目标周期 = 8.7个原周期
相当于 :87个原周期 = 10个目标周期,去计算除有多少个8分频和9分频
在这里插入图片描述
在这里插入图片描述

module div_M_N(
 input  wire clk_in,
 input  wire rst,
 output wire clk_out
);
	parameter M_N = 8'd87; 
	parameter c89 = 8'd24;  // 8/9时钟切换点
	parameter div_e = 5'd8; //偶数周期
	parameter div_o = 5'd9; //奇数周期
	
	reg [6:0]cnt0;   //总计数器0-86
	reg [3:0]cnt1;   //分频计数器
	reg flag;    //时钟分频切换标志
	reg clk_out_r;
	
	always@(posedge clk_in or negedge rst)begin
	    if(!rst)
	        cnt0<=0;
	    else
	        cnt0<=(cnt0==M_N-1)? 0 : cnt0+1;
	end
	
	always@(posedge clk_in or negedge rst)begin
	    if(!rst)
	        flag<=0;
	    else
	        flag<=(cnt0==c89-1||cnt0==M_N-1)? ~flag : flag;
	end
	
	//flag=0,八分频,flag=1,九分频
	always@(posedge clk_in or negedge rst)begin
	    if(!rst)
	        cnt1<=0;
	    else if(!flag)
	        cnt1<=(cnt1==div_e-1) ? 0 : cnt1+1;
	    else
	        cnt1<=(cnt1==div_o-1) ? 0 : cnt1+1;     
	end
	
	always@(posedge clk_in or negedge rst)begin
	    if(!rst)
	        clk_out_r<=0;
	    else if(!flag)
	        clk_out_r<=(cnt1<4) ? 1 : 0;
	    else
	        clk_out_r<=(cnt1<4) ? 1 : 0;   
	end
	
	assign clk_out=clk_out_r;
endmodule

在这里插入图片描述

四、总结

对于绝大多数的触发器,其实只需要用到时钟的上升沿触发,很少用到下降沿。在这种情况下,只要上升沿和时钟频率有关系,什么时候来下降沿不重要。所以50%的占空比不是必须的。

因此在小数分频器中关注的是得到一个尽量均匀的分频信号,而不是得到一个绝对50%占空比的分频信号。

五、testbench

module jishu_div_testbench ();
	reg clk_in;
	reg rst_n;
	wire clk_out;

	jishu_div jishu_div_i(
		.clk_in(clk_in),
		.rst_n(rst_n),
		.clk_out(clk_out)
	);

	initial begin
		clk_in = 0;
		rst_n = 0;
		//q = 0;
		#10;
		rst_n = 1;
	end

	always #5 clk_in = ~clk_in;
endmodule 
  • 6
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值