FPGA时钟分频的几种方法

FPGA时钟分频的几种方法

1.定义分频参数2.直接计算cnt计数值3.奇数分频4.PLL5.奇偶分频通用程序

1.定义分频参数

localparam CLK_DIVIDE = 4'd10 ; // 时钟分频系数

reg [ 3:0] clk_cnt ; // 时钟分频计数器

reg dri_clk ; // 数码管的驱动时钟

always @(posedge clk or negedge rst_n) begin

if(!rst_n) begin

clk_cnt <= 4'd0;

dri_clk <= 1'b1;

end

else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin

clk_cnt <= 4'd0;

dri_clk <= ~dri_clk;

end

else begin

clk_cnt <= clk_cnt + 1'b1;

dri_clk <= dri_clk;

end

end

2.直接计算cnt计数值

reg [11:0] div_cnt ; //分频计数器

reg div_clk ; //分频时钟

时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms

always @(posedge sys_clk or negedge sys_rst_n ) begin

if (!sys_rst_n) begin

div_cnt <= 12'd0;

div_clk <= 1'b0;

end

else if(div_cnt == 12'd3124) begin

div_cnt <= 12'd0;

div_clk <= ~div_clk;

end

else

div_cnt = div_cnt + 12'b1;

end

cnt的计算是用sysclk除以需要的频率,得到cnt_total,cnt_total除以2再减1就是cnt的值。

assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd3;// 模块驱动时钟的分频系数

//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作

always @(posedge clk or negedge rst_n) begin

if(rst_n == 1'b0) begin

dri_clk <= 1'b1;

clk_cnt <= 10'd0;

end

else if(clk_cnt == clk_divide - 1'd1) begin

clk_cnt <= 10'd0;

dri_clk <= ~dri_clk;

end

else

clk_cnt <= clk_cnt + 1'b1;

end

右移3位,这个程序中相对于I2C_FREQ时钟频率*8,但是少了上面除以2的操作,所以最后是四倍I2C_FREQ。因为我们操作的是时钟取反需要计数到的值,和我们平时接触到的直接改变数字大小是反着的

3.奇数分频

上面的1和2适合偶数分频,奇数分频采用下面方法

//7分频,奇数分频都可以类似这么做,只需要改div1和div2的参数。

//div1为奇数分频除2的整数部分,div2为分频系数减1。

//采用上升延和下降延分别触发不同波形,最后叠加的方式产生奇数分频。

module odd_div(clk, clk1x, rst, clk1xpose, clk1xnege, coutpose, coutnege);

input clk;

input rst;

output clk1x;

output clk1xpose;

output clk1xnege;

output[2:0] coutpose;

output[2:0] coutnege;

reg clk1xpose;

reg clk1xnege;

reg[2:0] coutpose;

reg[2:0] coutnege;

parameter div1 = 3 , div2 = 6; // div1 = 7 / 2, div2 = 7 - 1

assign clk1x = clk1xpose | clk1xnege;

always@(posedge clk or negedge rst)

begin

if(!rst)

clk1xpose <= 0;

else if(coutpose == div1)

clk1xpose <= ~clk1xpose;

else if(coutpose == div2)

clk1xpose <= ~clk1xpose;

else

clk1xpose <= clk1xpose;

end

always@(negedge clk or negedge rst)

begin

if(!rst)

clk1xnege <= 0;

else if(coutnege == div1)

clk1xnege <= ~clk1xnege;

else if(coutnege == div2)

clk1xnege <= ~clk1xnege;

else

clk1xnege <= clk1xnege;

end

always@(posedge clk or negedge rst)

begin

if(!rst)

coutpose <= 0;

else if(coutpose < div2)

coutpose <= coutpose + 1;

else

coutpose <= 0;

end

always@(negedge clk or negedge rst)

begin

if(!rst)

coutnege <= 0;

else if(coutnege < div2)

coutnege <= coutnege + 1;

else

coutnege <= 0;

end

endmodule

采用计数的方法分频,理论上看起来没什么问题,但实际应用中,时钟偏移有点严重,所以最好还是用PLL,但对时钟要求不是很精确时可以用计数的方法。下图是程序的时序仿真波形。

4.PLL

5.奇偶分频通用程序

//N是分频系数,WIDTH要大于等于能够表示N的位数

module clk_divide

#(

parameter WIDTH = 3,

parameter N = 5

)(

//INPUT

input clk,

input rst_n,

//OUTPUT

output clkout

);

//********************

//REGS

reg [WIDTH-1:0] cnt_p,cnt_n;

reg clk_p,clk_n;

assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;

//Sequential logic style

always @ (posedge clk)

begin

if(!rst_n)

cnt_p<=0;

else if (cnt_p==(N-1))

cnt_p<=0;

else cnt_p<=cnt_p+1;

end

always @ (negedge clk)

begin

if(!rst_n)

cnt_n<=0;

else if (cnt_n==(N-1))

cnt_n<=0;

else cnt_n<=cnt_n+1;

end

always @ (posedge clk)

begin

if(!rst_n)

clk_p<=0;

else if (cnt_p<(N>>1))

clk_p<=0;

else

clk_p<=1;

end

always @ (negedge clk)

begin

if(!rst_n)

clk_n<=0;

else if (cnt_n<(N>>1))

clk_n<=0;

else

clk_n<=1;

end

endmodule