一、原理
计数器可以统计输入脉冲的个数,具有计时、计数、分频、定时、产生节拍脉冲等功能。
根据计数器中触发器时钟端的链接方式,分为同步计数器和异步计数器;
根据计数方式,分为二进制计数器、十进制计数器和任意进制计数器;
根据计数器中的状态变化规律,分为加法计数器、减法计数器和加/减计数器。
二、常规计数器
// 带使能的模9(out=0、1、2、3、4、5、6、7、8,可以定时9×周期T)异步清零计数器
`timescale 1ns/1ps
module cnt #(parameter COUNT=9)( //这里可以改模
input clk,
input rst_n,
input cnt_en,
output reg [3:0]out//在这里更改out的位宽来适配COUNT的值
); // 9=1001 如果这里位宽改了,测试用例也要改out的位宽
reg set;
always@(posedge clk or negedge rst_n)//异步清零
begin
if(!rst_n)
begin out<=7'd0;set<=1'b0;end
else if(cnt_en)
begin
if(out==COUNT-1)
begin out<=7'd0;set<=1'b1;end
else
begin out<=out+1'b1;set<=1'b0;end
end
else
begin out<=7'd0;set<=1'b0;end
end
endmodule
三、生成的电路图
1、清零未使能时(rst_n=0,cnt_en=0),Q端为0,经过 +1 后,第一个选择器输出1。由于未使能,则第二个选择器的输入 I0 被“卡住”,所以只要不使能,第二个选择器输入 I0 端一直是 1 。由于不使能, 第二个选择器的输出选择默认输入(default)即第二个选择器一直输出0,即D端一直是0 。总结:清零且未使能,不管来多少个时钟上升沿,D触发器的D端和Q端(out)都是0
2、清零未使能时(rst_n=1,cnt_en=0),和上面一样,不管来多少个时钟上升沿,D触发器的D端和Q端(out)都是0
3、不清零且使能(rst_n=1,cnt_en=1),第一个时钟上升沿未到前,D端是1,但是Q端仍是0。在第一个上升沿后,Q端变成1 。第一个上升沿后,第二个上升沿前的这段时间,Q端 +1 ,第一个选择器输出2,第二个选择器输出2,D端是2,Q端是1 。
总结:不清零且使能(rst_n=1,cnt_en=1),第一个时钟上升沿到达那一刻,Q端是1 。
4、不清零且使能(rst_n=1,cnt_en=1),第八个时钟上升沿到,Q端是8 。在第八个上升沿后,第九个上升沿前的这段时间(蓝色部分),Q端是8,第一个选择器输出0 ,第二个选择器输出0,D端是0 。当第九个上升沿到时,Q端才变成0 。
总结:不清零且使能(rst_n=1,cnt_en=1),第八个时钟上升沿到达那一刻,Q端是8 。
不清零且使能(rst_n=1,cnt_en=1),第九个时钟上升沿到达那一刻,Q端是0 。
不清零且使能(rst_n=1,cnt_en=1),第十个时钟上升沿到达那一刻,Q端是1 。
5、对电路图总结:不清零且使能(rst_n=1,cnt_en=1)后的第一个上升沿开始,到第十个上升沿结束,即out=1、2、3、4、5、6、7、8、0 各维持一个时钟周期。这样便是9个时钟周期。
四、仿真波形
对于上面的电路,只要 不清零且使能(rst_n=1,cnt_en=1),那么第一个out肯定是1。
但是在引用计数模块时,可以加个set信号,让它在 不清零且使能(rst_n=1,cnt_en=1)后(第八个上升沿时,out为8)的第九个上升沿判断out是否为8,如果是,则让out=0,且set=1。
第十个上升沿,out=1,set=0 。
也就是说:
可以用set信号来定时,当 不清零且使能(rst_n=1,cnt_en=1)且在第一个时钟有效沿后,只要 out = 0,那么set就输出一个高电平。相邻两个set高电平期间的时间,就是 9个时钟周期。分别是 out =0、1、2、3、4、5、6、7、8 这九个值各维持一个时钟周期。
五、测试代码
module cnt_tst();
reg clk;
reg rst_n;
reg cnt_en;
wire [3:0]out;
cnt U_cnt(
.clk(clk),
.rst_n(rst_n),
.cnt_en(cnt_en),
.out(out)
);
initial
begin
clk=1;
rst_n=0;
cnt_en=0;
#10;
rst_n=1;
cnt_en=1;
#100;
cnt_en=0;
#200;
cnt_en=1;
#2000;
cnt_en=0;
end
always #10 clk=~clk;
endmodule