2024年寒假练 - 基于小脚丫FPGA套件STEP BaseBoard V4.0实现一个两位十进制数加、减、乘、除运算的计算器
该项目使用了Vscode,lattice diamond软件,verilog语言,实现了一个小脚丫FPGA套件STEP BaseBoard V4.0的设计,它的主要功能为:实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制。
标签
FPGA
显示
木木木
更新2024-04-01
北京理工大学
94

2024年寒假练

——基于小脚丫FPGA套件实现两位十进制数加减乘除的计算器

 

一、项目介绍

1.1项目功能介绍

本项目要求基于小脚丫FPGA套件实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,4×4键盘按键分配如下图所示。

运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。

在此基础上本项目支持负数计算,不支持小数运算。可以继续对上一次计算结果进行计算,或者直接按下数字键开始新的一轮计算。

1.2需求分析和设计实现思路

计算器整体设计分为三个大部分:输入和解码,储存和计算,显示。

输入和解码:采用4*4矩阵按键输入,利用状态机扫描获得按下时下降沿信号key_pulse[15:0];对key_pluse[15:0]进行解码,读出所键入数据seg_data[3:0],生成代表数据键入的flag信号并输出;

储存和计算:用四个状态来存储两个操作数num1[7:0], num2[7:0],操作符[3:0],计算结果result[15:0]并用data[31:0]输出;

(此处数据宽度设置考虑到题设为两位十进制数,故最大为99,二进制为8’h63,所以num1,num2设置位宽为8,计算最大值为99*99=9801,二进制为16’h2649,所以result设置位宽为16。最后输出数据data位宽为32是为了匹配八位数码管的八位数据,虽然目前计算结果最大只显示四位,但方便了后续进行其他项显示。)

显示:显示功能由三个模块实现分别是数据处理,数据译码和数码管驱动。其中数据处理是将传入的data[31:0]由二进制处理成需要显示的BCD码和负号,并输出片选信号;数据译码是将BCD码转成七段数码管点亮的八位码;数码管驱动为生成驱动m74HC595的时钟信号,并串行输出片选信号和七段数码管点亮八位码,从而驱动数码管进行扫描显示,利用视觉残留显示数据。


1.3功能框图

1.4硬件框图

1.5软件流程图


输入解码部分:

储存计算部分:



1.6资源占用报告

Design Summary:

Number of registers: 213 out of 4635 (5%)

PFU registers: 213 out of 4320 (5%)

PIO registers: 0 out of 315 (0%)

Number of SLICEs: 1025 out of 2160 (47%)

SLICEs as Logic/ROM: 1025 out of 2160 (47%)

SLICEs as RAM: 0 out of 1620 (0%)

SLICEs as Carry: 505 out of 2160 (23%)

Number of LUT4s: 2049 out of 4320 (47%)

Number used as logic LUTs: 1039

Number used as distributed RAM: 0

Number used as ripple logic: 1010

Number used as shift registers: 0

Number of PIO sites used: 13 + 4(JTAG) out of 105 (16%)

Number of block RAMs: 0 out of 10 (0%)

Number of GSRs: 1 out of 1 (100%)
二、简单的硬件介绍


在本项目中用到了矩阵键盘,数码管和核心板上的N14作为复位键



其中矩阵键盘利用row[3:0]输出,通过状态机扫描方式检测col[3:0]输入从而检测按键。

 

数码管是利用两个级联的74MC5950芯片通过串行输入并行输出来实现三个输入控制十六个输出。其中595_DIN为串行数据输入,595_SCK是时钟信号输入,595_RCK是控制十六位信号读取的时钟信号。

 


下面为管脚约束

 

 


三、实现的功能及图片展示

初始状态:

加法:样例为输入11+22。如图,结果为33,正确。


减法:继续上结果输入-48。如图,结果为-15,正确。


乘法:继续输入*4


如图,结果为-60,正确。

 

除法:继续输入/12


如图,结果为-5,正确。

 

虽然无法直接输入负数,但是可以通过输入“0-X=” 再继续输入计算符的方式使负数加入计算,总体实现了两位十进制的加减乘除及符号运算。

 

 

四、主要代码片段及说明

4.1输入部分

(1)矩阵键盘扫描状态机

always@(posedge clk_200hz or negedge rst_n) begin
    if(!rst_n) begin
        c_state <= STATE0;
        row <= 4'b1110;
    end else begin
        case(c_state)
            //状态c_state跳转及对应状态下矩阵按键的row输出
            STATE0: begin
                c_state <= STATE1;
                row <= 4'b1101;
            end
            STATE1: begin
                c_state <= STATE2;
                row <= 4'b1011;
            end
            STATE2: begin
                c_state <= STATE3;
                row <= 4'b0111;
            end
            STATE3: begin
                c_state <= STATE0;
                row <= 4'b1110;
            end
            default:begin c_state <= STATE0; row <= 4'b1110;key_out = 16'hffff; end
        endcase
    end
end

 

四个状态输出四种ROW进行扫描

always@(negedge clk_200hz or negedge rst_n) begin
    if(!rst_n) begin
        key_out <= 16'hffff;
        key <= 16'hffff;
    end else begin
        case(c_state)
            STATE0:begin
                //key_out[3:0] = col;       //采集当前状态的列数据赋值给对应的寄存器位
                key[3:0] <= col;  //矩阵键盘采样
                key_r[3:0] <= key[3:0];    //键盘数据锁存
                key_out[3:0] <= key_r[3:0]|key[3:0];   //连续两次采样判定
            end
            STATE1:begin
                //key_out[7:4] = col;
                key[7:4] <= col;    //矩阵键盘采样
                key_r[7:4] <= key[7:4];    //键盘数据锁存
                key_out[7:4] <= key_r[7:4]|key[7:4];   //连续两次采样判定
            end
            STATE2:begin
                //key_out[11:8] = col;
                key[11:8] <= col;    //矩阵键盘采样
                key_r[11:8] <= key[11:8];    //键盘数据锁存
                key_out[11:8] <= key_r[11:8]|key[11:8];   //连续两次采样判定
            end
            STATE3:begin
                //key_out[15:12] = col;
                key[15:12] <= col;    //矩阵键盘采样
                key_r[15:12] <= key[15:12];    //键盘数据锁存
                key_out[15:12] <= key_r[15:12]|key[15:12];   //连续两次采样判定
            end
            default:key_out = 16'hffff;
        endcase
    end
end

 

 

 

每个状态读取对应行的信息,当col为低时被按下。此处讲前一个时钟周期的key进行暂存后按位取或,进行防抖处理。

(2)译码

 

case(key_pulse)  //key_pulse脉宽等于clk_in的周期
        //编码
            16'h0001: begin seg_data <= 4'h0D; flag<=1'b1;end //%
            16'h0002: begin seg_data <= 4'h0E; flag<=1'b1;end //=
            16'h0004: begin seg_data <= 4'h0F; flag<=1'b1;end//begin seg_data = seg_data;seven_segement_led_dot=1'b1;end//= 4'h0F;  //.//seven_segement_led_dot=1'b1
            16'h0008: begin seg_data <= 4'h00; flag<=1'b1;end //0
            16'h0010: begin seg_data <= 4'h0C; flag<=1'b1;end//*
            16'h0020: begin seg_data <= 4'h01; flag<=1'b1;end //1
            16'h0040: begin seg_data <= 4'h02; flag<=1'b1;end //2
            16'h0080: begin seg_data <= 4'h03; flag<=1'b1;end //3
            16'h0100: begin seg_data <= 4'h0B; flag<=1'b1;end //-
            16'h0200: begin seg_data <= 4'h06; flag<=1'b1;end //6
            16'h0400: begin seg_data <= 4'h05; flag<=1'b1;end //5
            16'h0800: begin seg_data <= 4'h04; flag<=1'b1;end //4
            16'h1000: begin seg_data <= 4'h0A; flag<=1'b1;end //+
            16'h2000: begin seg_data <= 4'h09; flag<=1'b1;end //9
            16'h4000: begin seg_data <= 4'h08; flag<=1'b1;end //8
            16'h8000: begin seg_data <= 4'h07; flag<=1'b1;end //7
            default:  begin seg_data <= seg_data; flag<=1'b0;end  //无按键按下时保持
        endcase

 

