分频器简介和Verilog实现

[TOC]

写在前面的话

分频器,是将输入信号的频率进行缩小。实际上,在数学IC笔面中常考的分频器设计,就是采用计数器,对输入时钟合理生成中间信号,最后通过逻辑操作输出。本质上,这里考的还是计数器相关的知识点,同时会涉及到时钟相关的基本概念。
(1)在实际的项目中,除非是对时钟没有特殊要求,这时候才能使用分频的方法;
(2)一般会优先使用PLL、MMCM等成熟IP,得到精确、质量高的时钟信号。

关于分频器的种类,大致有以下几点:
(1)偶数分频,始终产生占空比50%。
(2)奇数分频,分为占空比50%和非50%。(相较于偶数分频,奇数分频是常考的知识点
(3)小数分频,结合正常分频和平均分布实现的。(之前都没怎么考过,相较于前两种,更加综合,看看现在这越来越卷的数字IC,建议还是要掌握

注意点:
除了分频器本身相关的知识点,这里在面试时也会有时钟和时序分析相关的拓展点,建议同学们也要了解时钟和时序分析相关的知识点。有条件的同学,可以亲手做做时序分析相关的实验,了解时序路径和时序报告。

偶数分频

级联触发器实现2^n偶数分频

采用触发器加反相器,可以构成简单的2分频电路,以这个基本单元进行级联可以实现4分频、8分频、16分频,分频系数是2^n.
2分频电路:

4分频电路:

2^n分频电路:

Verilog实现:

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : even_fre_div1.v
// Create : 2022-11-07 10:06:32
// Revise : 2022-11-07 10:06:32
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0 
// Description: 2^n 偶数分频 利用DFF 取反  //div2 div4 div8
// -----------------------------------------------------------------------------
module even_fre_div1 (
	input 		clk 			,    	// Source Clock
	input 		rst_n			, 		// Asynchronous reset active low
	output wire clk_div2		,  		// clock div2
	output wire clk_div4		,  		// clock div4
	output wire clk_div8		  		// clock div8
	
);


//reg  define
reg 	clk_div2_t 	;
reg 	clk_div4_t 	;
reg  	clk_div8_t 	;


//div_2
always @(posedge clk or negedge rst_n) begin 
	if(~rst_n) begin
		clk_div2_t <= 1'b0			;
	end 
	else begin
		clk_div2_t <= ~clk_div2_t	;
	end
end


//div4
always @(posedge clk_div2 or negedge rst_n) begin 
	if(~rst_n) begin
		 clk_div4_t <= 1'b0;
	end 
	else begin
		 clk_div4_t <= ~clk_div4_t ;
	end
end

//div8
always @(posedge clk_div4 or negedge rst_n) begin 
	if(~rst_n) begin
		 clk_div8_t <= 1'b0;
	end 
	else begin
		 clk_div8_t <= ~clk_div8_t ;
	end
end


//assign 
assign clk_div2 = clk_div2_t	;
assign clk_div4 = clk_div4_t	;
assign clk_div8 = clk_div8_t	;
endmodule 

计数器实现2n偶数分频

利用计数器,计数范围(0~2N-1),在N-1处取反。直接看波形:

计数器2n偶数分频:

Verilog实现:

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : even_fre_div2.v
// Create : 2022-11-07 10:30:09
// Revise : 2022-11-07 10:30:09
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion:	v1.0
// Description: 2n偶数分频,计数器实现 
// -----------------------------------------------------------------------------
module even_fre_div2 #(
	parameter div_2n 	= 	12  	
	)(
	input 				clk			,    		//  Source Clock
	input 				rst_n		, 			// Asynchronous reset active low
	output wire 		clk_div2n	  			// div 2n	
);

reg [3:0]	cnt 	;
reg clk_div2n_t 	;

//cnt 
always @(posedge clk or negedge rst_n) begin 
	if(~rst_n) begin
		cnt  <= 4'd0;
	end 
	else if(cnt == (div_2n/2) -1)begin
		cnt  <= 4'd0 ;
	end
	else begin
		cnt 	<= cnt +4'd1 ;
	end
end

//out clk
always @(posedge clk or negedge rst_n) begin 
	if(~rst_n) begin
		clk_div2n_t <= 1'b0;
	end 
	else if(cnt == (div_2n/2)-1) begin
		clk_div2n_t <= ~clk_div2n_t;
	end
	else begin
		clk_div2n_t <= clk_div2n_t ;
	end

end

//assign 
assign clk_div2n = clk_div2n_t ;

endmodule 

奇数分频(常考)

相较于偶数分频,奇数分频的知识点要稍微多些,有占空比50%和非50%;而占空比50%又是最常考的知识点,有相与、相或、相异或三种。

实际手撕代码时,要求熟练掌握一种奇数分频的电路就行,这里推荐相与和相或,两种任一选择一种练习就行。

PS:奇数分频还涉及到双边沿计数器,感兴趣的同学可以了解双边沿采样相关的知识点。

奇数分频,占空比50%(最常考,要求熟练掌握一种)

OR操作,2N+1 奇数分频

先看方法:

再看波形:

最后看代码:

示例1,单计数器实现:
只在上升沿计数,在上升沿和下降沿采用输出高低电平;

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : odd_fre_or_div1.v
// Create : 2022-11-07 15:49:36
// Revise : 2022-11-07 15:49:36
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0
// Description: 奇数分频 占空比50% OR操作 单计数器实现
// -----------------------------------------------------------------------------

module odd_fre_or_div1 #(
            parameter DIV_CLK = 9            //div 2n+1 
   )(
    input               clk            ,     //Source Clock
    input               rst_n          ,     //Asynchronous reset active low
    output     wire     clk_div_odd          //div odd
    );

