转载自https://blog.csdn.net/moon9999/article/details/75020355/

1 偶分频模块

偶分频模块设计较为简单,首先确定分频系数M和计数器值N

M = 时 钟 输 入 频 率 时 钟 输 出 频 率 M = \frac{时钟输入频率}{时钟输出频率} M= N = M 2 N = \frac{M}{2} N=2M

若输入时钟是50Mhz,输出时钟是1hz,则M=50_000_000,N=25_000_000。

偶分频则意味着M是偶数。

以M=4,N=2为例,希望得到的输出时钟时序如下:

在这里插入图片描述
因此只需要将counter以clk_in为时钟驱动计数,当counter = (N-1)时,clk_out翻转即可。

module clock_div(
    input clk,
    input rst,
    output reg clk_out
);

parameter N = 26'd25_000_000 , WIDTH = 25;
reg [WIDTH:0] counter;

always @(posedge clk or negedge rst) begin
	if (~rst) begin
		counter <= 0;
		clk_out <= 0;
	end
	else if (counter == N-1) begin
		clk_out <= ~clk_out;
		counter <= 0;
	end
	else
		counter <= counter + 1;
end

endmodule

2 奇分频模块

奇分频需要通过两个时钟共同得到,首先得到分频系数M和计数器值N:
M = 时 钟 输 入 频 率 时 钟 输 出 频 率 M = \frac{时钟输入频率}{时钟输出频率} M= N = M − 1 2 N = \frac{M-1}{2} N=2M1

如输入时钟为50M,输出时钟为10M,则M=5,N=2。

奇分频则意味着M为奇数。

以M=5,N=2为例,我们希望得到的输出时钟时序如下:

奇函数
其中clk_out为最终输出时钟,clk_out1和clk_out2为辅助时钟生成。

计数器counter由0计数至(M-1)。

  • clk_out1在clk_in的上升延跳变,条件是counter==(N-1)或(M-1)。

  • clk_out2在clk_in的下降延跳变,条件是counter==(N-1)或(M-1)。

  • 之后clk_out = clk_out1 & clk_out2即可得到M分频的时钟。

verilog代码如下,其中WIDTH为(N的位宽-1):

module time_adv_odd(
    input clk,
    input rst,
    output clk_out
);
parameter N = 2,WIDTH = 7;
reg [WIDTH:0] counter;
always @(posedge clk or posedge rst) begin
	if (rst) begin
		// reset
		counter <= 0;
	end
	else if (counter == (N << 1)) begin //M-1处跳变,因为 M - 1 = 2 * N,这里等于4
		counter <= 0;
	end
	else begin
		counter <= counter + 1;
	end
end

reg clk_out1;
always @(posedge clk or posedge rst) begin //clk_out1在clk_in的上升延跳变
	if (rst) begin
		// reset
		clk_out1 <= 0;
	end
	else if (counter == N-1) begin //N-1处跳变,这里等于1
		clk_out1 <= !clk_out1;
	end
	else if (counter == (N << 1)) begin //M-1处跳变,这里等于4
		clk_out1 <= !clk_out1;
	end
end

reg clk_out2;
always @(negedge clk or posedge rst) begin //clk_out2在clk_in的下降延跳变
	if (rst) begin
		// reset
		clk_out2 <= 0;
	end
	else if (counter == N-1) begin
		clk_out2 <= !clk_out2;
	end
	else if (counter == (N << 1)) begin
		clk_out2 <= !clk_out2;
	end
end

assign clk_out = clk_out1 & clk_out2;
endmodule

3 任意占空比的任意分频

在verilog程序设计中,我们往往要对一个频率进行任意分频,而且占空比也有一定的要求这样的话,对于程序有一定的要求。

现在在前面两个实验的基础上做一个简单的总结,实现对一个频率的任意占空比的任意分频。

比如: FPGA系统时钟是50M Hz,而我们要产生的频率是880Hz,那么,我们需要对系统时钟进行分频。很容易想到用计数的方式来分频:50000000/880 = 56818。

显然这个数字不是2的整幂次方,那么我们可以设定一个参数,让它到56818的时候重新计数就可以实现了。程序如下:

 1 //rtl
 2 module div(
 3     clk, 
 4     rst_n,
 5     clk_div
 6 );
 7     input clk,rst_n;
 8     output clk_div;
 9     reg clk_div;
10   
11     reg [15:0] counter;
12 
13 always @(posedge clk or negedge rst_n)
14     if(!rst_n) 
15         counter <= 0;
16     else if(counter==56817) 
17         counter <= 0;
18     else 
19         counter <= counter+1;
20 
21    assign clk_div = counter[15];
22 endmodule

分频的应用很广泛,一般的做法是先用高频时钟计数,然后使用计数器的某一位输出作为工作时钟进行其他的逻辑设计,上面的程序就是一个体现。

下面我们来算一下它的占空比:

我们清楚地知道,这个输出波形在counter为0到32767(2的14次方)的时候为低,在32768到56817的时候为高,占空比为40%多一些,如果我们需要占空比为50%,那么我们需要再设定一个参数,使它为56817的一半,使达到它的时候波形翻转,就可以实现结果了。

程序如下:28408=56818/2-1,计数到28408就清零,翻转,其余的计数期间,保持不变。

设计代码:

 1 //rtl
 2 module div(
 3     clk, 
 4     rst_n,
 5     clk_div
 6 );
 7     input clk,rst_n;
 8     output clk_div;
 9     reg clk_div;
10     reg [14:0] counter;
11 always @(posedge clk or negedge rst_n)
12     if(!rst_n) 
13         counter <= 0;
14     else if(counter==28408)
15         counter <= 0;
16     else 
17         counter <= counter+1;
18 
19 always @(posedge clk or negedge rst_n)
20     if(!rst_n) 
21         clk_div <= 0;
22     else if(counter==28408) 
23         clk_div <= ~clk_div;
24 endmodule

继续让我们来看如何实现任意占空比,比如还是由50M分频产生880Hz,而分频得到的信号的占空比为30%。

56818×30%=17045

设计代码:

 1 //rtl
 2 module div(
 3     clk,
 4     rst_n,
 5     clk_div,
 6     counter
 7 );
 8     input clk,rst_n;
 9     output clk_div;
10     reg clk_div;
11     output [15:0] counter;
12     reg [15:0] counter;
13     
14 always @(posedge clk)
15     if(!rst_n) 
16         counter <= 0;
17     else if(counter==56817) 
18         counter <= 0;
19     else counter <= counter+1;
20 
21 always @(posedge clk)
22   if(!rst_n) 
23     	clk_div <= 0;
24   else if(counter<17045) 
25     	clk_div <= 1;
26   else 
27     	clk_div <= 0;
28  endmodule
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