BCD计数器设计与验证


第一部分:单个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

波形图:


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM