基于FPGA的电子计算器系统设计(附代码)

EETOP 2022-11-26 12:14


今天给大侠带来基于FPGA的电子计算器设计。话不多说,上货。


导读 



本篇介绍了一个简单计算器的设计,基于 FPGA 硬件描述语言 Verilog HDL,系统设计由计算部分、显示部分和输入部分四个部分组成,计算以及存储主要用状态机来实现。显示部分由六个七段译码管组成,分别来显示输入数字,输入部分采用4*4矩阵键盘,由0-9一共十个数字按键,加减乘除四个运算符按键,一个等号按键组成的。通过外部的按键可以完成加、减、乘、除四种功能运算,其结构简单,易于实现。本篇为本人毕业设计部分整理,各位大侠可依据自己的需要进行阅读,参考学习。


前言



在国外,电子计算器在集成电路发明后,只用短短几年时间就完成了技术飞跃,经过激烈的市场竞争,现在的计算器技术己经相当成熟。计算器已慢慢地脱离原来的“辅助计算工具”的功能定位,正在向着多功能化、可编程化方向发展,在各个领域都得到了广泛的应用。用计算器不仅可以实现各种各样复杂的数学计算还可以用来编制、运行程序,甚至解方程组,图形计算器还可以进行图形处理。计算器内置的软件允许用户进行类似于对计算机的文件和目录管理等操作,允许用户对图形界面进行定制,同时各种新技术也被应用到计算器里使计算器功能越来越强大。可以说,计算器就是一个“微微型”的计算机。国内也有厂商利用计算器芯片开发新的产品,但对计算器技术的研究、计算器芯片的设计还处于起步阶段。计算器的主要功能还是在于“计算”,不妨称之为“低档计算器”。即便是对这种计算器,很多厂商也只从事计算器的组装、销售业务。一些IC设计公司、芯片提供商也开始研究计算器技术。


三、整体设计原理介绍




FPGA具有运算速度快,编程简易等优点,它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。它还是作为专用集成电路领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。所以我采用FPGA原理设计了本次计算器,主要包括数码管部分,按键电路部分以及运算器部分,设计方案基本原理如下。

3.1 数码管显示

数码管的显示分为两种,静态显示和动态显示,在这里我们使用的是动态显示。动态显示的特点是将所有位数码管的段选并联在一起,由位选线控制是哪一位数码管是有效的。这样一来,就没有为每一位数码管配置一个锁存器的必要,从而就会大大简化了硬件电路。选亮的数码管采用动态扫描显示。所谓的动态扫描显示就是轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管在同时都在显示。动态的显示的亮度要比静态的显示略差了一些,因而我们在选择需要的限流电阻应小于静态显示电路中的。


3.2 按键部分原理

我们采用了4*4矩阵键盘扫描在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。这样,一个端口就可以构成4*4=16个按键,比之直接将端口线的应用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(9键)。由此可见,我们在需要的键数比较多时,采用矩阵法来做键盘是合理的。

矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,列线通过了电阻连接正电源,并将行线所接的I/O口作为输出端,而列线所接的I/O口则作为输入。这样,当按键没有按下时,所有的输入端都是高电平,代表无键按下。行线输出是低电平,一旦有键按下,则输入线就会被拉低,这样,通过读入输入线的状态就可得知是否有键按下了。下面我们介绍行扫描法。

行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法,介绍过程如下。

首先判断键盘中有无键按下:将全部行线置低电平,然后检测列线的状态。只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中。若所有的列线均是高电平,则键盘中无键按下。

其次判断闭合键所存在的位置:在确认有键按下后,即可进入确定具体闭合键的过程。其方法是:依次将行线置为低电平,就是在置某根行线为低电平时,其它线为高电平。在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。


3.3 状态机原理

运算部分我们主要应用了状态机进行运算和存储的,主要是有限状态机,下面对有限状态机进行简单的介绍。

