2024年寒假练-基于小脚丫FPGA套件实现计算器
该项目使用了小脚丫FPGA套件,实现了计算器的设计,它的主要功能为:实现两位十进制加、减、乘、除计算器,并用数码管和LCD显示。
标签
FPGA
Rabbid
更新2024-03-29
504

一、 项目功能介绍

本项目通过小脚丫FPGA套件STEP BaseBoard V4.0实现两位十进制加、减、乘、除计算器。其中,显示方式通过尺寸为320*240的LCD屏幕和八个数码管进行显示。LCD屏可以显示整个运算过程,数码管从左往右的前两位显示第一位数,第三第四位显示第二位数,第五到第八位显示运算结果。其中,加法、减法、乘法都可以全精度显示,对于除法只能显示四位有效数字,并且除法会对最后一位进行四舍五入处理。

二、 设计思路

本项目要求实现两位十进制加、减、乘、除计算器,从输入输出的角度设计,需要能够输入数字和运算符号的按键和能够显示输入数字和运算结果的显示器件。本项目使用了小脚丫FPGA套件STEP BaseBoard V4.0上板载的矩阵键盘、八个数码管、320*240的LCD屏幕。矩阵键盘通过4个输入引脚和4个输出引脚来实现按键的消抖以及对应数字的输入;八个数码管通过两个74HC595芯片进行动态扫描,利用人眼的视觉暂留来实现对应数字的显示;320*240的LCD屏幕则通过符合ST7789数据手册的SPI通信方式进行显示相关数字、运算符。

三、 硬件框图

2e75946507e658c671979544301ddba.jpg

 上图为硬件框图,从图中可以看出,先通过矩阵键盘输入数字和运算符,经过FPGA的处理,将显示数据传送给数码管和LCD屏幕。

四、 软件流程图

762b93de1df176a2da825106134f61d.png

上图为硬件框图,从图中可以看出,FPGA上电之后先对LCD进行了初始化,只有在LCD初始化完成之后才能进行矩阵键盘的输入。在输入完成之后,FPGA会对相关运算进行处理,处理完成之后,将显示数据传输给数码管和LCD屏,传输完成之后,程序结束。

五、FPGA资源占用报

e429754830ac5cbfc605b4306822b34.png

此图为FPGA硬件占用资源的报告

六、简单的硬件介绍

本项目的硬件框图在下面进行展示

80d3fc0842b8bda28e447dc24c40810.png

上图为4×4矩阵按键的硬件电路图,可以看到4根行线(ROW1、ROW2、ROW3、ROW4)和4根列线(COL1、COL2、COL3、COL4),同时列线通过上拉电阻连接到VCC电压(3.3V),对于矩阵按键来讲:4根行线是输入的是由FPGA控制拉高或拉低,4根列线数输出的,是由4根行线的输入及按键的状态决定输出的高低电平

image.png

上图是用于显示运算数字和运算结果的数码管模块。考虑到节约使用引脚,这里使用了两块串行转并行的74HC595芯片进行动态扫描输出,利用人眼的视觉暂留效应使每个数码管都可以正常显示不同的数字。


image.png

上图是320*240的LCD模块电路,从上图可以看出,屏幕有六个引脚的输入,其中LED-引脚用于控制背光的亮度,RES引脚用于屏幕复位 ,DC引脚用于数据/命令的选择,SDA引脚用于SPI数据的输入,SCL引脚用于SPI时钟信号的输入,CS引脚用于SPI片选信号的输入。

七、实现的功能以及图片展示

在本项目中,通过小脚丫FPGA套件STEP BaseBoard V4.0实现两位十进制加、减、乘、除计算器。其中,显示方式通过尺寸为320*240的LCD屏幕和八个数码管进行显示。LCD屏可以显示整个运算过程,包括运算数、运算符号、数值正负;数码管从左往右的前两位显示第一位数,第三第四位显示第二位数,第五到第八位显示运算结果。其中,加法、减法、乘法都可以全精度显示,并且支持负数显示;对于除法,可以正确显示小数点的位置,但是考虑只有四位数码管显示运算结果,所以只能显示四位有效数字,并且除法会对最后一位数进行四舍五入处理。

