写在前面的话

我大学本科学的是测控专业,2012年考取首都师范大学物理系研究生。我从未学习过数字电路设计,对FPGA和Verilog语言没有任何概念,更没有设计数字电路系统的基础和经验,也从未自己动手装配和完成过一台能实际运行的电子系统。但我从小就对电子设计有浓厚的兴趣。为什么小小的计算器按几下就能完成非常复杂的数学计算,一直困惑着我,激起我年轻的好奇心。大学四年里,虽然学习过“数字电路”和“模拟电路”课程,考试成绩也很不错,但对我而言,计算器是如何设计的,仍旧是一头雾水。

听同学们说,如果掌握了FPGA设计,这个谜就能找到答案。我用关键字“FPGA培训”在百度搜索,发现一个公司正在开设FPGA就业培训(100天)班,也知道这个班由北京航空航天大学的夏宇闻教授亲自讲授和管理。于是下定决心抽出3个月时间,认真学习一下FPGA。经过100天的学习和练习,我初步掌握了如何用FPGA芯片设计和搭建复杂数字系统。现在我有充分的信心,只要设计需求明确,我完全有能力独立设计并完成一个较复杂的数字系统,并能可靠地完成预先设定的数据 处理任务。这个阶段的学习给了我很多启发,也增强了我的信心,很想把自己的感受和学习心得编写成小册子与大家分享。我的想法得到夏宇闻教授的支持。于是我把学习期间的心路历程和学到的知识、经验略加整理,以日记的形式写出来,与大家分享,希望能给打算学习Verilog和FPGA设计的初学者一些帮助和启发,起到抛砖引玉的作用。

本书使用的硬件为至芯科技的四代开发板、Altera CycloneⅣ的芯片,软件为QuartusⅡ13.0 sp1。

——作者

1

设计需求讲解

今天的设计任务有两项:1)完成计算器中负责二进制四则运算的算术逻辑模块(即alu0)。2)改写计算控制状态机模块(即key2bcd1)。算术逻辑模块是计算机设计中最后一个小模块。它运行所需的准备工作几乎都是由以前调试通过的模块完成的。操作数a和b,以及操作符opcode都可以从键盘输入,并转换为二进制数存入寄存器A、B。算术逻辑模块 alu0只需做相应的算术运算即可。

2

设计工具使用讲解

我们需要新建一个文件来作为计算模块。

选择File–New–Verilog HDL File,如图5-1所示,然后点击下面的OK按钮。

图5-1 新建文件

1、计算模块的可综合代码

计算模块的可综合代码如下:

module alu0(a, b, clk, rst_n, opcode, bin_data);

    input [23:0] a,b;    input clk;    input rst_n;    input [3:0] opcode;

    output reg [24:0] bin_data;

    always @ (posedge clk or negedge rst_n)    begin        if(!rst_n)        begin            bin_data <= 0;        end        else        begin            case(opcode)                10: begin bin_data <= a + b; end                  11: begin bin_data <= a - b; end                  12: begin bin_data <= a * b; end                  13: begin bin_data <= a / b; end             endcase        end    end

endmodule

经过这些天的练习,写起这段代码来一定很轻松了。我们只需要判断opcode的值来运行不同的运算即可。

2、计算模块的测试

计算模块的测试代码如下:

`timescale 1ns/1nsmodule alu_tb0;

    reg [23:0] a,b;    reg clk;    reg rst_n;    reg [3:0] opcode;

    wire [23:0] bin_data;

    alu0 u1(.a(a),.opcode(opcode),.b(b),.clk(clk),.rst_n(rst_n),.bin_data(bin_data));

    initial     begin        a = 0;b = 0;rst_n = 0; clk = 1; opcode = 0; //复位,初始化

        #101 rst_n = 1; //复位结束

        #1000 a = 11;       //11 + 2        opcode = 10;         b = 2;

        #1000 a = 13;       //13 - 3        opcode = 11;         b = 3;

        #1000 a = 4;              //4 × 5        opcode = 12;         b = 5;

        #1000 a = 10;       //10 ÷ 2        opcode = 13;         b = 2;

        #1000        $stop;

    end

    always #10 clk = ~clk;

endmodule

因为模块比较简单,逻辑不易出错,所以简单地做一遍测试即可。写好之后将alu.v和alu_tb.v加入到仿真文件中,如图5-2所示。

设置完毕后启动RTL仿真。

图5-2 新建仿真设置

3、转到ModelSim仿真工具进行仿真

将计算模块的测试代码转到ModelSim仿真工具进行仿真,得到的仿真波形如图5-3所示。观察时可以将所有的变量格式设置为unsigned,方便观察。

图5-3 仿真结果

复位后的第一组运算,a为11,b为2,opcode为10(加法),结果bin_data输出13,没有问题。同样,13-3=10,4×5=20,10÷2=5均运算正确。

4、模块连接关系

目前我们的顶层连接还是以前做的如图5-4所示这种形式,后来编写的BCD码与二进制数之间的转换模块以及计算模块还都没有添加进去,所以还需要修改顶层代码。

图5-4 模块连接图

前面曾经讨论过,由于计算模块需要二进制数,而其它模块均以BCD码传输,所以计算模块alu前后必须紧跟BCD转二进制,以及二进制转BCD。所以把这3个模块接在按键转BCD码key2bcd后面最合适不过了。而值得考虑的是,计算完的结果输出到哪里。这里有两种选择,一种是输出到显示模块display,另一种是输出到按键转BCD的模块key2bcd。一般情况下我们会先想到要直接输出到显示模块,这样计算的结果就直接显示出来了。但是这样就又牵扯到一个问题,那就是显示模块只能输出一个数据,也就是说,只能输出A,B,或者结果,到底要显示哪一个数,显示模块需要做出判断。而我们之前已经在显示模块里加了判断A或者B的代码,不如就在这个基础上进行改造,将输出结果赋值给A或者B,这样显示模块就可以不用改动,继续判断A,B进行输出即可。而这一做法还有另外一个好处,就是为了改进计算器实现连续运算提供了方便。当我们想实现连续运算A+B+C+D……的时候,我们不可能会设置N多个变量来保存这些操作数,可以将每一步的运算结果赋值回A,这样就可以简化为用A一直在与另外一个数B进行运算,只需要两个操作数。关于连续运算以后再进行讨论。下面先修改key2bcd中的代码。

module key2bcd1 (clk,                  rst_n,                  real_number,                  opcode,                  BCDa,                  BCDb,                  result ); //端口加上计算结果result

    input [4:0] real_number;    input rst_n,clk;    input [23:0] result;    //result定义为输入

    output reg [23:0] BCDa,BCDb;    output reg [3:0] opcode;

    reg [3:0] opcode_reg;    reg [3:0] state;    reg datacoming_state,datacoming_flag;

    always @(posedge clk)       if (!rst_n)         begin        datacoming_state <=0;        datacoming_flag <=0;         endelseif (real_number!=17)    case(datacoming_state)            0: begin                            datacoming_flag <=1;            datacoming_state <=1;             end            1: begin            datacoming_flag  <=0;            datacoming_state <=1;              end        endcase         else        begin           datacoming_state <= 0;           datacoming_flag <= 0;        end

    always @ (posedge clk or negedge rst_n)    beginif(!rst_n)        begin            BCDa <= 0;            BCDb <= 0;            state <= 0;            opcode <= 0;        end elseif(datacoming_flag)        begincase(state)           0:   case(real_number)             0,1,2,3,4,5,6,7,8,9:            begin             BCDa[23:0] <= {BCDa[19:0],real_number[3:0]};                 state <= 0;             end             10,11,12,13:                 begin                 opcode_reg <= real_number[3:0];                     state <= 1;              end                default:state <= 0;             endcase

         1: case(real_number)             0,1,2,3,4,5,6,7,8,9:             begin               opcode <= opcode_reg;                 BCDb[23:0] <= {BCDb[19:0],real_number[3:0]};                 state <= 1;             end             14: begin                  BCDa <= result;   //在按完等号之后将结果赋值给A                  BCDb <= 0;     //同时清空B的值,数码管就可以显示A                  opcode <= 0;                  state <= 2;                 end             default:state <= 1;           endcase

//加上一个状态,表示上一次运算结束后,重新输入数值A时,不受到运算结果的影响          2: case(real_number)            0,1,2,3,4,5,6,7,8,9:              begin             BCDa <= real_number;             state <= 0;  //输入完一位数后,状态跳回0,便又可以移位显示              end           10,11,12,13:              begin             opcode_reg <= real_number[3:0];                      state <= 1;             end             default:state <= 2;          endcase            default : state <= 0;          endcase        end    endendmodule

同时,顶层模块的连接关系也就产生了,如图5-5所示。

图5-5 模块连接图

按照这个连接关系把可综合顶层修改如下:

module calc2(clk,rst_n,seg,sel,keyin,keyscan);

    input clk, rst_n;    input [3:0] keyin;

    output [3:0] keyscan;    output [2:0] sel;    output [7:0] seg;

    wire clk_slow;    wire [4:0] real_number;    wire [23:0] BCDa,BCDb,a,b,bin_data,result;    wire [3:0] opcode;

    display3 u1( .clk(clk),                 .adata(BCDa),                 .bdata(BCDb),                 .rst_n(rst_n),                 .sel(sel),                 .seg(seg),                 .clk_out(clk_slow) );

    keyscan0 u2(                  .clk(clk_slow),                .rst_n(rst_n),                .keyscan(keyscan),                .keyin(keyin),                .real_number(real_number)              );

    key2bcd1 u3(                  .clk(clk_slow),                  .real_number(real_number),                  .opcode(opcode),                  .rst_n(rst_n),                  .BCDa(BCDa),                  .BCDb(BCDb),                  .result(result)                 );

    bcd2bin0 u4(                 .BCDa(BCDa),                 .BCDb(BCDb),                 .a(a),                 .b(b)                    );

    bin2bcd0 u5(                .bin(bin_data),                .bcd(result)                );

    alu0 u6(                .a(a),                .b(b),                .clk(clk_slow),                .rst_n(rst_n),                .opcode(opcode),                .bin_data(bin_data)              );

endmodule

写好之后将calc2模块设置为顶层,然后进行分析综合。通过之后可以单击RTL Viewer看一下综合后各个模块的连接关系,是否和之前设想的一样,如图5-6所示,在Analysis & Synthesis下找到RTL Viewer,双击即可。

图5-6 打开RTL视图

打开之后会弹出一个窗口如图5-7所示窗口,里面绘制了各个模块之间的连线和输入输出端口。

图5-7 RTL模型图

可以看出,程序综合出了我们想要的模块连接,接下来可以进行全编译,然后将代码下载到开发板里了。

5、下载程序到开发板进行调试

由于之前已经将管脚分配好了,所以等待编译成功之后,打开Programmer直接下载程序即可。下载成功后,初始状态数码管显示6个0,顺序输入数字、操作符、数字、等号,即可得到正确的结果,继续按下数字或者操作符,可以进行下一次运算。

3

今天工作总结

经过5天的努力,终于完成了可以实现基本功能的6位计算器了,赶快享受一下劳动成果吧!

今天通过上机仿真,不断地代码修改,逐步掌握了电路构造的思想,理解了模块划分,逐块调试和整合调试的概念。回顾一下今天所做的工作和学到的内容总结如下:

1)计算模块及其测试代码的编写。

2)多个模块之间数据和控制连接的思路。

3)通过观察RTL Viewer学会如何查看综合生成的逻辑电路。

4)   修改了key2bcd模块,增加了一个运算结果输入接口。

5)通过result接口,可将运算结果result从binary2bcd模块输出至key2bcd模块中的BCDa寄存器,直接输出display模块显示。

到此为止,我已达到了夏老师提出的最基本要求。从明天开始,要对这个计算器进行改进,包括占用逻辑资源的优化、运算功能的添加、显示效果的美化(消去有效数字前面的0)、连续运算等。

4

夏老师评述

赵然同学今天完成的工作非常出色,计算器四则运算的功能基本实现了。当然在资源消耗方面尚未做深入思考。他之所以能顺利地完成今天的工作,跟他前四天的工作基础是不能分割的。今天通过综合和布局布线后下载的模块calc2中,大部分是前几天经过测试成熟的模块。键盘扫描分析模块keyscan0不用做任何修改,显示模块display3、数制转换模块bcd2bin0和bin2bcd0也不用做任何修改,计算模块alu0与教材上的例子相似;计算控制状态机key2bcd1是在第三天编写的key2bcd0基础上修改的。该模块是赵然同学在彻底理解了老师课堂上介绍的gewshiwbaiw模块原理的基础上,自己动手编写的。key2bcd1模块思路巧妙,可以实现连续计算操作。该计算控制状态机的成功是赵然同学今天工作顺利的主要原因。首先他把数字0~9的的输入和四则运算操作符号的输入分别用不同的状态记录,他还把等号的输入与四则运算操作符、0~9数字的输入区分开,用不同状态记录,以此来管理参与运算数据的位数确定、中间结果的寄存和最后运算结果的确认。最后他还把计算结果输入到该状态机中的BCDa寄存器,使得显示模块不必做任何修改,而且还能实现连续计算,一箭双雕。他之所以能写出这个状态机,与他勤于思考,较深刻地理解状态的含义和状态在控制中的作用有关,也跟他敢于实践,勇于尝试新的途径 、不怕失败、相信自己有关。

  下期预告:

第6天——可进行连续运算的状态机改进

往期回顾

10天学会四则运算小计算器设计之第4天

姿势已摆好

就等你点啦

你们的“在看”是小编连载的动力喔 ?

用yacc编写的算术运算计算器_10天学会四则运算小计算器设计之第5天相关推荐

  1. 用yacc编写的算术运算计算器_如何用纯机械实现乘除运算,这是个问题

    在步进计算器诞生之后的两百多年中,机械计算之曲始终在莱布尼茨定好的基调上演奏.不难发现,两百年中的制造工艺在不断进步,机器的可靠性也不断提高,而计算原理却始终没有改进.尽管各路"莱系&quo ...

  2. 用yacc编写的算术运算计算器_详细的mac计算器操作技巧+快捷键分享

    我们的mac自带的计算器并不只可以应用于简单的计算,还有很多强大的实用功能你知道吗?今天小编就来带你解锁这些计算器的新功能.并有快捷键奉上~ 使用"计算器"执行基本计算.高级计算或 ...

  3. 用yacc编写的算术运算计算器_Linux里隐藏的计算器,你知道它的奥秘吗?

    大家都知道,windows下有个计算器工具,我们在工作生活中经常使用到它.但是,你可知Linux下也同样有个计算器吗? 当然,良许说的是命令行下的计算器工具,而不是界面型的计算器.良许是Linux应用 ...

  4. 用python编写猜成语游戏_10分钟学会用python写游戏!Python其实很简单!

    安装pygame 本人电脑是windows 10.python3.6,pygame下载地址:https://pypi.python.org/pypi/Pygame/1.9.3 请自行下载对应pytho ...

  5. android计算器求余,我的小计算器快完成了,就差一个取余和幂的运算了,下午搞定它...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 package llll; import java.awt.event.ActionEvent; import java.awt.event.Action ...

  6. python 计算器 tkinter_python -Tkinter 实现一个小计算器功能

    #-*- coding:utf-8 -*- from tkinter import * importtkMessageBoxdefget_Tk(): top=Tk()returntop#定义求总和函数 ...

  7. 财务工具 - 编写一个等额本息反推利率的计算器

    文章目录 财务工具 - 编写一个等额本息反推利率的计算器 1.算法实现 2.HTML 完整代码 3.运行效果 4.计算效果 财务工具 - 编写一个等额本息反推利率的计算器 1.算法实现 java 版本 ...

  8. 使用jquery制作计算器_如何使用jQuery对计算器进行编程

    使用jquery制作计算器 Previously, I showed you how to use CSS border-radius property to create the following ...

  9. c语言编程交互式计算器,C语言程序设计交互式函数计算器设计报告.docx

    程序设计小学期实验报吿 题目三 交互式函数计算器 课题名称:交互式函数计算器 一.课程需求及现实意义 课程现实意义 在学习生活中,常常会遇到一些复杂的数值运算,这时候,为了保证计算的准确,我们 就必须 ...

最新文章

  1. Eclipse 设置
  2. jsp oracle 环境配置文件,jsp + tomcat 连接Oracle数据实例
  3. 《产品之光》作者Kevin:对于小程序我有话要说
  4. 【MM 模块】 Optimized Purchasing 优化采购 2
  5. react 逆地理 高德地图_高德地图又出逆天黑科技!全国各大城市模型直接获取...
  6. gRPC学习记录(四)--官方Demo
  7. 网易智慧企业2020年度见面会4大亮点抢先看!
  8. NPoco for MySQL 配置
  9. python 网盘上传_python学习笔记 day32 实现网盘上传下载功能
  10. android 圆环温度控件,android 圆环倒计时控件
  11. py2.7+pyqt4开发端口检测工具
  12. VMware发布面向未来的员工工作解决方案,满足分散办公需求
  13. SpringBoot2 整合 AXIS2 服务端和客户端
  14. Java 重载和重写
  15. mysql emoji表情_mysql utf8mb4与emoji表情
  16. 一、大话HTTP协议-HTTP的前世今生
  17. 运行python.exe文件出现AttributeError: module ‘moviepy.audio.fx.all‘ has no attribute ‘audio_fadein‘
  18. 2022年烷基化工艺模拟考试题及烷基化工艺模拟考试题库
  19. Google APIs 学习/使用
  20. javaweb-一个投票网页

热门文章

  1. latex 基本用法(二)—— 矩阵(增广矩阵、长虚线)
  2. java数据库技术_JAVA数据库技术
  3. 同软件多个线程设置不同ip_IP数量不够该如何解决,快试试掘金网ip代理
  4. python从入门到精通需要多久-python从入门到精通需要多久?你需要先明白这两个点...
  5. python画简便的图-python中简单易学的绘图:用turtle画太极图
  6. python编程入门经典百度云-python电子书学习资料打包分享百度云资源下载
  7. 浅谈智能语音交互,看一个Windows语音识别程序
  8. 第E题 转换任意进制 (java方法直接解)==输入一个十进制数N,将它转换成R进制数输出
  9. 网卡重启影响nfs吗_nfs常见问题解决办法
  10. mysql create database 语法_MySQL中CREATE DATABASE语法总结