//reg define
   reg [3:0]            cnt ;
   reg                  clkp_odd_r ;
   reg                  clkn_odd_r ;

//cnt
   always @(posedge clk or negedge rst_n) begin
      if (~rst_n) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

//posedge clk_div 
   always @(posedge clk or negedge rst_n) begin          // N >>> high  N+1 >>> low
      if (~rst_n) begin
         clkp_odd_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1)-1 ) begin       
        clkp_odd_r <= 1'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin 
        clkp_odd_r <= 1'b1 ;
      end
      else begin
      	clkp_odd_r  <= clkp_odd_r 	;
      end
   end

//negedge clk_div 
   always @(negedge clk or negedge rst_n) begin          // N >>> high  N+1 >>> low
      if (~rst_n) begin
         clkn_odd_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1)-1 ) begin 
        clkn_odd_r <= 1'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin 
        clkn_odd_r <= 1'b1 ;
      end
      else begin
      	clkn_odd_r  <= clkn_odd_r 	;
      end
   end


//OR 
   assign clk_div_odd = clkp_odd_r | clkn_odd_r ;

endmodule

示例2,双计数器实现:
在上升沿和下降沿计数,在上升沿和下降沿采用输出高低电平;

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : odd_fre_or_div2.v
// Create : 2022-11-07 16:10:14
// Revise : 2022-11-07 16:10:14
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0
// Description: 奇数分频 占空比50% OR操作 双计数器实现
// -----------------------------------------------------------------------------
module odd_fre_or_div2 #(
	parameter  DIV_CLK = 9
	)(
	input 			clk 			,   // Source Clock
	input 			rst_n			,	// Asynchronous reset active low
	output wire  	clk_div_odd			// div odd
	
);

reg [3:0] cnt_p 	;
reg [3:0] cnt_n		;

reg clkp_odd_r 	;
reg clkn_odd_r 	;

// pos cnt
always @(posedge clk or negedge rst_n) begin
      if (~rst_n) begin
         cnt_p    <= 'b0 ;
      end
      else if (cnt_p == DIV_CLK-1) begin
         cnt_p    <= 'b0 ;
      end
      else begin
         cnt_p    <= cnt + 1'b1 ;
      end
end


//neg cnt
always @(negedge clk or negedge rst_n) begin
      if (~rst_n) begin
         cnt_n    <= 'b0 ;
      end
      else if (cnt_n == DIV_CLK-1) begin
         cnt_n    <= 'b0 ;
      end
      else begin
         cnt_n    <= cnt + 1'b1 ;
      end
end


//posedge clk_div 
   always @(posedge clk or negedge rst_n) begin          // N >>> high  N+1 >>> low
      if (~rst_n) begin
         clkp_odd_r <= 1'b0 ;
      end
      else if (cnt_p == (DIV_CLK>>1)-1 ) begin       
        clkp_odd_r <= 1'b0 ;
      end
      else if (cnt_p == DIV_CLK-1) begin 
        clkp_odd_r <= 1'b1 ;
      end
      else begin
      	clkp_odd_r	<=	clkp_odd_r	;
      end
   end

   //negedge clk_div 
   always @(negedge clk or negedge rst_n) begin          // N >>> high  N+1 >>> low
      if (~rst_n) begin
         clkn_odd_r <= 1'b0 ;
      end
      else if (cnt_n == (DIV_CLK>>1)-1 ) begin       
        clkn_odd_r <= 1'b0 ;
      end
      else if (cnt_n == DIV_CLK-1) begin 
        clkn_odd_r <= 1'b1 ;
      end
      else begin
      	clkn_odd_r	<=	clkn_odd_r	;
      end
   end

   //OR
   assign clk_div_odd = clkp_odd_r | clkn_odd_r ;

endmodule 
AND操作,2N+1 奇数分频

先看方法:

再看波形:

最后看代码:
示例1,单计数器实现:
只在上升沿计数,在上升沿和下降沿采用输出高低电平

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : odd_fre_and_div1.v
// Create : 2022-11-07 16:30:31
// Revise : 2022-11-07 16:30:31
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0
// Description:奇数分频 占空比50% AND操作 单计数器实现
// -----------------------------------------------------------------------------
module odd_fre_and_div1 #( 
		parameter DIV_CLK = 9 
   	)(
    input               clk 		,
    input               rst_n 		,
    output    wire      clk_div_odd
    );

   //capture at negedge
   reg [3:0]            cnt ;
   reg                  clkp_odd_r ;
   reg                  clkn_odd_r ;

   //cnt 
   always @(posedge clk or negedge rst_n) begin
      if (~rst_n) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

//posedge clk_div
   always @(posedge clk or negedge rst_n) begin  // N+1 >>> high  N >>> low
      if (~rst_n) begin
         clkp_odd_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1) ) begin 
        clkp_odd_r <= 1'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin 
        clkp_odd_r <= 1'b1 ;
      end
      else begin
      	clkp_odd_r <= clkp_odd_r ;
      end
   end

//negedge clk_div
   always @(negedge clk or negedge rst_n) begin // N+1 >>> high  N >>> low
      if (~rst_n) begin
         clkn_odd_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1) ) begin 
        clkn_odd_r <= 1'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin 
        clkn_odd_r <= 1'b1 ;
      end
      else begin
      	clkn_odd_r <= clkn_odd_r ;
      end
   end
//AND
   assign clk_div_odd = clkp_odd_r & clkn_odd_r ;

endmodule

示例2,双计数器实现:
在上升沿和下降沿计数,在上升沿和下降沿采用输出高低电平;

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : CHAO LI  1320343336@qq.com
// File   : odd_fre_and_div2.v
// Create : 2022-11-07 16:38:20
// Revise : 2022-11-07 16:38:20
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0
// Description: 奇数分频 占空比50% AND操作 双计数器实现
// -----------------------------------------------------------------------------
module odd_fre_and_div2 #(
   parameter  DIV_CLK = 9
   )(
   input          clk         ,     // Source Clock
   input          rst_n       ,     // Asynchronous reset active low
   output wire    clk_div_odd       // div odd
   
);

reg [3:0] cnt_p   ;
reg [3:0] cnt_n      ;

reg clkp_odd_r    ;
reg clkn_odd_r    ;

// pos cnt
always @(posedge clk or negedge rst_n) begin
      if (~rst_n) begin
         cnt_p    <= 'b0 ;
      end
      else if (cnt_p == DIV_CLK-1) begin
         cnt_p    <= 'b0 ;
      end
      else begin
         cnt_p    <= cnt + 1'b1 ;
      end
end


//neg cnt
always @(negedge clk or negedge rst_n) begin
      if (~rst_n) begin
         cnt_n    <= 'b0 ;
      end
      else if (cnt_n == DIV_CLK-1) begin
         cnt_n    <= 'b0 ;
      end
      else begin
         cnt_n    <= cnt + 1'b1 ;
      end
end


//posedge clk_div 
   always @(posedge clk or negedge rst_n) begin          // N+1 >>> high  N >>> low
      if (~rst_n) begin
         clkp_odd_r <= 1'b0 ;
      end
      else if (cnt_p == (DIV_CLK>>1) ) begin       
        clkp_odd_r <= 1'b0 ;
      end
      else if (cnt_p == DIV_CLK-1) begin 
        clkp_odd_r <= 1'b1 ;
      end
      else begin
         clkp_odd_r  <= clkp_odd_r  ;
      end
   end

   //negedge clk_div 
   always @(negedge clk or negedge rst_n) begin          // N+1 >>> high  N >>> low
      if (~rst_n) begin
         clkn_odd_r <= 1'b0 ;
      end
      else if (cnt_n == (DIV_CLK>>1) ) begin       
        clkn_odd_r <= 1'b0 ;
      end
      else if (cnt_n == DIV_CLK-1) begin 
        clkn_odd_r <= 1'b1 ;
      end
      else begin
         clkn_odd_r  <= clkn_odd_r  ;
      end
   end

   //AND
   assign clk_div_odd = clkp_odd_r & clkn_odd_r ;

endmodule 
XOR操作,2N+1 奇数分频

XOR操作相较于上述两种方法使用较少,这里大家理解一下实现的思路就好,仅供感兴趣的同学提供新思路。
重要的是自己要熟练掌握一种奇数分频方法。
先看方法:

再看波形:

最后看代码:

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : odd_fre_xor_div.v
// Create : 2022-11-07 17:02:24
// Revise : 2022-11-07 17:02:24
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0 
// Description: 奇数分频 占空比50% XOR操作 
// -----------------------------------------------------------------------------
module odd_fre_xor_div #(
		parameter  DIV_CLK 	= 9 
	)(
	input 			clk  				,    	// Source Clock
	input 			rst_n 				, 		// Asynchronous reset active low
	output wire  	clk_div_odd 		 		// div odd
	
);


reg	[3:0]	cnt;//上升沿计数
reg 		clkp_odd 	;
reg 		clkn_odd	;




//cnt
always@(posedge clk or negedge rst_n)begin
	if(~rst_n)begin
		cnt 	<= 4'd0;
	end
	else if(cnt == DIV_CLK-1) begin
		cnt		<= 4'd0;
	end
	else begin
		cnt		<= cnt + 1;
	end
end

//pos clk_div
always@(posedge clk or negedge rst_n)begin
	if(~rst_n)begin
		clkp_odd		<= 1'b0;
	end
	else  if (cnt == DIV_CLK-1 )begin
		clkp_odd		<= ~clkp_odd;
	end
	else begin
		clkp_odd 		<= clkp_odd;
	end
end


//neg clk_div
always@(negedge clk or negedge rst_n) begin
	if(~rst_n) begin
		clkn_odd		<= 1'b0;
	end
	else if (cnt == DIV_CLK >> 1) begin
		clkn_odd		<= ~clkn_odd;
	end
	else begin
		clkn_odd 		<= clkn_odd ;
	end
end

//XOR
assign		clk_div_odd = clkp_odd ^ clkn_odd;
endmodule

奇数分频,占空比非50%

占空比非50%的情况,只要分频后的波形周期是原波形周期的2N+1就行。
3分频有两种占空比,1/3 和2/3。分别对应OR 操作和AND操作,具体代码可以参考上述。

小数分频(针对越来越卷的数字IC,建议还是要掌握)

小数分频不能做到精确控制占空比,也没法做到精确的小数分频,而是平均频率是小数倍。这里小数分频涉及到奇数分频、偶数分频以及平均分布的方法。

