FPGA基础设计(二):任意分频器(奇数,偶数,小数)

前言

FPGA开发板上一般只有一个晶振,即一种时钟频率。数字系统设计中,时间的计算都要以时钟作为基本单元,对基准时钟进行不同倍数的分频而得到各模块所需时钟频率,可通过Verilog代码实现;倍频可通过锁相环【PLL】实现。

分频原理

把输入信号的频率变成成倍的低于输入频率的输出信号;每经历几个单位时钟周期就输出一个时钟周期。
例:clk_in为12MHz; clk_out为2MHz.则分频系数为6。那么如何实现6分频:
  把输入信号作为计数脉冲(边沿触发一次,计数一次),
当计数6次,为输出信号的一个周期,此时输入信号经历了6个周期,置0,重新开始计数,循环往复。(前提:输入信号占空比50%)
而输出信号的不同占空比的实现无非是输出信号经历的单位周期输出高低电平不同;

偶数分频

   6分频

占空比50%:6分频电路:一个always块实现一个模3计数器。
     另一个always块实现当计数到2(从0计数到2,计数,3次)输出信号翻转,
     占空比50%。
占空比不是50%:先实现模6计数器,然后可任意设计当计数到某个值输出高电平,再计数到另一个值输出低电平。

   代码
//不带标志位占空比50%
module divider_six(clk,reset,clk_out,cnt);

         input clk;
		 input reset;
		 output reg clk_out;
		 
		 output reg [1:0] cnt;//用于计数的寄存器; always块内,reg型变量
		 
		 always@(posedge clk)
		       if(reset == 1'b0)
				    cnt <= 2'd0;
				 else if(cnt == 2'd2)//从0开始,计数到2,计数为3,则6分频
				    cnt <= 2'd0;
				 else
				    cnt <= cnt + 1'b1;
//计数3次,输出翻转;所以cnt计数3次作为标志,实现翻转功能					 
		  always@(posedge clk)
		        if(reset == 1'b0)
				     clk_out <= 1'b0;
				  else if(cnt == 2'd2)
				     clk_out <= ~clk_out;
				  else;
endmodule
   tb
`timescale 1ns/100ps
`define clk_cycle 50

module divider_six_tb;
  
     reg clk;
	 reg reset;
	 
	 wire clk_out;
	 wire [1:0]cnt;
	 
	 always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
	 
	 initial begin
	 
	     clk = 0;
		 reset = 1;
		 
		 #10 reset = 0;
		 #110 reset = 1;
		 #100000
		 $stop;
		 
	 end
	 divider_six u1(
	          .reset(reset),
				 .clk(clk),
				 .clk_out(clk_out),
				 .cnt(cnt)
				 );
endmodule 
   仿真波形

在这里插入图片描述
在这里插入图片描述

奇数分频

算法:需要在系统时钟为上升沿和下降沿都工作(不是同时工作)
    产生50%的占空比,需要设计两个分频时钟跳变点,分频周期的N-1(N-1)/2
例:13分频:两个分频时钟跳变点:6[(13-1)/2]和12[13-1],此时输出保持7个高电平时钟周期,6个低电平时钟周期分别在上升沿触发clk1和下降沿触发clk2,两者相位相差半个单位周期,clk_out = clk1 & clk2; 之后clk_out占空比50%
如图仿真波形:

  仿真波形

在这里插入图片描述

  代码
//13分频,占空比为50%

module div_13(clk,reset,clk_out,clk1,clk2);

       input clk;
	   input reset;
		 
	   output  clk_out;
		 
	   reg [3:0] cnt;
		 
	   output reg clk1;
	   output reg clk2;

//计数从0到12		 
		 always@(posedge clk)
		       if(reset == 1'b0 || cnt == 4'd12)
				    cnt <= 4'd0;
				 else
				    cnt <= cnt + 1'b1;
					 
//实现分频,clk1:7个高电平时钟周期,6个低电平时钟周期,上升沿触发	
       always@(posedge clk)				 
				 if(reset == 1'b0)
				    clk1 <= 1'b0;
				 else if(cnt == 4'd6)//复位后,计数到6,开始低电平,保持6个周期,
			       clk1 <= 1'b0;   //此时计数到12,重新计数,保持高电平直至计数到6
				 else if(cnt == 4'd12)//计数到6和12,这两个是分频跳变点
			       clk1 <= 1'b1;	  //如何得到?N-1;(N-1)/2
//clk2:下降沿触发,7个高电平时钟周期,6个低电平时钟周期
       always@(negedge clk)	
             if(reset == 1'b0)
		          clk2 <= 1'b0;
			    else if(cnt == 4'd6)
		          clk2 <= 1'b0;
			    else if(cnt == 4'd12)
		          clk2 <= 1'b1;
//clk1,clk2两者相差半个时钟周期					 
//clk1,clk2配合作用,都计数7次后,clk1立即回到低电平,而clk2延迟半个周期					 
		 assign	clk_out = clk1 & clk2;		 					
endmodule		 
  tb
`timescale 1ns/100ps
`define clk_cycle 50

module div_13_tb;
  
    reg clk;
	 reg reset;
	 
	 wire clk_out;
	 wire clk1;
	 wire clk2;
	 
	 always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
	 
	 initial begin
	 
	    clk = 0;
		 reset = 1;
		 
		 #10 reset = 0;
		 #110 reset = 1;
		 #100000
		 $stop;
		 
	 end
	 div_13 u1(
	          .reset(reset),
			  .clk(clk),
		      .clk_out(clk_out),
			  .clk1(clk1),
		      .clk2(clk2)
			 );
endmodule 

小数分频

   说明

1.等效意义上的小数分频:例5.3分频,没有周期为5.3的分频电路,实际是输出时钟53的周期对应于输出时钟的10个周期。
2.按序输出:时钟用处不大,没有意义
3.插入乱序输出:防止相位抖动,把多出来的脉冲均匀分散开

 半整数分频:N+0.5

思路:例:5.5分频
原时钟的半周期为单位,此时周期为11可分频输出6高5低。
上升沿触发,使得clk1前6周期输出高电平【N-1=10为一个跳变点】后5周期输出低电平【N-1)/2=5为另一个跳变点】;
下降沿触发使得clk2前5周期为低电平(cnt为0时的跳变点),后6【cnt=N-1)/2=5】周期为高电平。两个信号相与即为分频信号clk_div = clk1 & clk2;,类似奇分频

  仿真波形

在这里插入图片描述

  代码
//5.5分频
//原时钟周期一半为单位,输出1高10低,从0到10计数,前6周期高电平,后5周期低电平;
//再使用下降沿触发前5周期低电平,前6周期高电平,最后相与
module div_half_1(clk,rst_n,clk_div,clk1,clk2,cnt);

      input clk;
	   input rst_n;
	   
	   output clk_div;
		
	   
	   output reg[3:0] cnt;
	   output reg clk1;
	   output reg clk2;
//从0到10计数 模11	   
	   always@(posedge clk or negedge rst_n)
	          if(!rst_n)
			    cnt <= 4'd0;
			  else if (cnt==4'd10)
			    cnt <= 0;
			  else
			    cnt <= cnt + 1'b1;
				
	    always@(posedge clk or negedge rst_n)
		       if(!rst_n)
			     clk1 <= 1'b0;
			   else if(cnt==4'd10)//6个周期的高电平N-1=10
			     clk1 <= 1'b1;
			   else if(cnt==4'd5)//5个周期的低电平(N-1)/2=5
			     clk1 <= 1'b0;  //两个跳变点,(N-1)/2;N-1
//clk2相比于clk1延时半个时钟周期,			 
		always@(negedge clk or negedge rst_n)
               if(!rst_n)
                 clk2 <= 1'b0;
               else if(cnt==4'd0)//5个周期的低电平
                 clk2 <= 1'b0;
               else if(cnt==4'd5)//6个周期的高电平
                 clk2 <= 1'b1;

assign clk_div = clk1 & clk2;
endmodule				 
  tb
`timescale 1ns/100ps
`define clk_cycle 50

module div_half_1_tb;
  
    reg clk;
	 reg reset;
	 
	 wire clk_out;
	 wire clk1;
	 wire clk2;
	 wire[3:0] cnt;
	 
	 always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
	 
	 initial begin
	 
	    clk = 0;
		 reset = 1;
		 
		 #10 reset = 0;
		 #110 reset = 1;
		 #100000
		 $stop;
		 
	 end
	 div_half_1 u1(
	             .rst_n(reset),
				 .clk(clk),
				 .clk_div(clk_out),
				 .clk1(clk1),
				 .clk2(clk2),
				 .cnt(cnt)
				 );
endmodule 

 小数分频

   5.3分频

53/10=5.3 设5分频a次;6分频次
a+b=10;
5a+6b=53 ->a=7 ; b=3
如何插入排序:6556556555
Verilog实现:
脉冲删除:53个单位周期删除43个
算法:分母作为累加值,在clk的上升沿cnt加上分母10,判断是否大于分子,若小于(脉冲删除标志为1,删除)继续累加,若大于(脉冲删除标志为0,不删除)在下一周期减去分子。结果刚好是6556556555(由来:分母-余数=7,作为累加值,大于分母,输出N分频,然后减去分母,继续累加7,;小于分母,输出N+1分频)

  代码
//脉冲删除小数分频

module div_5_3_1(clk,rst_n,clk_out,cnt);  

       input clk;
	   input rst_n;
	   
	   output clk_out;
	   
	   parameter fra = 6'd53;//分子
	   parameter den = 6'd10;//分母
	   
	   output reg[5:0] cnt;
	   reg flag_del;//脉冲删除标志位
				
	   always@(posedge clk or negedge rst_n) begin
	          if(!rst_n)begin
			    cnt <= 6'd0;
				 flag_del <= 1'b0;
			  end
			//大于分子,不删除  
			  else if(cnt>fra) begin
			    cnt <= cnt + den - fra;
				 flag_del <= 1'b0;
			  end
			//小于分子,累加;删除:保持高电平
			  else begin
			    cnt <= cnt + den;
			  	 flag_del <= 1'b1;
			  end
		end	
		
		assign clk_out = flag_del ? 1 : clk;

endmodule
  tb
`timescale 1ns/100ps
`define clk_cycle 50

module div_5_3_1_tb;
  
     reg clk;
	 reg reset;
	 
	 wire clk_out;
	 wire [5:0]cnt;
	 
	 always #(`clk_cycle) clk = ~clk;//产生测试时钟,时钟周期100ns(s>ms>us>ns>ps)
	 
	 initial begin
	 
	     clk = 0;
		 reset = 0;
		 
		 #10 reset = 0;
		 #110 reset = 1;
		 #100000
		 $stop;
		 
	 end

	 		 div_5_3_1 u1 (
		          .clk(clk),
				  .rst_n(reset),
				  .clk_out(clk_out),
				  .cnt(cnt)
				  );
endmodule 
  仿真波形

在这里插入图片描述

最后

不足之处:未实现参数化编程;

参考:

https://blog.csdn.net/weixin_43698385/article/details/122773225
https://blog.csdn.net/Reborn_Lee/article/details/97553078
https://blog.csdn.net/wangyanchao151/article/details/81204126?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%B0%8F%E6%95%B0%E5%88%86%E9%A2%91&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-5-81204126.nonecase&spm=1018.2226.3001.4187

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值