fpga[1.1]BCD计数器(附源码)

这篇具有很好参考价值的文章主要介绍了fpga[1.1]BCD计数器(附源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

BCD码也称二进码十进数,可以分为有权码和无权码两个种类。常见的有权BCD码有8421码、2421码、5421码,无权BCD码有余3码、余3循环码、格雷码。

fpga[1.1]BCD计数器(附源码)
其中,8421BCD码是最基本和最常用的BCD码。其各个bit位的权值分别为8d、4d、2d、1d(5421码、2421码同理)。
BCD码常用于数码管这类经常用到除法计算的硬件,通常进行除法运算耗费的时间较长,所以常常先将数字转换为BCD码,这就可以节约一部分运算时间。
两个8421BCD码进行相加时,如果和大于9,需要加6d进行修正。(详细原理见文末)
总之,了解BCD码的原理尤其是8421BCD码的应用可以对FPGA的学习产生有效帮助。

本文使用的软件和硬件平台同第一篇博文,开发流程也相同,这里就不再赘述,可以参考这里→fpga自学之路[1]计数器

1.绘制波形图

对于时序逻辑电路来说,需要时钟的驱动,复位方式选择异步复位,cin是外部输入的计数信号。
同时定义一个变量cnt来对cin进行计数(BCD码的范围是0~9,故将cnt的位宽设置为4),计数溢出时将flag置为高电平。最后设置一个4位宽的输出端口来引出计数值并连接到4个LED上,方便我们观察。
fpga[1.1]BCD计数器(附源码)

2.rtl代码

编写代码如下:

module BCD_counter
(
	input			sys_clk		,		
	input			sys_rst_n	,
	input			cin			,			//计数信号
	
	output	   [3:0]q			,			//计数值
	output		reg flag					//计满信号
);
	parameter	CNT_MAX = 9; 				//设置计数最大值
	
	reg [3:0]	cnt;						//声明一个4位宽的计数变量cnt
	
	always@(posedge sys_clk or negedge sys_rst_n)		//复位方式设为异步复位
		if(sys_rst_n == 1'b0)
			cnt <= 4'd0;				//复位信号为低电平时计数变量清零
		else	if(cin == 1'b1)begin	//计数信号到来时执行计数操作
			if(cnt == 4'd9)
				cnt <= 4'd0;			//计数器计满时清零
			else
				cnt <= cnt + 1;			//其他情况对计数信号进行计数
				end
		else 
			cnt <= cnt ;				//没有计数信号到来时维持原有数值
	
	always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			flag <= 1'b0;				//复位信号为低电平时标志信号设为低电平
		else	if(cin == 1'b1 && cnt == CNT_MAX)
			flag <= 1'b1;				//计数溢出时将标志信号置高电平
		else 
			flag <= 1'b0;				//其他情况标志信号保持不变

	assign q = cnt;						//将计数变量cnt引出
	
endmodule

Quartus II中进行一次全编译后,没有报错。
此时,我们可以在任务栏中查看RTL 视图,方便我们理解硬件结构。
fpga[1.1]BCD计数器(附源码)

fpga[1.1]BCD计数器(附源码)

3.编写testbench代码

为了验证我们的设计,需要编写testbench文件进行仿真验证
我们编写testbench文件,是为了产生满足条件的激励信号,同时对模块的输出进行捕捉,测试输出是否满足要求。
编写代码如下:

`timescale 1ns/1ns		//设置时间参数
module tb_BCD_counter();	//模块声明

reg	sys_clk;			
reg sys_rst_n;
reg cin;

wire [3:0]q;
wire flag;				//声明端口

initial	sys_clk = 1'b0;
always#10 sys_clk = ~sys_clk;		//设置时钟周期为20ns

initial					//初始化语句
	begin
		sys_rst_n = 1'b0; cin = 1'b0;//设置初始参数
		#40		//延迟40个时间单位
		sys_rst_n = 1'b1;		//复位信号结束使能
		#10
		repeat(30000)begin		//我们产生多次波形方便观察
		cin = 1'b1;
		#20;
		cin = 1'b0;
		#100;					//产生占空比为1:6的方波
		end
	end

BCD_counter	BCD_counter_inst		//例化语句
(
	.sys_clk	(sys_clk),
	.sys_rst_n	(sys_rst_n),
	.cin		(cin),
	
	.q			(q),
	.flag		(flag)
);   

endmodule

tb_BCD_counter文件添加到工程中,并进行编译,结果没有报错。
添加完仿真文件后,使用Quartus & Modelsim 联合仿真
首先添加波形
fpga[1.1]BCD计数器(附源码)
得到仿真波形如下:
fpga[1.1]BCD计数器(附源码)

对局部进行放大,可以看出:
当复位信号有效时(为低电平),cnt未开始计数,复位信号为高电平且计数信号到来时计数开始
fpga[1.1]BCD计数器(附源码)
每次计数信号cin到来时cnt增加1
fpga[1.1]BCD计数器(附源码)
当计数溢出时cnt清零,且flag信号电平被拉高
fpga[1.1]BCD计数器(附源码)
从这里看出来我们的设计是正确的。

4.拓展

上面的流程中,我们使用了设计了单个8421BCD计数器,而在实际开发中单个计数器常常不能胜任复杂的项目,此时就需要用到级联多个BCD计数器来满足我们的需求。
我们需要建立一个顶层模块,并将上文中我们使用到的计数器电路作为底层模块进行多次例化。
代码如下:
编写代码如下:

module BCD_counter_top			//声明顶层模块
(
	input			sys_clk		,		
	input			sys_rst_n	,
	input			cin			,		//计数信号
	
	output	   [11:0]q			,		//为了级联三个计算器故位宽设置为12
	output		 	flag				//计满信号
);

wire cin1;
wire cin2;
wire[3:0]q0,q1,q2;

BCD_counter		BCD_counter_inst0
(
	.sys_clk(sys_clk),		
	.sys_rst_n(sys_rst_n),
	.cin(cin),			//计数信号
	
	.q(q0),			//计数值
	.flag(cin1)					//计满信号
);

BCD_counter		BCD_counter_inst1
(
	.sys_clk(sys_clk),		
	.sys_rst_n(sys_rst_n),
	.cin(cin1),			//计数信号
	
	.q(q1),			//计数值
	.flag(cin2)					//计满信号
);

BCD_counter		BCD_counter_inst2
(
	.sys_clk(sys_clk),		
	.sys_rst_n(sys_rst_n),
	.cin(cin2),			//计数信号
	
	.q(q2),			//计数值
	.flag(flag)					//计满信号
);

assign q = {q2,q1,q0};

endmodule

编译通过后,我们查看rtl视图,可以看到三个底层的BCD_计数器模块共同级联成了top模块:
fpga[1.1]BCD计数器(附源码)
然后我们再将上面编写的testbench文件稍加修改,用于该模块的仿真:
代码如下:
编写代码如下:

`timescale 1ns/1ns		//设置时间参数
module tb_BCD_counter_top();	//模块声明

reg	sys_clk;			
reg sys_rst_n;
reg cin;

wire [11:0]q;
wire flag;				//声明端口

initial	sys_clk = 1'b0;
always#10 sys_clk = ~sys_clk;		//每10个时间单位sys_clk反转一次

initial					//初始化语句
	begin
		sys_rst_n = 1'b0;			cin = 1'b0;//设置初始参数
		#40		//延迟40个时间单位
		sys_rst_n = 1'b1;	
		#10
		repeat(3000)begin			//产生3000个周期占空比为1:6的方波
		cin = 1'b1;
		#20;
		cin = 1'b0;
		#100;
		end
	end

BCD_counter_top	BCD_counter_top_inst		//例化语句
(
	.sys_clk	(sys_clk),
	.sys_rst_n	(sys_rst_n),
	.cin		(cin),
	
	.q			(q),
	.flag		(flag)
);   

endmodule

在modesim中产生的波形如图:
fpga[1.1]BCD计数器(附源码)
通过观察波形,我们发现每个计数器间进位信号的传递均延迟了一个时钟周期,导致顶层模块的进位信号延迟了两个时钟周期,这不符合我们的预期,所以需要对rtl代码进行重新编写。

原代码段:
	always@(posedge sys_clk or negedge sys_rst_n)
			if(sys_rst_n == 1'b0)
				flag <= 1'b0;		
			else	if(cin == 1'b1 && cnt == CNT_MAX)
				flag <= 1'b1;
			else 
				flag <= 1'b0;
修改为:
	assign flag = (cnt == 4'd9 && cin == 1'b1);

**同时将flag的类型修改为wire型**

修改后的仿真波形如下,我们可以看到仿真的波形是正确的
fpga[1.1]BCD计数器(附源码)
原因分析:
修改前的底层rtl电路:
我们可以看到flag信号其实是经过了一个D触发器的,所以延迟了一个时钟周期。这个延迟在经过多级电路后被放大,所以造成了顶层电路更大的延迟。
fpga[1.1]BCD计数器(附源码)
修改后的电路:
flag信号经过的是一个逻辑门电路,延迟可以忽略不计,所以在经过层级较少的电路后,也不会产生明显的延迟。
fpga[1.1]BCD计数器(附源码)

5.总结

本次项目我们设计了一个BCD计数器,并在拓展部分将三个BCD计数器级联起来组成了一个12位的计数器,通过分析产生的异常,学会了简单的代码调试,也理解了verilog的语法差异。文章来源地址https://www.toymoban.com/news/detail-408231.html


附.关于8421BCD码的思考

  • 两个8421BCD码相加,如1001(9d)+1001(9d)=0001_0010(18d)。虽然用二进制看来等式是正确的,但是按照8421BCD码的规则来看0001_0010=12,此时就需要+6来进行修正。
    为什么加6?因为二进制的第五位权值是16,但在8421BCD码中该位权值为10,故需要加6来修正。

到了这里,关于fpga[1.1]BCD计数器(附源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • logisim,quartus实现模为60的BCD码计数器

    一、分析:利用封装的74161完成带有异步复位端的模6和模10计数器,然后级联构成模为60的BCD码计数器: ​ 由74161功能表可知要构成模6计数器和模10计数器,可以采用同步置数法:当达到需要的计数终点时反馈一个置数信号到LD非,使得输出Q3Q2Q1Q0=D3D2D1D0。也可以采用反馈清零法

    2024年02月06日
    浏览(31)
  • 【EDA技术】 实验二 BCD码输出的60进制计数器的VHDL

    (1) 熟悉EDA软件,并能熟练使用。 (2) 分析设计任务,根据任务要求完成设计内容。 (3) 利用软件对设计内容进行仿真调试,得到正确运行结果。 (1)设计具有异步清零,同步使能的60进制计数器,并用VHDL语句进行例化 (2)设计七段译码器的VHDL代码,利用元件例化的

    2024年02月02日
    浏览(36)
  • 【FPGA】Verilog:计数器 | 异步计数器 | 同步计数器 | 2位二进制计数器的实现 | 4位十进制计数器的实现

    目录 Ⅰ. 实践说明 0x00 计数器(Counter) 0x01 异步计数器(Asynchronous Counter)

    2024年02月05日
    浏览(46)
  • 【FPGA】Verilog:升降计数器 | 波纹计数器 | 约翰逊计数器 | 实现 4-bit 升降计数器的 UP/DOWN

    目录 Ⅰ. 理论部分 0x00 升降计数器(UP DOWN Counter) 0x01 波纹计数器(Ripple Counter)

    2024年02月05日
    浏览(40)
  • FPGA-计数器的实现

    计数器是依托时钟实现的,在时钟沿(一般在上升沿)进行检测,实现计数加1; 计数是从0开始计数的,所以计数值为(M-1),其中M为计数的值。比如计数到10,我们实现时到9即可; 这里为计数器的第一种实现方法,该方法非最优方法,我们只需要了解即可,后续我们会介

    2024年02月04日
    浏览(30)
  • 计数器简介以及FPGA实现

    在时序逻辑电路中,最基本的单元是寄存器,本篇将会介绍如何利用寄存器,实现一个具有计数器功能的电路。在FPGA开发中,一切与时间有关的设计都会用到计数器,所以学会设计计数器至关重要。 计数是一种最简单基本的运算,计数器就是实现这种运算的逻辑电路,计数

    2024年02月05日
    浏览(42)
  • 【FPGA】Verilog:时序电路设计 | 二进制计数器 | 计数器 | 分频器 | 时序约束

    前言: 本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例:计数器与分频器   ​​ 功能特性: 采用 Xilinx Artix-7 XC7A35T芯片  配置方式:USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度  存储器:2Mbit SRAM   N25Q064A SPI Flash(样图旧款为N25Q032A) 通用

    2024年02月02日
    浏览(48)
  • FPGA实验二:模可变计数器设计

    目录 一、实验目的 二、实验要求 三、实验代码 1.实验源码

    2024年02月12日
    浏览(39)
  • FPGA Vivado环境下实现计数器

    本文实现的是一个简单的计数器,模块中包含时钟信号和复位信号,计数使用的是一个四位的输出,复位键有效时,计数器置零,当时钟信号上升沿时,计数加一,实现计数。(仅供参考) 建立工程counter,并新建一个设计文件命名为:counter     3.打开counter文件,进行计数器

    2024年01月21日
    浏览(37)
  • Verilog语言实现FPGA上的计数器

    Verilog语言实现FPGA上的计数器 计数器是数字电路中经常使用的基本元素之一,它用于生成指定脉冲数量或者指定计数范围内的计数信号。在现代数字电路设计中,FPGA(Field Programmable Gate Array)作为一种可编程逻辑器件被广泛应用,可以通过Verilog语言来实现计数器模块。 在V

    2024年02月05日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包