状态机简写为FSM(Finite State Machine),主要分为2大类:第一类,若输出只和状态有关而与输入无关,则称为Moore状态机;第二类,输出不仅和状态有关而且和输入有关系,则称为Mealy状态机。要特别注意的是,因为Mealy状态机和输入有关,输出会受到输入的干扰,所以可能会产生毛刺(Glitch)现象,使用时应当注意。事实上现在市面上有很多EDA工具可以很方便的将状态图的描述转换成可以综合的Verilog程序代码。

关于状态机的一个极度确切的描述是它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前”节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态,状态机停止。

包含一组状态集(states)、一个起始状态(start state)、一组输入符号集(alphabet)、一个映射输入符号和当前状态到下一状态的转换函数(transition function)的计算模型。当输入符号串,模型随即进入起始状态。它要改变到新的状态,依赖于转换函数。在有限状态机中,会有有许多变量,例如,状态机有很多与动作(actions)转换(Mealy机)或状态(摩尔机)关联的动作,多重起始状态,基于没有输入符号的转换,或者指定符号和状态(非定有限状态机)的多个转换,指派给接收状态(识别者)的一个或多个状态,等等。

传统应用程序的控制流程基本是顺序的:遵循事先设定的逻辑,从头到尾地执行。很少有事件能改变标准执行流程;而且这些事件主要涉及异常情况。“命令行实用程序”是这种传统应用程序的典型例子。

另一类应用程序由外部发生的事件来驱动——换言之,事件在应用程序之外生成,无法由应用程序或程序员来控制。具体需要执行的代码取决于接收到的事件,或者它相对于其他事件的抵达时间。所以,控制流程既不能是顺序的,也不能是事先设定好的,因为它要依赖于外部事件。事件驱动的GUI应用程序是这种应用程序的典型例子,它们由命令和选择(也就是用户造成的事件)来驱动。

Web应用程序由提交的表单和用户请求的网页来驱动,它们也可划归到上述类别。但是,GUI应用程序对于接收到的事件仍有一定程度的控制,因为这些事件要依赖于向用户显示的窗口和控件,而窗口和控件是由程序员控制的。Web应用程序则不然,因为一旦用户采取不在预料之中的操作(比如使用浏览器的历史记录、手工输入链接以及模拟一次表单提交等等),就很容易打乱设计好的应用程序逻辑。

显然,必须采取不同的技术来处理这些情况。它能处理任何顺序的事件,并能提供有意义的响应,即使这些事件发生的顺序和预计的不同。有限状态机正是为了满足这方面的要求而设计的。

有限状态机是一种概念性机器,它能采取某种操作来响应一个外部事件。具体采取的操作不仅能取决于接收到的事件,还能取决于各个事件的相对发生顺序。之所以能够做到这一点,是因为机器能跟踪一个内部状态,它会在收到事件后进行更新。为一个事件而响应的行动不仅取决于事件本身,还取决于机器的内部状态。另外,采取的行动还会决定并更新机器的状态。这样一来,任何逻辑都可建模成一系列事件/状态组合。

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。

有限状态机用于描述电路模型的时序行为,所有的输入都可以看成是模型的激励,所有的输出可以看成是模型对激励的响应。CLK提供时间基准。

图3-1 抽象时序电路的行为模型
Figure 3-1 abstract behavior model of sequential circuits

时序电路模型可以表示为:R=F(t),这里F(t)是模型行为的描述。当电路的输出仅仅与状态时间有关时候,所描述的模型为摩尔型状态机;当电路的输出不仅与时间有关,也与当前的输入信号有关时,称为米利型状态机。


四、计算器设计的电路部分



4.1  FPGA的最小系统

FPGA最小系统是可以使FPGA正常工作的最简单的系统。它的外围电路尽量最少,只包括FPGA必要的控制电路。一般我们所说的FPGA的最小系统主要包括:FPGA芯片,下载电路,外部时钟,复位电路和电源。

4.1.1  电源时钟复位

我们采用了低电平复位电路,电路图如下

 

图4-1 复位电路
Figure 4-1 reset circuit

4.1.2  JTAG接口

JTAG最初是用来对芯片进行测试的,JTAG的基本原理是在器件内部定义一个TAP(Test Access Port;测试访问口)通过专用的JTAG测试工具对内部节点进行测试。JTAG测试允许多个器件通过JTAG接口串联在一起,形成一个JTAG链,能实现对各个器件分别测试。如今,JTAG接口还常用于实现ISP(In——System Programmer,在系统编程),对FLASH等器件进行编程。

