Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真

文章目录

  • 1、 模10计数器的设计与仿真
  • 2、加入使能信号
  • 3、先递增后递减的计数器设计与仿真
  • 4、二分频(用D触发器实现)
  • 5、三分频
  • 6、任意分频(占空比为50%)
    • 6.1 任意偶数N分频方式
    • 6.2 任意奇数N分频方式
  • 产生iic的scl 250khz时钟频率

计数器的逻辑功能:记录时钟脉冲的个数

1、 模10计数器的设计与仿真

现要求设计模10计数器,0到9循环累加,计数满清0。

 module Count_1(
    input clk,
	input rst_n,
	
	output reg [3:0] cnt	    
);
 
always@(posedge clk or negedge rst_n)
    if(!rst_n)
	    cnt <= 4'd0; 
    else if(cnt == 4'd9)
	    cnt <= 4'd0;
	else 
	    cnt <= cnt + 1'b1;

endmodule 

综合出的RTL:
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第1张图片

testbench仿真:

`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期
module Count_1_tb;

    reg clk;
	reg rst_n;
	
	wire [3:0] cnt;

Count_1 u1(
          .clk(clk),
		  .rst_n(rst_n),
		  .cnt(cnt)
);
//产生时钟激励
initial	 clk = 0;
always #(`Clock/2) clk= ~clk;

//产生复位激励
initial begin
   rst_n=0; #(`Clock*5);
   rst_n=1;

#500
$stop;
end	
endmodule

波形如下:复位拉高,计数器实现从0-9的计数,每当计数到9后即可清零。
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第2张图片

2、加入使能信号

设计带使能的模10计数器。当复位拉高且使能信号有效的时候开始0到9循环累加计数,计数满清0.

verilog代码:

 module Count_1(
    input clk,
	input rst_n,
	input en,
	
	output reg [3:0] cnt	    
);
 
always@(posedge clk or negedge rst_n)
    if(!rst_n)
	    cnt <= 4'd0; 
	 else if(en)begin
        if(cnt == 4'd9)
	        cnt <= 4'd0;
	     else 
	        cnt <= cnt + 1'b1;
	end
	else
	    cnt <= cnt;

endmodule 

生成的RTL图:
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第3张图片

tb仿真文件:

`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期
module Count_1_tb;

    reg clk;
	reg rst_n;
	reg en;
	
	wire [3:0] cnt;

Count_1 u1(
          .clk(clk),
		  .rst_n(rst_n),
		  .cnt(cnt),
		  .en(en)
);
//产生时钟激励
initial	 clk = 0;
always #(`Clock/2) clk= ~clk;

//产生复位激励和输入
initial begin
   rst_n=0;
	en = 0;
	#(`Clock*5);
   rst_n=1;
	#(`Clock*2);
	en = 1;
   #500
   $stop;
end	
endmodule

波形可看到,复位拉高后并没有直接开始计数,只有当en信号有效的时候,才能开始计数。

Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第4张图片

3、先递增后递减的计数器设计与仿真

设计一个计数器,先递增,递增到一定数后开始递减,递减到一定数后又递增,循环反复
.
复位拉高且使能信号有效,进行计数。
当递增标志信号有效且未增到9,则计数器+1
当递减标志信号有效且未减到0,则计数器-1
递增信号、递减信号

module Count_1(
		input         clk                 , 
		input         rst_n               , 
		input         en                  ,
		
		output reg    [3:0]cnt               ,
		output reg    flag_x   //递增递减的标志信号
);


always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        cnt <= 0;
	 else if(en)begin
       if(flag_x==0 && cnt<9) //递增有效且计数小于9
        cnt <= cnt + 1;
     else if(flag_x==1 && cnt>0)//递减有效且计数大于0
        cnt <= cnt - 1;
		end
	 else 
	      cnt <= 0;
end

//生成递增递减的标志信号
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        flag_x <= 0;
    else if(flag_x==0 && cnt==9)
        flag_x <= 1;
    else if(flag_x==1 && cnt==0)
        flag_x <= 0;
end
endmodule

tb仿真文件:

`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期
module Count_1_tb;

    reg clk;
	reg rst_n;
	reg en;
	
	wire [3:0] cnt;
	wire flag_x;

Count_1 u1(
          .clk(clk),
		  .rst_n(rst_n),
		  .cnt(cnt),
		  .en(en),
		  .flag_x(flag_x)
);
//产生时钟激励
initial	 clk = 0;
always #(`Clock/2) clk= ~clk;