以下对实现的功能进行图片展示:

加法:image.png

减法:image.png

乘法:image.png

除法:image.png

八、主要代码片段及说明

首先是顶层文件代码:

module calculator(
        input               clk,            //system clock
        input               rst_n,      //system reset
        input       [3:0]   col,
        output      [3:0]   row,
        output              seg_rck,    //74HC595的RCK管脚
        output              seg_sck,    //74HC595的SCK管脚
        output              seg_din,        //74HC595的SER管脚
        output              lcd_rst,
        output              lcd_blk,
        output              lcd_dc,  
        output              lcd_sclk,
        output              lcd_mosi,
        output              lcd_cs  
    );  

wire            [15:0]  key_out;
wire            [15:0]  key_pulse;
wire                    init_done;
wire [15:0] T_data;
wire [15:0] H_data;
wire [7:0] dat_en;
wire [7:0] dot_en;
wire [2:0] c_state;
wire [3:0] symbol;
//Array_KeyBoard
array_keyboard u1(
        .clk(clk),
        .rst_n(rst_n),
        .col(col),
        .row(row),
        .key_out(key_out),
        .key_pulse(key_pulse)
    );
//calculate module
calculate u2(
        .clk            (clk            ),
        .rst_n          (rst_n          ),  //系统复位,低有效
        .init_done      (init_done      ),  //LCD初始化结束信号
        .key_pulse      (key_pulse     ),   //按键矩阵输出脉冲
       
        .T_data         (T_data         ),  //运算数
        .H_data         (H_data         ),  //运算结果
       
        .dat_en         (dat_en         ),  //数码显示使能
        .dot_en         (dot_en         ),  //小数点显示使能
        .c_state        (c_state        ),  //计算器当前状态
        .symbol         (symbol         )   //运算符号
);
//segment_show
segment_scan u3(
        .clk            (clk            ),  //系统时钟
        .rst_n      (rst_n          ),  //系统复位,低有效
        .dat_1      (T_data[15:12]  ),  //SEG1 显示运算数第一位十位
        .dat_2      (T_data[11: 8]  ),  //SEG2 显示运算数第一位个位
        .dat_3      (T_data[ 7: 4]  ),  //SEG3 显示运算数第二位十位
        .dat_4      (T_data[ 3: 0]  ),  //SEG4 显示运算数第二位个位
        .dat_5      (H_data[15:12]  ),  //SEG5 显示运算结果第一位
        .dat_6      (H_data[11: 8]  ),  //SEG6 显示运算结果第二位
        .dat_7      (H_data[ 7: 4]  ),  //SEG7 显示运算结果第三位
        .dat_8      (H_data[ 3: 0]  ),  //SEG8 显示运算结果第四位
        .dat_en     (dat_en         ),  //各位数码管数据显示使能,[MSB~LSB]=[SEG1~SEG8]
        .dot_en     (dot_en         ),  //各位数码管小数点显示使能,[MSB~LSB]=[SEG1~SEG8]
        .seg_rck        (seg_rck        ),  //74HC595的RCK管脚
        .seg_sck        (seg_sck        ),  //74HC595的SCK管脚
        .seg_din        (seg_din        )   //74HC595的SER管脚
    );
//LCD_show
picture_display u4(
        .clk        (clk        ),
        .rst_n      (rst_n      ),
        .cal_state  (c_state    ),//计算器当前状态
        .cal_symbol (symbol     ),//运算符号
        .T_data     (T_data     ),//运算数
        .H_data     (H_data     ),//运算结果
        .dat_en     (dat_en     ),//数字显示使能
        .dot_en     (dot_en     ),//小数点使能

        .lcd_rst    (lcd_rst    ),
        .lcd_blk    (lcd_blk    ),
        .lcd_dc     (lcd_dc     ),
        .lcd_sclk   (lcd_sclk   ),
        .lcd_mosi   (lcd_mosi   ),
        .lcd_cs     (lcd_cs     ),
        .init_done  (init_done  )  

);