JTAG编程方式是在线编程,传统生产流程中先对芯片进行预编程然后再装到板上,简化的流程为先固定器件到电路板上,再用JTAG编程,从而大大加快工程进度。JTAG接口可对DSP芯片内部的所有部件进行编程。

JTAG(Joint Test Action Group,联合测试行动小组)是一种国际标准测试协议,主要用于芯片内部测试及对系统进行仿真、调试,JTAG技术是一种嵌入式调试技术,它在芯片内部封装了专门的测试电路TAP(Test Access Port,测试访问口),通过专用的JTAG测试工具对内部节点进行测试。

如今大多数比较复杂的器件都支持JTAG协议,如ARM、DSP、FPGA器件等。标准的JTAG接口是4线:TMS、TCK、TDI、TDO,分别为测试模式选择、测试时钟、测试数据输入和测试数据输出。如今JTAG接口的连接有两种标准,即14针接口和20针接口。

支持边界扫描的逻辑元器件与测试相关的所有外部通信都采用串行通信方式,允许测试指令及相关的测试数据串行送给元器件,然后允许把测试指令的执行结果从元器件中串行读出。为了完成这样的功能,边界扫描技术包含了一个与元器件的每个引脚相接,包含在边界扫描寄存器单元中的寄存器链,这样元器件的边界信号可以用扫描测试原理进行控制和观察,这也是边界扫描的含义。

 

图4-2 JTAG配置接口电路
Figure 4-2 the JTAG configuration interface circuit


4.1.3  FLASH

闪存存储器是一类非易失性存储器,即使在供电电源关闭后仍能保持片内信息。数字电路中经常需要使用大容量存储器,串行Flash存储速度快,体积小,功耗低,在FPGA中设计中发挥的作用也越来越大,广泛应用于实现系统及功能验证。FPGA的灵活性和串行Flash的体积小的特点相结合,具有灵活性强实用性强等特点。我们的Flash程序存储电路如图4-3所示。

 

图4-3 Flash程序存储电路
Figure 4-3 Flash program memory circuit



4.2  数码管电路

我们应用的LED数码管段数为7段,就是7个发光二极管,任意一个阿拉伯数字0-9都是可以通过亮灭组合来实现的。LED数码管根据LED的接法不同分为共阴和共阳两类,共阴极数码管公共端接地,共阳极数码管公共端接电源,另一个非公共端的引脚留给用户的I/O直接控制了,在这里我们采用共阳极数码管。我们在这个应用中,把公共端连接到了FPGA的I/O引脚上,这就是我们数码管的片选信号。如果我们FPGA的I/O引脚输出低电平0,那么这个数码管就是可以显示数字的,如果我们这个I/O输出高电平1。那么即使不管数码管的段选端输出0还是1,都没有办法将这7个发光二极管的任意一个点亮,这也达到了关闭数码管显示的效果。一旦这样,这个数码管的公共端被当作数码管片选引脚使用了,虽然不是名副其实的“片选”,也是可以达到我们想要的效果。了解LED的这些特性,对编程是很重要的,不同类型的数码管,除了它们的硬件电路有差异外,方法也是不同的。


4-4 数码管电路图
Figure 4-4 digital tube circuit diagram


4.3  按键输入电路

我们要实现一个计算器,首先需要有计算器的输入信号,通常是使用连接在FPGA的GPIO接口上的pushbutton作为输入信号。简易计算器的输入信号比较少,只需要数字和运算符号。我们可以将其各自使用一个按键来表示,然后都连接在FPGA的通用接口上。此时,我们便引入了矩阵键盘构造计算器的输入。

我们采用的是4*4矩阵键盘电路,矩阵键盘又称行列式键盘,它是用4条I/O线作为行线4条I/O线作为列线组成的键盘。在行线和列线的每一个交叉点上,设置一个按键。这样键盘中的按键的个数就是4*4=16个。这种行列式键盘结构能够有效地提高单片机系统中I/O口的利用率。其中的ROW0,ROW1,ROW2,ROW3,以及COL0,COL1,COL2,COL3信号分别连接到了FPGA上,使用一种独特的方法,这8个信号能够表示出16个按键各自按下的状态。即这16个按键只占用了FPGA的8个管脚,而使用每个按键都连接到了FPGA的输入管脚需要占用16个管脚。

行列矩阵采用了扫描的形式,其8个连接到FPGA上的信号是4个输入4个输出的。我们一般使用的其他按键方法都是将按键产生的信号作为输入。在这里,ROW0-ROW3为FPGA的输入信号,COL0-COL3是FPGA的输出信号;电路上,ROW0-ROW3还要连接一个上拉电阻。

如果左上方第一个按键被按下,ROW0和COL0连接在一起;如果不按下,两个信号则没有连接。16个按键表示16种连接的关系,在没有按键按下的时候,输出的COL信号就悬空了,输入的4个ROW信号收到上拉电阻的影响都是高电平1。如果说连接COL3与ROW2的按键被按下,那么FPGA的输入信号ROL2就等于输出信号COL3的值,其他的ROW输入信号则全部为1。

我们在FPGA内部使输出的COL0信号为0而其他的COL是1,那么不管其他的列上对应的按键都怎么按下,都有输入的FPGA的ROW为全1;仅仅当第一列的4个按键中有一个按下时,对应的行值为0,其余3个的行值为1,这样的话第一列所对应的按键就唯一确定下来了。在这样的输出状态下(COL[3:0]=1110),4个输入只能确定出来第一列的4个按键。要是按下的不是第一行的4个按键,那么输入值ROW[3:0]为1111,表示第一列没有按键被按下。于是我们开始扫描第二列,就是令COL[3:0]输出1101,然后查看ROW上是否有值为0。如果此列上仍然没有扫描到,就继续扫描下一列。

对于一组数码管动态扫描显示需要由两组信号来控制:一组是输出口输出的字形代码,用来控制显示的字形,称为段码;另一组是位输出口输出的控制信号,用来选择第几位数码管的工作,称为位码。

由于各位数码管的段线并联,段码的输出对各位数码管来说都是相同的。因此,同一时刻如果各位数码管的位选线都处于选通状态的话,位数码管将显示相同的字符。若要各位数码管能够显示出与本位相应的字符,就必须采用扫描显示方式。即在某一时刻,只让某一位的选线处于导通状态,而其他各位的位选线处于关闭状态。同时,段线上输出相应位要显示字符的字形码。这样同一时刻,只有选通的那一位显示出字符,而其它各位则是熄灭的,如此循环下去,就可以使各位数码管显示出将要显示的字符。

虽然这些字符是在不同时刻出现的,而且同一时刻只有一位显示,其它各位熄灭,但是由于数码管具有余辉特性和人眼有视觉暂留现象,只要每位数码管显示间隔足够短,给人眼的视觉印象就会是连续稳定地显示。

 

图4-5键盘电路
Figure 4-5 The keyboard circuit



4.4  电源电路设计

我们使用的是LM1117,LM1117是一个低压差电压调节器系列。其压差在1.2V输出,负载电流为800mA时为1.2V。它与国家半导体的工业标准器件LM317有相同的管脚排列。LM1117有可调电压的版本,通过2个外部电阻可实现1.25~13.8V输出电压范围。另外还有5个固定电压输出(1.8V、2.5V、2.85V、3.3V和5V)的型号。

LM1117提供电流限制和热保护。电路包含1个齐纳调节的带隙参考电压以确保输出电压的精度在±1%以内。LM1117系列具有LLP、TO-263、SOT-223、TO-220和TO-252 D-PAK封装。输出端需要一个至少10µF的钽电容来改善瞬态响应和稳定性。

 

图4-6 5V转数字3.3V电路
Figure 4-6 5V to 3.3V circuit

 

图4-7 5V转数字2.5V电路
Figure 4-7 5V to 2.5V circuit
 

图4-8 5V转数字1.2V电路
Figure 4-8 5V to 1.2V circuit
 

图4-9 电源插座电路
Figure 4-9 power socket circuit
 

图4-10 电源开关电路
Figure 4-10 power switch circuit


五、总体代码设计



系统总体设计框图如图5-1所示。此设计由计算部分、存储部分、显示部分和输入部分组成。

图5-1 计算器的系统组成框图
Figure 5-1 calculator system composition block diagram

 

图5-2 RTL视图
Figure 5-2 RTL view

5.1  计算器的输入部分

计算器输入部分的设计最主要的是按键译码电路的设计和实现。计算器的输入部分是由0—9十个数字按键、加减乘除四则运算的运算符按键、一个等号按键和一个清零按键组成的,设计所要做的是对按键信息进行译码,使其在计算器内部可以使用。这里使用的是4*4键盘矩阵作为输入。数字按键译码电路的主体部分Verilog语言描述如下。

图5-3 数字电路译码部分代码
Figure 5-3 digital tube display program code


设计的键盘矩阵输入模块如图:

 

图5-4 键盘矩阵驱动模块

Figure 5-4 matrix keyboard driver module


在输入键盘矩阵驱动模块中,我们有三个输入,四个输出,其中clk为主时钟输入,rst_n为复位信号,row为4*4键盘的行输入信号,输出col列信号,data为键入的数字(0-15),valid为数字的脉冲信号,clk_1k为给计算模块输出的时钟。


5.2  计算器的运算和存储部分

存储部分用状态机和寄存器来实现,我们输入的数字应用了移位拼接的原理,若第一个输入的是数字键,则保存下来,第二次输入还是输入的是数字键时,第一个数值左移变为十位,第二次的为个位,第三次若还是数字键,那么第一个数值将变为百位,第二个为十位,第三个为个位,以此类推,直到有符号键输入。

当进行第一次计算时,第一个数字存放在num1里面。按下运算符以后,第二个数字放在bin_data里面。当再按下运算符号或者等号时,第一次计算的结果将存放在ans里面,同时reg清零,等待下一个数字的输入。进行第二次运算时,将num1里面的结果与reg里面新输入的数字进行运算,再将运算结果存放在num1里面,直到最后按下等号按键的时候,显示最终的运算结果。

程序框图如下:

图5-5 状态1程序框图

Figure 5-5 state 1 program block diagram


图5-6 状态2程序框图

Figure 5-6 state 2 program block diagram


计算部分驱动模块如图5-7所示,共有4个输入部分,一个输出。其中clk为时钟,flag为数字脉冲信号,rst_n为复位,key_data为按键数据输入,bin_data为二进制中间数据输出。

 

图5-7 计算部分驱动模块

Figure 5-7 calculation part of the driver module


5.3  计算器的显示部分

显示部分是系统的输出部分,用于显示按键值及计算结果,由于数字系统的数据运算都是二进制的,而输出表达式都是BCD码,为了满足BCD码的译码显示,最方便的方法就是利用译码程序在FPGA中实现。本文采用的是共阳极7段数码管,显示数字时需要将对应管脚置为低电平,输出时,从左到右,按从高到低位的顺序依次接g、f、e、d、c、b、a,小数点为h,为最高位。七段译码器的基本结构如图5-8所示。

图5-8 七段译码器的结构

Figure5-8 The structure of the seven-segment decoder


Verilog硬件位选信号描述性语言描述如下图5-9所示:

图5-9 数码管显示位选程序代码

Figure 5-9 digital tube display program code


Verilog硬件段选信号描述性语言描述如下图5-10所示:

图5-10 数码管显示段选程序代码

Figure 5-10 digital tube display program code segment

 

图5-11 数码管显示驱动模块

Figure 5-11 digital tube display driver module


图5-11为数码管显示驱动模块,一共三个输入,两个输出,其中clk为时钟,rst_n为复位,data_in数据输入,sel为位选信号,seg为段选信号。

六、仿真验证设计



6.1  ModelSim简介

