bcd计数器
定义:4bit的counter计数到9,再进行级联,则输出值q可以直接用bcd码表示成十进制数。
例如计数456次,输出值为q为12’h456。
q[11:8] | q[7:4] | q[3:0] |
4 | 5 | 6 |
代码如下:
counter_4bit
module counter_4bit
#( parameter c_number = 4'd10 )
(
input clk ,
input rst_n ,
input cin ,
output reg cout ,
output reg[3:0] q
);
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
q <= 4'd0 ;
else if(q == c_number-1'b1 && cin == 1'b1)
q <= 4'd0 ;
else if(cin == 1'b1)
q <= q+1'b1;
else q <= q;
end
always@(*) begin
if(q == c_number-1'b1 && cin == 1'b1)
cout <= 1'b1;
else cout <= 1'b0;
end
endmodule
bcd_counter
module bcd_counter(
input clk ,
input rst_n,
input cin ,
output cout ,
output [11:0] q
);
wire m1,m2;
counter_4bit //对counter_4bit进行三次例化
#(.c_number(10))
u1
(
.clk (clk ),
.rst_n (rst_n),
.cin (cin ),
.cout (m1 ),
.q (q[3:0])
);
counter_4bit
u2
(
.clk (clk ),
.rst_n (rst_n),
.cin (m1 ),
.cout (m2 ),
.q (q[7:4])
);
counter_4bit
u3
(
.clk (clk ),
.rst_n (rst_n),
.cin (m2 ),
.cout (cout ),
.q (q[11:8])
);
endmodule
tb
`timescale 1ns/1ns
`define clk_period 20
module tb();
reg clk;
reg rst_n;
reg cin;
wire cout;
wire[11:0] q;
initial begin
clk <= 1'b1;
cin <= 1'b0;
rst_n <= 1'b0;
#40 rst_n <= 1'b1;
repeat(1200)begin
#60 cin <= 1'b1;
#20 cin <= 1'b1;
end
#1000
$stop;
end
always #(`clk_period/2) clk <= ~clk;
bcd_counter bcd_ins1(
.clk (clk ),
.rst_n(rst_n),
.cin (cin ),
.cout (cout ),
.q (q )
);
endmodule
综合后的电路如图1所示,仿真波形图如图2所示。
图1
图2
疑问1: 为什么u2的q不在①处发生变化呢?而是在②处才捕获到cin信号,发生变化呢?
图3
猜测:即使cout是使用组合逻辑赋值的,但cin经过了一个与门传给cout有延迟,且u1中的cout传递到u2中的cin这个过程需要时间(实际情况可参考图6中的标注处),所以才在下个时钟上升沿②处发生变化。
图4 counter_4bit
图5 图6
疑问2:若cin信号不连续,且由1变0的跳变沿与时钟上升沿重合(图5所示)这时会出现错误值(889-899-900),图7为完整波形图。
图7
图8
在实际应用中,由于DFF的setup、hold要求,cin在clk上升沿之前要稳定下来,图8所示为正常状态。边沿重合本就是不稳定的状态,设计中要完全避免,但计数信号cin是随机的,不可避免会满足图6所示的情况,该计数器存在上述问题。(存疑)
Quartus中对IP核进行级联:
图9 使用counter ip核进行级联是正确的
计数器级联
参考如下链接中的内容:
即疑问2中图6、图7所示的不正常状态。
以74ls160为例,该计数器是对CP引脚(时钟脉冲输入端)计数,当CP时钟上升沿来临时,计数值加一,所以在设计计数器时应以CP为敏感条件。上述的counter_4bit中,计数值的变化以系统时钟clk为敏感条件没有实际意义,应该为当cin上升沿来临时对计数值加一,更改后的代码如下,图10是更改后的仿真波形。
counter_4bit
module counter_4bit
#( parameter c_number = 4'd10 )
(
input rst_n ,
input cin ,
output reg cout ,
output reg[3:0] q
);
always@(posedge cin or negedge rst_n)
begin
if(!rst_n)
q <= 4'd0 ;
else if(q == c_number-1'b1 && cin == 1'b1)
q <= 4'd0 ;
else if(cin == 1'b1)
q <= q+1'b1;
else q <= q;
end
always@(posedge cin) begin // 在计数器之间加上D触发器,可消除上述错误状态
if(q == c_number-1'b1 && cin == 1'b1)
cout <= 1'b1;
else cout <= 1'b0;
end
endmodule
图10