key_pulse[15:0]所代表的被按下按键进行译码。

4.2存储和计算部分


(1)总览:

计算部分为有四个状态的状态机。

(2)储存

STATE0: begin 
                if(flag==1'b1)begin
                    if(seg_data <10) begin//不可出现链续判断,例如9<deg_data<14
                        //if(num1!=0 ||seg_data!= 0)begin data<={data[27:0],seg_data};end//向左覆盖
                        num1<=num1*10+seg_data;
                        data<=data*10+seg_data;
                        c_state<=STATE0;
                    end
                    else begin
                        if(seg_data==14) begin//等号误操,不变
                            c_state<=STATE0;
                        end      
                        else begin//计算操作符,data不变,仍为num1
                            opcpde<=seg_data;
                            c_state<=STATE1;
                        end
                    end
                end
                else begin c_state<=STATE0;end
            end

例如STATE1里的num1opcode暂无操作时进行及时储存。

(3)计算

STATE1: begin 
                if(flag==1'b1)begin
                    if(seg_data<10) begin
                        //if(num1!=0 ||seg_data!= 0)begin data<={data[27:0],seg_data};end//向左覆盖
                        data<=num2*10+seg_data;
                        num2<=num2*10+seg_data;
                        c_state<=STATE1;
                    end
                    else if(seg_data==14) begin//等号
                        case(opcpde)
                        4'b1010:begin result<=num1+num2; end
                        4'b1011:begin result<=num1-num2; end
                        4'b1100:begin result<=num1*num2; end
                        4'b1101:begin result<=num1/num2; end
                        endcase
                        c_state<=STATE2;
                    end
                    else begin
                        c_state<=STATE1;
                    end
                end
                else if(!flag) begin//不按
                    c_state<=STATE1;
                end
            end

4.3显示部分

(1)数据处理

assign disp_buma = disp_data[15]? ({1'b0,~disp_data[14:0]}+1'b1):disp_data;
    assign fuhao = disp_data[15]? 1'b1 : 1'b0;

    assign data0 = disp_buma % 4'd10;                  //个位数
    assign data1 = disp_buma / 4'd10      % 4'd10;     //十位数
    assign data2 = disp_buma / 7'd100     % 4'd10;     //百位数
    assign data3 = disp_buma / 10'd1000   % 4'd10;     //千位数

对传入的数据进行二进制转BCD码,并识别是否有符号,进行补码转换。


(2)译码

case(seven_segement_led_dot)
            1'b0:
                case(key_in[3:0])
                4'b0000:temp = 8'b11111100;//0,abcdef
                4'b0001:temp = 8'b01100000;//1,bc
                4'b0010:temp = 8'b11011010;//2,abged
                4'b0011:temp = 8'b11110010;//3,abcdg
                4'b0100:temp = 8'b01100110;//4,bcfg
                4'b0101:temp = 8'b10110110;//5,acdfg
                4'b0110:temp = 8'b10111110;//6,acdefg
                4'b0111:temp = 8'b11100000;//7,abc
                4'b1000:temp = 8'b11111110;//8
                4'b1001:temp = 8'b11110110;//9,abcdfg
                4'b1010:temp = 8'b01100010;//A,bcg
                4'b1011:temp = 8'b00000010;//B,g
                4'b1100:temp = 8'b01101110;//C,bcefg
                4'b1101:temp = 8'b10010010;//D,agd
                4'b1110:temp = 8'b10010000;//E,cg
                default: temp = 8'b0;
                endcase
            1'b1:
                case(key_in[3:0])
                    4'b0000:temp = 8'b11111101;//0,abcdef
                    4'b0001:temp = 8'b01100001;//1,bc
                    4'b0010:temp = 8'b11011011;//2,abged
                    4'b0011:temp = 8'b11110011;//3,abcdg
                    4'b0100:temp = 8'b01100111;//4,bcfg
                    4'b0101:temp = 8'b10110111;//5,acdfg
                    4'b0110:temp = 8'b10111111;//6,acdefg
                    4'b0111:temp = 8'b11100001;//7,abc
                    4'b1000:temp = 8'b11111111;//8
                    4'b1001:temp = 8'b11110111;//9,abcdfg
                    default: temp = 8'b0000_0000;
                endcase
        endcase

设有显示小数点功能,对应八段数码管最后一位。


(3)驱动时钟

case(SHCP_EDGE_CNT)
                5'd0: begin SH_CP <= 1'b0; ST_CP <= 1'b1; DS <= r_data[15]; end
                5'd1: begin SH_CP <= 1'b1; ST_CP <= 1'b0;end
                5'd2: begin SH_CP <= 1'b0; DS <= r_data[14];end
                5'd3: begin SH_CP <= 1'b1; end
                5'd4: begin SH_CP <= 1'b0; DS <= r_data[13];end
                5'd5: begin SH_CP <= 1'b1; end
                5'd6: begin SH_CP <= 1'b0; DS <= r_data[12];end
                5'd7: begin SH_CP <= 1'b1; end
                5'd8: begin SH_CP <= 1'b0; DS <= r_data[11];end
                5'd9: begin SH_CP <= 1'b1; end
                5'd10:begin SH_CP <= 1'b0; DS <= r_data[10];end
                5'd11:begin SH_CP <= 1'b1; end
                5'd12:begin SH_CP <= 1'b0; DS <= r_data[9];end
                5'd13:begin SH_CP <= 1'b1; end
                5'd14:begin SH_CP <= 1'b0; DS <= r_data[8];end
                5'd15:begin SH_CP <= 1'b1; end
                5'd16:begin SH_CP <= 1'b0; DS <= r_data[7];end
                5'd17:begin SH_CP <= 1'b1; end
                5'd18:begin SH_CP <= 1'b0; DS <= r_data[6];end
                5'd19:begin SH_CP <= 1'b1; end
                5'd20:begin SH_CP <= 1'b0; DS <= r_data[5];end
                5'd21:begin SH_CP <= 1'b1; end
                5'd22:begin SH_CP <= 1'b0; DS <= r_data[4];end
                5'd23:begin SH_CP <= 1'b1; end
                5'd24:begin SH_CP <= 1'b0; DS <= r_data[3];end
                5'd25:begin SH_CP <= 1'b1; end
                5'd26:begin SH_CP <= 1'b0; DS <= r_data[2];end
                5'd27:begin SH_CP <= 1'b1; end
                5'd28:begin SH_CP <= 1'b0; DS <= r_data[1];end
                5'd29:begin SH_CP <= 1'b1; end
                5'd30:begin SH_CP <= 1'b0; DS <= r_data[0];end
                5'd31:begin SH_CP <= 1'b1; end
                default:begin SH_CP <= 1'b0;ST_CP <= 1'b0;DS <= 1'b0;   end
            endcase

串行传入点亮数码管的信号和片选信号

 

五、仿真波形图


(1)ArrayKeyBoard.v

col1111变成1110后,row变为1101,则key_out变为4’hffef,即第三排的第三个按键被按下。


(2)Decoder.v

key_pulse[15:0]均被译为对应的seg_data[3:0]


(3)FSM.v

如图,num1=23num2=45,操作符为4’hb,即减,结果为-22,下一个时钟后,data也会被翻译为-22.


(4)scan.v

可以看到对disp_data[31:0]每一位的显示由seven_segment_led[7:0]seven_segment_led_sel[7:0]控制,例如当disp_data=32’b0000fffe时,最后一段是8’h5b,倒数第二段是8’h40,4’b1110对应的led信号。

(5)m74hc595.v


如图,ST_CP的时钟周期为 SH_CP16倍,且脉冲宽度相同,方向相反。

 

附件下载
Caculater_two.zip
团队介绍
菜菜又爱爱
团队成员
木木木
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号