在仿真设计时,用到了Mentor公司(已被西门子收购)的Modelsim,这是一款硬件描述语言仿真软件,该款软件不单单能提供十分友好的仿真环境,而且它也是我们业界第一个也是仅此一个的单内核支持VHDL和Verilog语言混合仿真的软件。它采用直接优化的编译技术、Tcl/Tk技术、和单一内核仿真技术,从而达到令人编译仿真速度快的效果,而且编译代码和整个平台没有关系,这样就更容易保护IP核,它是FPGA/ASIC设计的首选仿真软件。

Modelsim有不同版本,例如:SE、PE、LE和OEM,其中最高级的版本是SE,而集成在 Actel、Atmel以及Lattice等FPGA厂商设计工具中的都是其OEM版本。

Modelsim SE支持PC、UNIX和LINUX的混合平台;能给出十分全面到位以及高性能的验证功能;全面支持业界设定的广泛标准;同时Mentor Graphics公司提供了整个行业最出色的技术支持与服务。

Modelsim的主要特点有:

1)支持单内核的VHDL和Verilog混合在一起进行仿真处理;

2)具有源代码模版、助手以及项目管理功能;

3)汇聚了性能考核、波形参考、代码覆盖、数据流Chase X、Signal Spy、虚拟对象Virtual Object、Assertion窗口、Memory窗口、源码窗口显示信号值、信号条件断点等众多调试功能;

4)C和Tcl/Tk接口,C调试;

5)能够实现对System C的直接支持功能,同时可以和HDL任意混合使用;

6)能够实现System Verilog的设计功能;

7)可以做到对系统级描述语言进行最全面的支持;

8)可以单独或同时进行行为(behavioral)、RTL级、和门级(gate-level)的代码。

9)能够实现RTL和门级优化,编译仿真速率非常快,跨平台跨版本的仿真。



6.2  模块仿真验证分析

FPGA设计流程包括设计输入,仿真,综合,生成,板级验证等很多阶段。在整个设计流程中,完成设计输入并成功进行编译仅仅能说明设计符合一定的语法规范,并不能说明设计功能的正确性,这时,我们就需要通过仿真对设计进行验证。我们主要进行的是功能仿真,又叫逻辑仿真,是指在不考虑器件延时和布线延时的理想情况下对源代码进行逻辑功能验证;而时序仿真是在布局布线后进行。我们仿真是为了保证设计的正确性。


6.2.1  矩阵按键模块

矩阵键盘测试程序流程框图如下:

图6-1 按键测试程序流程图

Figure 6-1 key testing program flow chart


首先,我们要先进行按键检测,判断是否有按键闭合,如果没有说明没有按键键入,那么返回就是我们常说的消抖,重新进行按键检测,直到有按键闭合。接下来会有10ms的延迟,保存键值;再继续判断按键是否松开,如果是则又会产生10ms的延迟,否则返回判断直到按键有松开为止,最后返回键值。

我们需要编译一个模拟键盘定义data0-15,然后模拟输入给FPGA一个行信号,FPGA接收行信号,同时输出给模拟键盘一个列信号,如果输出的列信号不存在低电平,那么行信号为4‘b1111,代表输入的按键不在本列上,继续扫描下一列直到找到相应的行信号为止。部分代码如图所示。

图6-2 键盘扫描部分代码

Figure 6-2 keyboard scan code


图6-3为键盘扫描仿真图,当我们按下1时,数据显示1,按下10显示10,按下2显示的是2,按下15显示15,仿真结果有效,程序编译正确。


图6-3 键盘扫描仿真

Figure 6-3 keyboard scanning simulation

6.2.2  计算仿真举例

加法计算举例,首先pnumber输入1,data_in输入也为1,扫描结果为1;然后输入10;pnumber输入为2,data_in输入为2,扫描结果为2;最后按键“=”,显示结果即为3。仿真显示结果正确,说明我们的编译代码没有问题,计算有效,计算器结果可信。


图6-4 1+2=3程序仿真图
Figure 6-4 1+2=3 process simulation diagram

 

七、结论



本次电子计算器的设计是基于FPGA设计的,计算器基本上可以实现的加减乘除的功能。系统的计算部分、存储部分、显示部分和输入部分四个部分都可以完成设计要求,输入部分采用键盘矩阵原理,存储部分用状态机来实现,并进行了仿真。实现了防消抖的要求,计算结果较精确。达到了预期的要求目标。


