第一部分:单个BCD计数器
一、BCD计数器原理
- BCD码的特点:用4位二进制数,来表示一位十进制数(0~9)。
- 类似于4位二进制计数器,但4位二进制计数器需要计数到1111然后才返回0000,而十进制计数器要求计数到1001(十进制的9)就返回0000。BCD计数器是一种常见的十进制计数器。
- 而4位二进制就相当于1位十六进制,因此看十六进制更方便。
二、RTL图
三、代码编写
1、BCD_Counter 模块
module BCD_Counter(Clk, Cin, Rst_n, Cout, q);//端口首字母最好大写,各个端口用空格隔开
input Clk; //计数基准时钟
input Cin; //计数器进位输入
input Rst_n; //系统复位
output reg Cout; //计数器进位输出
output[3:0] q; //计数器输出
reg[3:0] cnt; //定义 计数器寄存器(counter)
//执行计数过程
always@(posedge Clk or negedge Rst_n) begin
if(Rst_n == 1'b0)
cnt <= 4'b0;
else if(Cin == 1'b1)begin
if(cnt == 4'd9)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt;
end
//产生进位输出信号
always@(posedge Clk or negedge Rst_n) begin
if(Rst_n == 1'b0)
Cout <= 4'b0;
else if(Cin == 1'b1 && cnt == 4'd9)
Cout <= 1'b1;
else
Cout <= 1'b0;
end
assign q = cnt;
endmodule
2、testbench 代码
`timescale 1ns/1ns
`define clock_period 20 //系统时钟20ns(50MHZ)
module BCD_Counter_tb;
//激励信号定义,对应连接到待测模块输入端口。 reg型
reg Clk;
reg Cin;
reg Rst_n;
//待检测信号定义,对应连接到待测试模块的输出端口。 wire型
wire Cout;
wire[3:0] q;
//例化待测试模块
BCD_Counter BCD_Counter0(
.Clk(Clk),
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(Cout),
.q(q)
);
//产生时钟信号
initial Clk = 1'b1;
always#(`clock_period/2) Clk = ~Clk;
//产生 Cin激励信号 和 Rst_n复位信号
initial begin
Rst_n = 0; //首先系统处于复位状态
Cin = 1'b0;
#(`clock_period*200); //延时200个系统时钟周期
Rst_n = 1'b1; //系统开始运行
#(`clock_period*20);
repeat(30) begin //重复执行30次。1个周期高电平,5个周期低电平。
Cin = 1'b1;
#`clock_period;
Cin = 1'b0;
#(`clock_period*5);
end
#(`clock_period*20);
$stop;
end
endmodule
四、波形图
结果:每计数到9,进位输出:Cout = 1 ,计数器清零。
五、内部结构
第二部分:三个BCD计数器的级联
一、RTL图
testbench:
二、代码编写
1、BCD_Counter_top 模块 (设置为 “顶层文件”)
注意:q 是12位。q0 是低位,q2 是高位。
module BCD_Counter_top(Clk, Cin, Rst_n, Cout, q);
input Clk; //计数基准时钟
input Cin; //计数器进位输入
input Rst_n; //系统复位
output Cout; //计数器进位输出
output[11:0] q; //计数器输出
wire Cout0,Cout1;
wire[3:0] q0,q1,q2;
assign q = {q2,q1,q0};
BCD_Counter BCD_Counter0(
.Clk(Clk),
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(Cout0),
.q(q0)
);
BCD_Counter BCD_Counter1(
.Clk(Clk),
.Cin(Cout0),
.Rst_n(Rst_n),
.Cout(Cout1),
.q(q1)
);
BCD_Counter BCD_Counter2(
.Clk(Clk),
.Cin(Cout1),
.Rst_n(Rst_n),
.Cout(Cout),
.q(q2)
);
endmodule
2、BCD_Counter_top_tb testbench
`timescale 1ns/1ns
`define clock_period 20
module BCD_Counter_top_tb;
reg Clk;
reg Cin;
reg Rst_n;
wire Cout;
wire[11:0] q;
BCD_Counter_top BCD_Counter_top0(
.Clk(Clk),
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(Cout),
.q(q)
);
//产生时钟信号
initial Clk = 1'b1;
always#(`clock_period/2) Clk = ~Clk;
//产生 Cin激励信号 和 Rst_n复位信号
initial begin
Rst_n = 0; //首先系统处于复位状态
Cin = 1'b0;
#(`clock_period*200); //延时200个系统时钟周期
Rst_n = 1'b1; //系统开始运行
#(`clock_period*20);
Cin = 1'b1;
//直接就让 Cin = 1 ,不再设置Cin为周期信号了。
#(`clock_period*3000);
$stop;
end
endmodule
注意:
- 1、顶层的 q 是12位
- 2、直接就让 Cin = 1 ,不再设置Cin为周期信号了。
对比之前的图:
现在的 Cin :
三、仿真结果
波形存在错误:999 之后 不是 000, 而是999 -> 990 -> 901 -> 002
1. 错误分析步骤:
(1)添加子模块波形
(2)对信号进行分组
Ctrl + A 全选信号
Ctrl + G 对信号智能分组
(3)分组完 restart 重新启动一下
(4)波形分析
Cout 一直滞后,因为有D触发器,导致了延迟。
2. 修改代码
修改方法:把always改成assign
波形图: