计数器的逻辑功能:记录时钟脉冲的个数
现要求设计模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
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后即可清零。
设计带使能的模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
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信号有效的时候,才能开始计数。
设计一个计数器,先递增,递增到一定数后开始递减,递减到一定数后又递增,循环反复
.
复位拉高且使能信号有效,进行计数。
当递增标志信号有效且未增到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代码:
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
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脉冲,因此实现了二分频。
由div3可看到,其为clk的三分频信号。pos_cnt为clk上升沿计数器,neg_cnt为下降沿计数器。由于是三分频,因此计数三个clk的上升和三个clk的下降沿即可,最后进行逻辑或运算得到div3.
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
任意分频通过计数器实现,但需要注意的是任意分频电路分为两种:奇数分频和偶数分频。偶数分频可直接采用计数器实现,而奇数分频还需要利用组合逻辑。
.
建议在设计过程中采用参数化设计,这样就可以随时改变参量以得到不同的分频需要。
.
在对时钟要求不是很严格的FPGA系统中,分频通常都是通过计数器的循环计数来实现的,否则将采用PLL来实现。
方法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
例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
方法:
第一步:设计N计数器(加到N-1清零)
.
第二步:产生上升沿分频信号和下降沿分频信号【都分别在(N-1)/2 和 N-1 处进行翻转 】
.
第三步:对第二步中的两个信号进行或运算。
例子:进行三分频设计:
如下为三分频的原理,设计模3计数器,分别进行时钟上升沿和下降沿的相应检测,输出的信号进行或运算。
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,说明三分频成功。
方法:先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
直接利用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
更多计数器的学习