endmodule

这部分是顶层文件的代码,可以看到,整个工程被分成四部分:第一部分是按键矩阵的输入输出,该模块将按键信号读入处理之后变成一个16位的脉冲信号传输给计算模块;第二部分是计算模块,它控制计算器的运行状态,并将运算结果、运算符、运算状态进行输出;第三个部分是数码管显示模块,负责将运算模块输出的运算结果进行显示;第四部分是LCD显示模块,负责将运算模块输出的运算结果、运算符等在LCD上进行显示。

由于按键矩阵、数码管显示模块、LCD驱动模块参考官方的例程代码,这里不再进行赘述。

接下来只对运算模块进行说明:

首先是calculate模块:

    //按键数值对应
    reg[3:0] but_val;
    always @ (key_pulse)
    begin
        case(key_pulse)                  //首先按照任务图片里的按键进行映射                                
            16'h0001: but_val = 4'h7;   // 7
            16'h0002: but_val = 4'h8;   // 8
            16'h0004: but_val = 4'h9;   // 9
            16'h0008: but_val = 4'hd;   // /
            16'h0010: but_val = 4'h4;   // 4
            16'h0020: but_val = 4'h5;   // 5
            16'h0040: but_val = 4'h6;   // 6
            16'h0080: but_val = 4'hc;   // *
            16'h0100: but_val = 4'h3;   // 3
            16'h0200: but_val = 4'h2;   // 2
            16'h0400: but_val = 4'h1;   // 1
            16'h0800: but_val = 4'hb;   // -
            16'h1000: but_val = 4'h0;   // 0
            16'h2000: but_val = 4'he;   // .
            16'h4000: but_val = 4'hf;   // =
            16'h8000: but_val = 4'ha;   // +
            default:  but_val = 4'h0;       //
        endcase
    end