//产生复位激励和输入
initial begin
   rst_n=0;
	en = 0;
	#(`Clock*5);
   rst_n=1;
	#(`Clock*2);
	en = 1;
   #500
   $stop;
end	
endmodule

波形如下:
当en信号有效且flag_x为低电平,计数器小于9的时候,开始了0-9递增计数;当计数到9且flag_x为高电平,表示开始递减计数,于是从9-0;
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第5张图片

4、二分频(用D触发器实现)

Verilog代码:

module Count_1(
		input         clk   , 
		input         rst_n , 
		
		output reg    out
);

always @(posedge clk or negedge rst_n) 
    if (!rst_n)
        out <= 0;
	 else 
	     out <= ~out;
endmodule

RTL图:
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第6张图片

tb测试文件:

`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期
module Count_1_tb;
    reg clk;
	reg rst_n;
	wire out;
	
Count_1 u1(
        .clk(clk),
		  .rst_n(rst_n),
		  .out(out)
);

//产生时钟激励
initial	 clk = 0;
always #(`Clock/2) clk= ~clk;

//产生复位激励和输入
initial begin
   rst_n=0;
	#(`Clock*5);
	rst_n=1;
	#(`Clock*5);
   #500
   $stop;
end	
endmodule

可看到输出的一个周期中,包含两个clk脉冲,因此实现了二分频。
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第7张图片

5、三分频

由div3可看到,其为clk的三分频信号。pos_cnt为clk上升沿计数器,neg_cnt为下降沿计数器。由于是三分频,因此计数三个clk的上升和三个clk的下降沿即可,最后进行逻辑或运算得到div3.

Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第8张图片

verilog代码:

module Count_1(

	input rst_n,
	input clk,
	
	output reg [1:0] pos_cnt,  //上升沿计数器
	output reg [1:0] neg_cnt,  //下降沿计数器
	
	output reg neg_cnt_0,  //上升沿脉冲信号
	output reg pos_cnt_0, //下降沿脉冲信号
	
	output div3_o//输出三分频
 
);