先看方法:

再看波形:

关键点:

小数分频的关键点有两个:
(1)完成奇偶分频,确定好分频次数;
(2)确定分频次数后,要合理安排分频的顺序,确保平均。

例如8.4分频,得到6次8分频和4次9分频,对应有四种分布方式。

分布方式 具体操作 备注
1 先6次8分频,再4次9分频 频率不均,边缘处时钟抖动大
2 先4次9分频,再6次8分频 频率不均,边缘处时钟抖动大
3 6次8分频和4次9分频,平均分布 频率均匀,抖动小

Verilog实现:

以8.4倍小数分频举例

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT904  1320343336@qq.com
// File   : fre_fractional-n.v
// Create : 2022-11-08 14:45:45
// Revise : 2022-11-08 14:45:45
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: v1.0
// Description: 小数分频  8.4倍小数
// -----------------------------------------------------------------------------

module fre_fractional-n (	
  #(
   parameter            SOURCE_NUM = 84 , 		//Sorce clocks
   parameter            DEST_NUM   = 10  		//div nums 	 		8.4 
   )
   (
    input               rstn 			,	//Asynchronous reset active low
    input               clk 			,	//Source Clock
    output              clk_frac 			//fractional frequency division
    );

   parameter            SOURCE_DIV = SOURCE_NUM/DEST_NUM ; 				//8-divisor
   parameter            DEST_DIV   = SOURCE_DIV + 1; 					//9-divisor
   parameter            DIFF_ACC   = SOURCE_NUM - SOURCE_DIV*DEST_NUM ;


   //reg
   reg [3:0]            cnt_end_r 	;
   reg [3:0]            main_cnt 	;
   reg                  clk_frac_r 	;
   wire         		diff_cnt_en	;
   //difference counter
   reg [4:0]            diff_cnt_r 	;
   wire [4:0]           diff_cnt 	;

   //main_cnt
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         main_cnt    <= 'b0 ;
         clk_frac_r  <= 1'b0 ;
      end
      else if (main_cnt == cnt_end_r) begin
         main_cnt    <= 'b0 ;
         clk_frac_r  <= 1'b1 ;
      end
      else begin
         main_cnt    <= main_cnt + 1'b1 ;
         clk_frac_r  <= 1'b0 ;
      end
   end

   //diff_cnt
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         diff_cnt_r <= 0 ;
      end
      else if (diff_cnt_en) begin
         diff_cnt_r <= diff_cnt ;
      end
   end

   // cnt_end
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt_end_r      <= SOURCE_DIV-1 ;
      end
      else if (diff_cnt >= 10) begin
         cnt_end_r      <= DEST_DIV-1 ;
      end
      else begin
         cnt_end_r      <= SOURCE_DIV-1 ;
      end
   end

   //assign
   assign       clk_frac        = clk_frac_r ;

   assign       diff_cnt_en     = (main_cnt == cnt_end_r)?1'b1:1'b0 ;


   assign      diff_cnt = diff_cnt_r >= DEST_NUM ?
                                   (diff_cnt_r -10 + DIFF_ACC)
                                   : (diff_cnt_r + DIFF_ACC) ;

endmodule


总结

分频器作为数字IC笔面最常考的知识点,经常会出现在编程题和手撕代码环节,其中,奇数分频又是最常考的部分,要求必须熟练掌握一种奇数分频方法。
(1)偶数分频不需要考虑占空比,是理想的50%。
(2)奇数分频需要考虑占空比是否50%,要求使用双边沿逻辑,涉及到双边沿操作,这里会有一定的拓展,例如双边沿采样怎么实现?另外,时钟相关的概念也有涉及,例如频率、相位、抖动和偏移等。
(3)小数分频涉及奇数分频和偶数分频,之前很少出现手撕代码的环节,大多在笔试环节或者面试询问相关的实现方式,要求理解实现方法就好,但看现在数字IC越来越卷的现状,建议有时间精力的同学还是了解相关知识点。
(4)分频器相关的知识点拓展可以是时钟相关的IP以及时序分析相关的知识点,同学们也要了解相关的知识点,打好基础。