按照任务图片里的按键进行映射

    //状态机,根据输入的多少个数、运算符号、等号等进行状态跳转
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                c_state <= STATE0;
                num[0]<=4'd0;num[1]<=4'd0;num[2]<=4'd0;num[3]<=4'd0;symbol<=4'd0;
            end
        else if(init_done == 1'd0)
            begin
                c_state <= STATE0;
                num[0]<=4'd0;num[1]<=4'd0;num[2]<=4'd0;num[3]<=4'd0;symbol<=4'd0;
            end
        else if(key_pulse!=(16'd0))
            begin
                case(c_state)
                    //
                    STATE0: begin //初始状态
                                if((key_pulse&16'b0001011101110111)!=16'd0)
                                    begin
                                        c_state <= STATE1;
                                        num[0]<=num[0];num[1]<=but_val;symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                                else
                                    begin
                                        c_state <= STATE0;
                                        num[0]<=4'd0;num[1]<=4'd0;symbol<=4'd0;num[2]<=4'd0;num[3]<=4'd0;
                                    end
                            end
                    STATE1: begin//已经输入一个数
                                if((key_pulse&16'b1000100010001000)!=16'd0)
                                    begin
                                        c_state <= STATE3;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=but_val;num[2]<=num[2];num[3]<=num[3];
                                    end
                                else if((key_pulse&16'b0001011101110111)!=16'd0)
                                    begin
                                        c_state <= STATE2;
                                        num[0]<=num[1];num[1]<=but_val;symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                                else
                                    begin
                                        c_state <= c_state;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                            end
                    STATE2: begin //已经第二个数
                                if((key_pulse&16'b1000100010001000)!=16'd0)
                                    begin
                                        c_state <= STATE3;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=but_val;num[2]<=num[2];num[3]<=num[3];
                                    end
                                else
                                    begin
                                        c_state <= c_state;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                            end
                    STATE3: begin //已经输入了运算符号
                                if((key_pulse&16'b0001011101110111)!=16'd0)
                                    begin
                                        c_state <= STATE4;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=but_val;
                                    end
                                else
                                    begin
                                        c_state <= c_state;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end  
                            end
                    STATE4: begin//已经输入第三个数
                                if((key_pulse&16'b0100000000000000)!=16'd0)
                                    begin
                                        c_state <= STATE6;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                                else if((key_pulse&16'b0001011101110111)!=16'd0)
                                    begin
                                        c_state <= STATE5;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[3];num[3]<=but_val;
                                    end
                                else
                                    begin
                                        c_state <= c_state;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                            end
                    STATE5: begin //已经输入第四个数
                                if((key_pulse&16'b0100000000000000)!=16'd0)
                                    begin
                                        c_state <= STATE6;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                                else
                                    begin
                                        c_state <= c_state;
                                        num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                                    end
                            end
                    STATE6: begin//已经输入等号
                                c_state <= c_state;
                                num[0]<=num[0];num[1]<=num[1];symbol<=symbol;num[2]<=num[2];num[3]<=num[3];
                            end
                    default:begin
                                c_state <= STATE0;  
                                num[0]<=4'd0;num[1]<=4'd0;symbol<=4'd0;num[2]<=4'd0;num[3]<=4'd0;
                            end
                endcase
            end
    end

状态机,完成整个计算器的状态跳转

//加法运算
my_add my_add_inst(
        .clk(clk)  ,
        .rst_n(rst_n),      //复位信号
        .symbol(symbol),        //运算符号
        .num1(num1),
        .num2(num2),
        .state(c_state),
       
        .add_out(add_out),      //
        .add_flag(add_flag)
    );
//减法运算    
my_sub my_sub_inst(
        .clk(clk)  ,
        .rst_n(rst_n),      //复位信号
        .symbol(symbol),        //运算符号
        .num1(num1),
        .num2(num2),
        .state(c_state),
       
        .sub_out(sub_out),      //
        .sub_flag(sub_flag),
        .zf(zf)
    );
//乘法运算    
my_mul my_mul_inst(
        .clk(clk)  ,
        .rst_n(rst_n),      //复位信号
        .symbol(symbol),        //运算符号
        .num1(num1),
        .num2(num2),
        .state(c_state),
       
        .mul_out(mul_out),      //
        .mul_flag(mul_flag)
    );
//除法运算    
my_div my_div_inst(
        .clk(clk)  ,
        .rst_n(rst_n),      //复位信号
        .symbol(symbol),        //运算符号
        .num1(num1),
        .num2(num2),
        .state(c_state),
       
        .div_out(div_out),      //
        .div_flag(div_flag),
        .point_pos(point_pos)
    );

四个运算模块,实现加减乘除。

//对运算结果进行拆解,拆解成数码管和LCD可以显示的单个数    
reg [13:0] ans;
reg [15:0] ans_num;
reg c_flag;
always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                ans<=14'd1;c_flag<=1'b0;
                ans_num<=16'd0;
            end
        else if(c_flag)
            begin
                if(ans>=14'd1000)
                    begin
                        ans<=ans-14'd1000;
                        c_flag<=c_flag;
                        ans_num<={(ans_num[15:12]+4'd1),ans_num[11:0]};
                    end
                else if(ans>=14'd100)
                    begin
                        ans<=ans-14'd100;
                        c_flag<=c_flag;
                        ans_num<={ans_num[15:12],(ans_num[11:8]+4'd1),ans_num[7:0]};
                    end
                else if(ans>=14'd10)
                    begin
                        ans<=ans-14'd10;
                        c_flag<=c_flag;
                        ans_num<={ans_num[15:8],(ans_num[7:4]+4'd1),ans_num[3:0]};
                    end
                else if(ans>=14'd1)
                    begin
                        ans<=ans-14'd1;
                        c_flag<=c_flag;
                        ans_num<={ans_num[15:4],(ans_num[3:0]+4'd1)};
                    end
                else
                    begin
                        ans<=ans;
                        c_flag<=c_flag;
                        ans_num<=ans_num;
                    end
            end
        else if(((symbol==4'ha)&&add_flag)||((symbol==4'hc)&&mul_flag))
            begin
                ans<=(add_flag) ? add_out : mul_out;
                c_flag<=1'b1;
                ans_num<=16'd0;
            end
        else if(((symbol==4'hb)&&sub_flag))
            begin
                ans<=sub_out;
                c_flag<=1'b1;
                ans_num<=(~zf)?(16'd0):((sub_out>8'd9)?(16'h0b00):(16'h00b0));
            end
        else if(((symbol==4'hd)&&div_flag))
            begin
                ans<=div_out;
                c_flag<=1'b1;
                ans_num<=16'd0;
            end
        else
            begin
                ans<=ans;
                c_flag<=c_flag;
                ans_num<=ans_num;
            end
end

对运算结果进行每个位拆解,用于数码管和LCD显示数据

//对显示数据进行更新
always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                T_data<= 16'd0;
                H_data<= 16'd0;
            end
        else
            begin
                case(c_state)
                    STATE0: begin
                                T_data<= 16'd0;
                                H_data<= 16'd0;
                            end
                    STATE1: begin
                                T_data<= {T_data[15:12],num[1],T_data[7:0]};
                                H_data<= H_data;
                            end
                    STATE2: begin
                                T_data<= {num[0],num[1],T_data[7:0]};
                                H_data<= H_data;
                            end
                    STATE3: begin
                                T_data<= T_data;
                                H_data<= H_data;
                            end
                    STATE4: begin
                                T_data<= {T_data[15:4],num[3]};
                                H_data<= H_data;
                            end
                    STATE5: begin
                                T_data<= {T_data[15:8],num[2],num[3]};
                                H_data<= H_data;
                            end
                    STATE6: begin
                                T_data<= T_data;
                                H_data<= ans_num;
                            end
                    default:begin
                                T_data<= T_data;
                                H_data<= H_data;
                            end
                endcase
            end
end

完成显示数据的更新;

//数字使能,小数点使能进行更新
reg [3:0] dat_en_1234;
reg [3:0] dat_en_5678;
always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                dat_en_1234<= 4'd0;
            end
        else
            begin
                case(c_state)
                    STATE0: dat_en_1234 <= 4'd0;    //L1,
                    STATE1: dat_en_1234 <= dat_en_1234|4'b0100; //L2,
                    STATE2: dat_en_1234 <= dat_en_1234|4'b1000; //L3,
                    STATE3: dat_en_1234 <= dat_en_1234; //L4,
                    STATE4: dat_en_1234 <= dat_en_1234|4'b0001; //L5,
                    STATE5: dat_en_1234 <= dat_en_1234|4'b0010; //L6,
                    STATE6: dat_en_1234 <= dat_en_1234;
                    default:  dat_en_1234 <= dat_en_1234;       //cycle为0,PWM占空比为0,低电平
                endcase
            end
end

reg [1:0] cnt_dat;

always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                dat_en_5678<= 4'd0;
            end
        else if(c_state==STATE6&&ans==14'd0&&dat_en_5678==4'd0)
            begin
                if(symbol==4'hd)
                    begin
                        dat_en_5678<=4'b1111;
                    end
                else
                    begin
                        if(ans_num[15:12]!=4'd0)
                            dat_en_5678<=4'b1111;
                        else if(ans_num[11:8]!=4'd0)
                            dat_en_5678<=4'b0111;
                        else if(ans_num[7:4]!=4'd0)
                            dat_en_5678<=4'b0011;
                        else    
                            dat_en_5678<=4'b0001;
                    end
            end
        else
            begin
                dat_en_5678<= dat_en_5678;
            end
end

assign dat_en ={dat_en_1234,dat_en_5678};

always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                dot_en<= 8'd0;
            end
        else if(((symbol==4'hd)&&div_flag))
            begin
                dot_en<=(point_pos==2'd3)?(8'b00001000):(8'b00000100);
            end
        else
            begin
                dot_en<= dot_en;
            end
end

完成数码管使能和小数点使能更新

接下来是每个运算模块

module my_add(
        input               clk  ,
        input               rst_n,      //复位信号

        input   [3:0]      symbol,      //运算符号
        input   [6:0]       num1,
        input   [6:0]       num2,
        input   [2:0]       state,
       
        output  reg [7:0]   add_out,        //
        output  add_flag
    );

reg flag;
reg flag_r;    
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
           begin
               add_out <= 8'd0;
               flag<= 1'd0;
           end
       else if((state==3'd6)&&(symbol==4'ha))
           begin
               add_out <= num1 + num2;
               flag<= 1'd1;
           end
       else
           begin
               add_out <= 8'd0;
               flag<= 1'd0;
           end
end

always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            flag_r<= 1'd0;
        else
            flag_r<=flag;
end

assign add_flag = flag&(~flag_r);
   
endmodule

加法模块,当状态机进入运算状态时进行相加,完成时输出一个脉冲信号

module my_sub(
        input               clk     ,
        input               rst_n   ,       //复位信号

        input   [3:0]      symbol   ,       //运算符号
        input   [6:0]       num1    ,
        input   [6:0]       num2    ,
        input   [2:0]       state   ,
       
        output  reg [7:0]   sub_out ,       //
        output  sub_flag            ,
        output  reg zf
    );

reg flag;
reg flag_r;    
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
           begin
               sub_out <= 8'd0;
               zf<= 1'b0;
               flag<= 1'd0;
           end
       else if((state==3'd6)&&(symbol==4'hb))
           begin
               sub_out <= (num1>=num2)?(num1-num2):(num2-num1);
               zf<= (num1==num2||num1>num2)?(1'b0):(1'b1);
               flag<= 1'd1;
           end
       else
           begin
               sub_out <= 8'd0;
               zf<= 1'b0;
               flag<= 1'd0;
           end
end

always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            flag_r<= 1'd0;
        else
            flag_r<=flag;
end

assign sub_flag = flag&(~flag_r);
   
endmodule

减法模块,当状态机进入运算状态时进行相减,完成时输出脉冲信号,同时需要输出运算结果的正负信号

module my_mul(
        input               clk     ,
        input               rst_n   ,       //复位信号

        input   [3:0]      symbol   ,       //运算符号
        input   [6:0]       num1    ,
        input   [6:0]       num2    ,
        input   [2:0]       state   ,
       
        output  reg [13:0]  mul_out ,       //
        output  mul_flag            
    );

reg flag;
reg flag_r;
reg [3:0] cnt;    
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        begin
            mul_out <= 14'd0;
            flag<= 1'd0;
            cnt <= 4'd0;
        end
    else if(cnt >= 4'd8)
        begin
            mul_out<=mul_out;
            flag<=1'd1;
            cnt <= cnt;
        end
    else if(cnt>4'd0)
        begin
            mul_out<=(num2[7-cnt])?({mul_out[12:0],1'b0}+num1):({mul_out[12:0],1'b0});//进行移位相加
            flag<= 1'd0;
            cnt <= cnt +4'd1;
        end
    else if((state==3'd6)&&(symbol==4'hc)&&(cnt==4'd0))
        begin
            mul_out <= 14'd0;
            flag<= 1'd0;
            cnt <= 4'd1;
        end
    else
        begin
            mul_out <= 14'd0;
            flag<= 1'd0;
            cnt <= 4'd0;
        end
end

always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            flag_r<= 1'd0;
        else
            flag_r<=flag;
end

assign mul_flag = flag&(~flag_r);
   
endmodule

乘法模块,主要核心思想是进行移位相加,运算完成时输出脉冲信号

module my_div(
        input               clk     ,
        input               rst_n   ,       //复位信号

        input   [3:0]      symbol   ,       //运算符号
        input   [6:0]       num1    ,
        input   [6:0]       num2    ,
        input   [2:0]       state   ,
       
        output  [15:0]  div_out ,       //
        output  div_flag            ,
        output  reg [1:0]   point_pos
    );

reg flag;
reg flag_r;
reg [4:0] cnt;
reg [2:0] cnt_bit;
reg [3:0] zero_pos;
reg [7:0] num_d;
reg [15:0] bin_div;
reg [28:0] bin_x;    
wire [7:0] num_d_2;
wire num_bu;
assign num_d_2=num_d-num2;
assign num_bu=((cnt_bit==3'd7)?(1'b0):(num1[6-cnt_bit]));
//完成移位相减功能
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        begin
            bin_div <= 16'd0;
            num_d   <= 8'd0;
            cnt     <= 5'd0;
            cnt_bit <= 3'd0;
        end
    else if(cnt == 5'd17)
        begin
            bin_div <= bin_div;
            num_d   <= num_d;
            cnt     <= cnt;
            cnt_bit <= cnt_bit;
        end
    else if(cnt>=5'd1)
        begin
            bin_div <= {bin_div[14:0],(num_d>=num2)};
            num_d   <= (num_d>=num2)?({num_d_2[6:0],num_bu}):({num_d[6:0],num_bu});
            cnt     <= (num1>num2)?((bin_div==16'd0)?(cnt +(num_d>=num2)):(cnt+5'd1)):(cnt+5'd1);
            cnt_bit <=(cnt_bit==3'd7)?(cnt_bit):(cnt_bit+3'd1);
        end
    else if((state==3'd6)&&(symbol==4'hd)&&(cnt==5'd0))
        begin
            bin_div <= 16'd0;
            num_d   <= (num1>num2)?(8'd0):(num1);
            cnt     <=5'd1;
            cnt_bit <= (num1>num2)?(3'd0):(3'd7);
        end
    else
        begin
            bin_div <= 16'd0;
            num_d   <= 8'd0;
            cnt     <= 5'd0;
            cnt_bit <= 3'd0;
        end
end

reg flag_z;
//记录小数点的位置
always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                zero_pos<= 4'd0;
                flag_z  <= 1'b0;
            end
        else if(cnt>=5'd1 && cnt<=5'd16 &&flag_z==1'b0)
            begin
                zero_pos<=(cnt_bit==3'd7)?(cnt-1):(4'd0);
                flag_z  <=(cnt_bit==3'd7)?(1'b1):(1'b0);
            end
        else
            begin
                zero_pos<=zero_pos;
                flag_z  <=flag_z;
            end
end

//将除法结果乘100或者1000,方便后面进行拆分,用于显示
always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            begin
                bin_x<= 26'd0;
                point_pos<=2'd0;
                flag<=1'b0;
            end
        else if(cnt == 5'd17)
            begin
                bin_x<=(zero_pos>=4'd4)?({bin_div,6'd0}+{bin_div,5'd0}+{bin_div,2'd0}):({bin_div,10'd0}-{bin_div,4'd0}-{bin_div,3'd0});
                point_pos<=(zero_pos>=4'd4)?(2'd2):(2'd3);
                flag<=1'b1;
            end
        else
            begin
                bin_x<=bin_x;
                point_pos<=point_pos;
                flag<=flag;
            end
end

wire [13:0] div_mid;
//对乘100或者1000的结果进行截位,截取乘100或1000之后小数点之前的数
assign div_mid = {bin_x[28-zero_pos],bin_x[27-zero_pos],bin_x[26-zero_pos],bin_x[25-zero_pos],bin_x[24-zero_pos],bin_x[23-zero_pos]
,bin_x[22-zero_pos],bin_x[21-zero_pos],bin_x[20-zero_pos],bin_x[19-zero_pos],bin_x[18-zero_pos],bin_x[17-zero_pos],bin_x[16-zero_pos]
,bin_x[15-zero_pos]};

always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            flag_r<= 1'd0;
        else
            flag_r<=flag;
end

assign div_flag = flag&(~flag_r);
assign div_out  = div_mid+ bin_x[14-zero_pos];

endmodule

这是除法模块

九、仿真波形图

image.png

顶层文件calculator仿真图

image.png

按键输入部分仿真

image.png

计算模块仿真

image.png

数码管显示模块仿真

image.png

LCD驱动仿真模块


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