【常用电路】奇数/偶数分频电路
一、偶数分频电路
偶数倍分频是最简单的一种分频模式,完全可通过计数器计数实现。
1 ////////////////////////////////////////////////////////////////////////////////// 2 // 偶数分频电路 3 // 这个分频模块适用于待分频时钟和目标时钟的频率呈整倍数关系 4 // 通过改变cnt_values确定是N分频,cnt_values应该等于N/2-1,以16分频为例,cnt_values=7; 5 6 module clk_div_even( 7 clk_in, 8 reset, 9 clk_out 10 ); 11 12 input clk_in; 13 input reset; 14 output clk_out; 15 16 reg clk_out; 17 reg [15:0] cnt; 18 19 parameter cnt_values = 'd7; 20 21 always @ ( posedge clk_in ) begin 22 if ( reset ) begin 23 cnt <= 16'b0; 24 clk_out <= 1'b0; 25 end 26 else if ( cnt == cnt_values ) begin 27 cnt <= 16'b0; 28 clk_out <= ~clk_out; 29 end 30 else begin 31 cnt <= cnt + 1'b1; 32 clk_out <= clk_out; 33 end 34 end 35 36 endmodule
二、奇数分频电路
相较于偶数倍分频,奇数倍分频要复杂一些。奇数倍分频有多种方法,下面介绍错位“异或”法,主要在时钟的上升沿和下降沿分别产生占空比非50%的时钟,再进行或运算。
1 ////////////////////////////////////////////////////////////////////////////////// 2 // 奇数分频电路 3 // 这个分频模块适用于待分频时钟和目标时钟的频率呈整倍奇数关系 4 // 通过相或运算实现50%占空比的奇数N分频,N=cnt_values; 5 6 module clk_div_odd( 7 clk_in, 8 reset, 9 clk_out 10 ); 11 12 input clk_in; 13 input reset; 14 output clk_out; 15 16 reg [15:0] cnt_p, cnt_n; 17 reg [15:0] cnt_values = 'd3; 18 reg clk_p, clk_n; 19 20 always @ ( posedge clk_in ) begin 21 if ( reset ) begin 22 cnt_p <= 16'b0; 23 clk_p <= 1'b0; 24 end 25 else if ( cnt_p == 16'b0 ) begin 26 cnt_p <= cnt_p + 16'b1; 27 //cnt_p <= 16'b0; 28 clk_p <= ~clk_p; 29 end 30 31 else if ( cnt_p == (cnt_values - 16'b1)/2 ) begin 32 cnt_p <= cnt_p + 16'b1; 33 //cnt_p <= 16'b0; 34 clk_p <= ~clk_p; 35 end 36 else if ( cnt_p == (cnt_values - 16'b1))begin 37 cnt_p <= 16'b0; 38 //cnt_p <= cnt_p + 16'b1; 39 clk_p <= clk_p; 40 end 41 else begin 42 cnt_p <= cnt_p + 16'b1; 43 clk_p <= clk_p; 44 end 45 end 46 47 48 always @ ( negedge clk_in ) begin 49 if ( reset ) begin 50 cnt_n <= 16'b0; 51 clk_n <= 1'b0; 52 end 53 else if ( cnt_n == 16'b0 ) begin 54 cnt_n <= cnt_n + 16'b1; 55 //cnt_p <= 16'b0; 56 clk_n <= ~clk_n; 57 end 58 else if ( cnt_n == (cnt_values - 16'b1)/2 ) begin 59 cnt_n <= cnt_n + 16'b1; 60 //cnt_p <= 16'b0; 61 clk_n <= ~clk_n; 62 end 63 else if ( cnt_n == (cnt_values - 16'b1))begin 64 cnt_n <= 16'b0; 65 //cnt_p <= cnt_p + 16'b1; 66 clk_n <= clk_n; 67 end 68 else begin 69 cnt_n <= cnt_n + 16'b1; 70 clk_n <= clk_n; 71 end 72 end 73 74 75 assign clk_out = clk_p | clk_n; //两个占空比非50%的时钟相或之后,得到占空比50%的时钟 76 77 endmodule
三、半整数分频
利用时钟的双边沿逻辑,可以对时钟进行半整数的分频。但是无论怎么调整,半整数分频的占空比不可能是 50%。半整数分频的方法有很多,这里只介绍一种和奇数分频调整占空比类似的方法。
对任意N:复位之后,在一个上升沿进行上升沿计数,计数到2N清零,并且在0和N+1+N/2的时候进行翻转,得到clk_p;
复位之后,在一个下降沿进行下降沿计数,计数到6清零,并且在N/2和N的时候进行翻转,得到clk_n。
1 module clk_div( 2 input clk, 3 input rst_n, 4 output clk_out 5 ); 6 7 `define DIV_CLK_N 63 // N.5分频 8 9 reg clk_2in; // 2N倍频时钟 10 reg clk_2in_2; // 下降沿2N倍频时钟 11 reg [7:0] cnt_2N; // 2N倍频时钟计数 12 reg [7:0] cnt_2N_2; // 下降沿2N倍频时钟计数 13 14 assign clk_out = clk_2in && clk_2in_2; 15 16 // 上升沿生成2N分频时钟电路 17 always@(posedge clk or negedge rst_n) begin 18 if(!rst_n) cnt_2N <= 8'b0; 19 else if(cnt_2N == 2*`DIV_CLK_N) cnt_2N <= 8'b0; 20 else cnt_2N <= cnt_2N + 1'b1; 21 end 22 23 always@(posedge clk or negedge rst_n) begin 24 if(!rst_n) clk_2in <= 1'b0; 25 //else if((cnt_2N == `DIV_CLK_N+1) ||( cnt_2N ==0 )) begin // 这个是占空比为1:2N+1,未改进 26 else if((cnt_2N == `DIV_CLK_N+1+`DIV_CLK_N/2) ||( cnt_2N ==0 )) begin // 这个是占空比尽量靠近0.5的方式 27 clk_2in <= ~clk_2in; 28 end 29 else clk_2in <= clk_2in; 30 end 31 32 // 下降沿生成2N分频时钟电路 33 always@(negedge clk or negedge rst_n) begin 34 if(!rst_n) cnt_2N_2 <= 8'b0; 35 else if(cnt_2N_2 == 2*`DIV_CLK_N) cnt_2N_2 <= 8'b0; 36 else cnt_2N_2 <= cnt_2N_2 + 1'b1; 37 end 38 39 always@(negedge clk or negedge rst_n) begin 40 if(!rst_n) clk_2in_2 <= 1'b1; 41 //else if((cnt_2N_2 == `DIV_CLK_N) ||( cnt_2N_2 == 0 )) begin // 这个是占空比为1:2N+1,未改进 42 else if((cnt_2N_2 == `DIV_CLK_N) ||( cnt_2N_2 == `DIV_CLK_N/2 )) begin // 这个是占空比尽量靠近0.5的方式 43 clk_2in_2 <= ~clk_2in_2; 44 end 45 else clk_2in_2 <= clk_2in_2; 46 end 47 48 endmodule
三、仿真
附上tb文件
1 module odd_sim; 2 3 4 reg clk_in; 5 reg reset; 6 wire clk_out; 7 8 initial 9 begin 10 clk_in = 1'b0; 11 reset = 1'b1; 12 13 #1000 14 reset = 1'b0; 15 16 end 17 18 always #50 clk_in = ~clk_in; //给时钟激励,即50ns翻转一次,10MHz 19 20 clk_div_odd odd_sim( 21 .clk_in (clk_in), 22 .reset (reset), 23 .clk_out (clk_out) 24 ); 25 endmodule