//上升沿计数且每计数到2清零
always@(posedge clk or negedge rst_n) begin 
	if(!rst_n)
		pos_cnt<=2'd0;
	else if(pos_cnt==2'd2) 
		pos_cnt<=2'd0;
	else
		pos_cnt<=pos_cnt+1'b1;
end

 //下降沿计数且每计数到2清零
always@(negedge clk or negedge rst_n)begin  

	if(!rst_n)	
		neg_cnt<=2'd0;
	else if(neg_cnt==2'd2)
		neg_cnt<=2'd0;
	else
		neg_cnt<=neg_cnt+1'b1;
end

//产生两个时钟沿的脉冲信号
always@(posedge clk or negedge rst_n) 
begin
	if(!rst_n)	
		pos_cnt_0<=1'b0;
	else if (pos_cnt == 2'd1)
	   pos_cnt_0<=1'b1;
	else
      pos_cnt_0<=1'b0;
end		
		
always@(negedge clk or negedge rst_n) 
begin
	if(!rst_n)	
		neg_cnt_0<=1'b0;
	else if (neg_cnt == 2'd1)
	   neg_cnt_0<=1'b1;
	else
      neg_cnt_0<=1'b0;	
end	
assign div3_o = pos_cnt_0 | neg_cnt_0; 

endmodule 

tb测试文件:


`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期

module Count_1_tb;

   reg rst_n;
   reg clk;
   wire  [1:0] pos_cnt;  //上升沿计数
   wire  [1:0] neg_cnt;  //下降沿计数
   wire pos_cnt_0;
   wire neg_cnt_0;
   wire div3_o;
		
Count_1 u1(
		.clk(clk),
		.rst_n(rst_n),
		.pos_cnt(pos_cnt),
		.neg_cnt(neg_cnt),
		.neg_cnt_0(neg_cnt_0),
		.pos_cnt_0(pos_cnt_0),
		.div3_o(div3_o)
	);
	
initial clk = 0;
	always #(`Clock/2) clk= ~clk;
	
initial begin
	   rst_n=0 ;
		#(`Clock*20);
	   rst_n=1;
	   #(`Clock*100);
	   $stop;		
end	
	
endmodule 

波形仿真:
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第9张图片

6、任意分频(占空比为50%)

任意分频通过计数器实现,但需要注意的是任意分频电路分为两种:奇数分频和偶数分频。偶数分频可直接采用计数器实现,而奇数分频还需要利用组合逻辑。
.
建议在设计过程中采用参数化设计,这样就可以随时改变参量以得到不同的分频需要。
.
在对时钟要求不是很严格的FPGA系统中,分频通常都是通过计数器的循环计数来实现的,否则将采用PLL来实现。

6.1 任意偶数N分频方式

方法1、先得到1Mhz的时钟,然后每(N/2-1 )和 N-1进行翻转
.
方法2、设计一个 N/2计数器,计数到(N/2-1)清零,然后每(N/2)-1 进行翻转。

例1:通过1Mhz时钟的方式进行偶数分频。

系统时钟为50Mhz,假如要产生1Mhz的时钟,则需要对系统时钟进行分频处理,50分频为偶数分频,可以采用计数的方式进行实现,50/1=50,则计数到49次的时候清零,否则进行+1计数,然后产生1Mhz的时钟频率

module Count_1(

input rst_n,
input clk,
output reg [7:0]cnt,  //上升沿计数
output reg out

);

always@(posedge clk or negedge rst_n )  
begin
	if(!rst_n)
		cnt<=1'b0;
	else if(cnt==8'd49)begin
		cnt<=1'b0;
	 end
	else
		cnt<=cnt+1'b1;
end

always@(posedge clk or negedge rst_n )  
begin
	if(!rst_n)
		out <= 0;
	else if(cnt ==8'd24 | cnt ==8'd49  )begin
		out <= ~out;
	 end
	else
		out <= out;
end

endmodule 
`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期

module Count_1_tb;

		reg rst_n;
		reg clk;
		wire [7:0] cnt;  //上升沿计数
		wire out;
	
Count_1 counter
			(
			  .clk(clk),
			  .rst_n(rst_n),
			  .cnt(cnt),
			  .out(out)
);
initial	clk = 0;
always #(`Clock/2) clk= ~clk;

initial begin
   rst_n=0 ;
	#(`Clock*20);
   rst_n=1;
   #(`Clock*150);
   $stop;
end	
endmodule

波形如下 :
此时的计数器cnt为50计数器。我们让其在中间24和49的位置进行翻转,最终得到了周期为1000ns的输出,对应的时钟频率为1Mhz
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第10张图片


例2:通过设计N/2计数器的方式进行偶数4分频。

module Count_1(
		input         clk   , 
		input         rst_n , 
		
		output reg    out
);
reg [2:0] cnt;

always @(posedge clk or negedge rst_n) 
    if (!rst_n)
        cnt <= 0;
	 else if(cnt == 1)
	     cnt <= 0;
	 else
	     cnt <= cnt + 1;
		  
		  
always @(posedge clk or negedge rst_n) 
    if (!rst_n)
        	out  <= 0;
			
	 else if(cnt == 1)
	      out  <= ~out;
		  
		  
endmodule

tb测试文件:


`timescale 1ns/1ps //时间精度
`define Clock 20

module Count_1_tb;

		reg clk;
		reg rst_n;
		
		wire out;  //上升沿计数
		
Count_1 inst_Count_1 (
    .clk(clk), 
    .rst_n(rst_n),
     .out(out)
    );

initial clk = 0;
always 	#(`Clock/2) clk = ~clk;
	

initial begin
   rst_n = 0;
	#20;
	rst_n = 1;
   #(`Clock*150);
   $stop;
end	
endmodule

Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第11张图片

6.2 任意奇数N分频方式

方法:

第一步:设计N计数器(加到N-1清零)
.
第二步:产生上升沿分频信号和下降沿分频信号【都分别在(N-1)/2 和 N-1 处进行翻转 】
.
第三步:对第二步中的两个信号进行或运算。

例子:进行三分频设计:

如下为三分频的原理,设计模3计数器,分别进行时钟上升沿和下降沿的相应检测,输出的信号进行或运算。
Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第12张图片
verilog代码:

module Count_1(
		input         clk   , 
		input         rst_n , 
		
		output        out
);
reg [1:0] cnt;
reg pos_div;
reg neg_div;

always @(posedge clk or negedge rst_n) 
    if (!rst_n)
        cnt <= 0;
	 else if(cnt == 2)
	     cnt <= 0;
	 else
	     cnt <= cnt + 1;
		  

//上升沿分频信号		  
always @(posedge clk or negedge rst_n) 
    if (!rst_n)
        	pos_div  <= 0;
			
	 else if(cnt == 1)
	      pos_div  <= ~pos_div;
	 else if(cnt == 2)
	      pos_div  <= ~pos_div;
	 else 
	     pos_div  <= pos_div;
		  
//下降沿分频信号
always @(negedge clk or negedge rst_n) 
    if (!rst_n)
        	neg_div  <= 0;
			
	 else if(cnt == 1)
	      neg_div  <= ~neg_div;
	 else if(cnt == 2)
	      neg_div  <= ~neg_div;
	 else 
	     neg_div  <= neg_div;
//或运算
assign out = pos_div | neg_div;

	
		  
endmodule

tb测试文件同上。
波形如下:直接看分频出来的out信号的周期是60ns,说明三分频成功。

Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第13张图片

产生iic的scl 250khz时钟频率

方法:先50分频产生1Mhz时钟,然后4分频产生250Khz

module Count_1(

    input clk,
    input rst_n,


    output i2c_scl,  //iic总线的串行时钟信号250Khz
    output i2c_clk  //1Mhz

    );
	
reg [5:0] clk_1_cnt;
reg [1:0] clk_1_4_cnt;
reg clk_1; //1Mhz
reg clk_1_4;//250Khz

//通过系统时钟50Mhz得到1Mhz时钟
//产生50计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        clk_1_cnt <= 0;
    end
    else if (clk_1_cnt == 6'd49) begin
        clk_1_cnt <= 0;
    end
    else begin
        clk_1_cnt <= clk_1_cnt + 1;
    end
end
//每计数49和计数24的时候进行翻转,即可生成一个1Mhz的时钟
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        clk_1 <= 0;
    end
    else if (clk_1_cnt == 6'd49 | clk_1_cnt == 6'd24) begin
        clk_1 <= ~ clk_1;
    end
    else begin
        clk_1 <= clk_1;
    end
end

//对1Mhz时钟进行四分频
//产生4计数器
always @(posedge clk_1 or negedge rst_n) begin
    if (!rst_n) begin
        clk_1_4_cnt <= 0;
    end
    else if (clk_1_4_cnt == 2'd3) begin
        clk_1_4_cnt <= 0;
    end
    else begin
        clk_1_4_cnt <= clk_1_4_cnt + 1;
    end
end
//每计数1和计数3的时候进行翻转,即可生成一个250khz的时钟
always @(posedge clk_1 or negedge rst_n) begin
    if (!rst_n) begin
        clk_1_4 <= 0;
    end
    else if (clk_1_4_cnt == 2'd3 | clk_1_4_cnt == 2'd1  ) begin
        clk_1_4 <= ~clk_1_4;
    end
    else begin
        clk_1_4 <= clk_1_4;
    end
end
assign i2c_clk = clk_1;
assign i2c_scl = clk_1_4;
endmodule

tb仿真:


`timescale 1ns/1ps //时间精度
`define Clock 20

module Count_1_tb;

		reg clk;
		reg rst_n;
		

		wire i2c_scl;
		wire i2c_clk;


Count_1 inst_Count_1
		(
			.clk     (clk),
			.rst_n   (rst_n),
			.i2c_scl (i2c_scl),
			.i2c_clk (i2c_clk)
		);


initial clk = 0;
always 	#(`Clock/2) clk = ~clk;
	

initial begin
   rst_n = 0;
	#20;
	rst_n = 1;
   #(`Clock*300);
   $stop;
end	



endmodule

Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第14张图片

直接利用200分频,生成250khz时钟

module Count_1(

    input clk,
    input rst_n,

    output i2c_clk  //250Khz
	 
    );
	
reg [7:0] clk_1_cnt;

reg clk_1; 


//通过系统时钟50Mhz得到250khz时钟
//产生200计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        clk_1_cnt <= 0;
    end
    else if (clk_1_cnt == 8'd199) begin
        clk_1_cnt <= 0;
    end
    else begin
        clk_1_cnt <= clk_1_cnt + 1;
    end
end
//每计数199和计数99的时候进行翻转,即可生成一个1Mhz的时钟
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        clk_1 <= 0;
    end
    else if (clk_1_cnt == 8'd199 | clk_1_cnt == 8'd99) begin
        clk_1 <= ~ clk_1;
    end
    else begin
        clk_1 <= clk_1;
    end
end


assign i2c_clk = clk_1;
endmodule

Verilog基础知识-——计数器设计以及任意分频设计与modelsim仿真_第15张图片

更多计数器的学习

你可能感兴趣的:(FPGA基础,modelsim仿真,verilog,数字信号处理,fpga开发,fpga,硬件工程)