附录:设计主体源代码



二进制转BCD代码:

module bin2bcd_12bit(bin, bcd);
input [19:0] bin; output reg [23:0] bcd; always @ (*) begin bcd[3:0] = bin%10; bcd[7:4] = bin/10%10; bcd[11:8] = bin/100%10; bcd [15:12] = bin/1000%10; bcd[19:16] = bin/10000%10; bcd[23:20] = bin/100000%10; end
endmodule


计算模块代码:

module calculator (clk, rst_n, flag, key_data, bin_data);
input clk; input rst_n; input flag; input [3:0] key_data; output reg [19:0] bin_data;
reg [1:0] state; reg [19:0] num1; reg [3:0] opcode; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin state <= 0; num1 <= 0; bin_data <= 0; opcode <= 0; end else begin case (state) 0 : begin if (flag) begin if (key_data < 10) begin bin_data <= bin_data * 10 + key_data; end else begin if (key_data == 14) begin state <= 0; end else begin opcode <= key_data; state <= 1; num1 <= bin_data; bin_data <= 0; end end end else begin state <= 0; end end 1 : begin if (flag) begin if (key_data < 10) begin bin_data <= bin_data * 10 + key_data; end else begin if (key_data == 14) begin case (opcode) 10 : begin bin_data <= num1 + bin_data; state <= 2; end 11 : begin bin_data <= num1 - bin_data; state <= 2; end 12 : begin bin_data <= num1 * bin_data; state <= 2; end 13 : begin bin_data <= num1 / bin_data; state <= 2; end default : bin_data <= 0; endcase end else begin state <= 1; end end end else begin state <= 1; end end 2 : begin if (flag) begin if (key_data < 10) begin bin_data <= {16'd0,key_data}; state <= 0; end else begin if (key_data == 14) begin state <= 2; end else begin num1 <= bin_data; opcode <= key_data; bin_data <= 0; state <=1; end end end else begin state <= 2; end end endcase end end

endmodule


输入部分代码:

module key_board (clk, rst_n, row, col, data, valid, clk_1k);
input clk; input rst_n; input [3:0] row; output reg [3:0] col; output reg [3:0] data; output reg valid; output reg clk_1k;
reg [14:0] cnt; parameter T1ms = 24999; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin clk_1k <= 1'b1; cnt <= 15'd0; end else begin if (cnt < T1ms) begin cnt <= cnt + 15'd1; end else begin cnt <= 15'd0; clk_1k <= ~clk_1k; end end end
reg [7:0] row_col; reg [1:0] state; reg [4:0] count; always @ (posedge clk_1k or negedge rst_n) begin if (!rst_n) begin col <= 4'b0000; row_col <= 8'd0; state <= 0; valid <= 0; count <= 0; end else begin case (state) 0 : begin if (row == 4'b1111) begin col <= 4'b0000; end else begin state <= 1; end end 1 : begin if (row == 4'b1111) begin state <= 0; count <= 0; end else begin if (count < 19) begin count <= count + 1; end else begin count <= 0; state <= 2; col <= 4'b0111; end end end 2 : begin if (row == 4'b1111) begin col <= {col[2:0],col[3]}; state <= 2; end else begin row_col <= {row,col}; state <= 3; valid <= 1; end end 3 : begin if (row == 4'b1111) begin state <= 0; valid <= 0; end else begin valid <= 0; state <= 3; end end default : state <= 0; endcase end end
always @ (*) begin case (row_col) 8'b0111_0111 : data = 4'hf; 8'b0111_1011 : data = 4'he; 8'b0111_1101 : data = 4'hd; 8'b0111_1110 : data = 4'hc; 8'b1011_0111 : data = 4'hb; 8'b1011_1011 : data = 4'ha; 8'b1011_1101 : data = 4'h9; 8'b1011_1110 : data = 4'h8; 8'b1101_0111 : data = 4'h7; 8'b1101_1011 : data = 4'h6; 8'b1101_1101 : data = 4'h5; 8'b1101_1110 : data = 4'h4; 8'b1110_0111 : data = 4'h3; 8'b1110_1011 : data = 4'h2; 8'b1110_1101 : data = 4'h1; 8'b1110_1110 : data = 4'h0; default : data = 4'h0; endcase end

endmodule


数码管顶层代码:

module seven_seg (clk, rst_n, data_in, sel, seg);
input clk; input rst_n; input [23:0] data_in; output [2:0] sel; output [7:0] seg;
wire clk_1k; freq freq_dut( .clk(clk), .rst_n(rst_n), .clk_1k(clk_1k) );
sel_seg_encode sel_seg_encode_dut( .clk(clk_1k), .rst_n(rst_n), .data_in(data_in), .sel(sel), .seg(seg) ); endmodule


分频部分代码:

module freq (clk, rst_n, clk_1k);
input clk; input rst_n; output reg clk_1k;
reg [14:0] cnt; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin clk_1k <= 1; cnt <= 0; end else begin if (cnt < 24_999) begin cnt <= cnt + 1; end else begin cnt <= 0; clk_1k <= ~clk_1k; end end end endmodule


位选段选连接代码:

module sel_seg_encode (clk, rst_n, data_in, sel, seg);    input clk;  input rst_n;  input [23:0] data_in;    output [2:0] sel;  output [7:0] seg;
wire [3:0] num; seg_encode seg_encode_dut( .rst_n(rst_n), .num(num), .seg(seg) ); sel_encode sel_encode_dut( .clk(clk), .rst_n(rst_n), .data_in(data_in), .num(num), .sel(sel) );

endmodule


段选部分代码:

module seg_encode (rst_n, num, seg);
input rst_n; input [3:0] num; output reg [7:0] seg;
always @ (*) begin if (!rst_n) begin seg = 8'b0000_0000; end else begin case (num) 0 : seg = 8'b1100_0000; 1 : seg = 8'b1111_1001; 2 : seg = 8'b1010_0100; 3 : seg = 8'b1011_0000; 4 : seg = 8'b1001_1001; 5 : seg = 8'b1001_0010; 6 : seg = 8'b1000_0010; 7 : seg = 8'b1111_1000; 8 : seg = 8'b1000_0000; 9 : seg = 8'b1001_0000; default : seg = 8'b0000_0000; endcase end end
endmodule


位选部分代码:

module sel_encode (clk, rst_n, data_in, num, sel);
input clk; input rst_n; input [23:0] data_in; output reg [3:0] num; output reg [2:0] sel;
always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin sel <= 0; end else begin if (sel < 5) begin sel <= sel + 1; end else begin sel <= 0; end end end
always @ (*) begin if (!rst_n) begin num = 0; end else begin case (sel) 0 : num = data_in[23:20]; 1 : num = data_in[19:16]; 2 : num = data_in[15:12]; 3 : num = data_in[11:8]; 4 : num = data_in[7:4]; 5 : num = data_in[3:0]; default : num = 0; endcase end end
endmodule

EETOP EETOP半导体社区-国内知名的半导体行业媒体、半导体论坛、IC论坛、集成电路论坛、电子工程师博客、工程师BBS。
评论
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 48浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 89浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 122浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 97浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 181浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 171浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 111浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 94浏览
  • 车身域是指负责管理和控制汽车车身相关功能的一个功能域,在汽车域控系统中起着至关重要的作用。它涵盖了车门、车窗、车灯、雨刮器等各种与车身相关的功能模块。与汽车电子电气架构升级相一致,车身域发展亦可以划分为三个阶段,功能集成愈加丰富:第一阶段为分布式架构:对应BCM车身控制模块,包含灯光、雨刮、门窗等传统车身控制功能。第二阶段为域集中架构:对应BDC/CEM域控制器,在BCM基础上集成网关、PEPS等。第三阶段为SOA理念下的中央集中架构:VIU/ZCU区域控制器,在BDC/CEM基础上集成VCU、
    北汇信息 2025-01-03 16:01 193浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 166浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 72浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 71浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 79浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 85浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