当前位置:   article > 正文

至简设计系列_简易计算器_quartus简易计数器实现加减乘

quartus简易计数器实现加减乘

–作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!

1.1 总体设计

1.1.1 概述

计算器是近代人发明的可以进行数字运算的机器。现代的电子计算器能进行数学运算的手持电子机器,拥有集成电路芯片,但结构比电脑简单得多,可以说是第一代的电子计算机,且功能也较弱,但较为方便与廉价,可广泛运用于商业交易中,是必备的办公用品之一。除显示计算结果外,还常有溢出指示、错误指示等。计算器电源采用交流转换器或电池。为了节省电能,计算器都采用CMOS工艺制作的大规模集成电路。
计算器一般由运算器、控制器、存储器、键盘、显示器、电源和一些可选外围设备及电子配件,通过人工或机器设备组成,抵挡计算器的运算器、控制器由数字逻辑电路实现简单的串行运算
计算器是最早的计算工具,例如:古代印加人利用许多颜色的绳结来计数或者记录历史,还有古希腊人的安提凯希拉装置,中国的算盘等。中国古代最早采用的一种计算工具叫筹策,又被叫做算筹。

1.1.2 设计目标

简易计算器支持简单的四则运算(支持负数),在此基础上,添加了连续运算功能。计算器面板如下:
在这里插入图片描述

1、计算器通过矩阵键盘模拟按键输入,并通过数码管显示。
2、计算器有“0、1、2、3、4、5、6、7、8、9、+、-、*、/、C、=”共16个按键。
3、计算器不支持输入负数,运算结果支持负数但不支持小数。
4、运算数1、运算数2以及运算结果最大支持8位。其中,运算数1和运算结果的位数包括符号位“-”。
5、运算数1和运算数2的默认值为0.
6、计算器支持连续运算,允许在输入运算数2后按下运算符,或者得出运算结果后按下运算符。
7、当运算结果溢出时,数码管显示8个F。
8、当操作数1或者操作数2的长度溢出时,蜂鸣器会响。

1.1.3 系统结构框图

系统结构框图如下所示:
在这里插入图片描述

图一

1.1.4 模块功能

键盘扫描模块实现功能
1、将外来异步信号打两拍处理,将异步信号同步化。
2、实现20ms按键消抖功能。
3、实现矩阵键盘的按键检测功能,并输出有效按键信号。

工作状态选择模块实现功能
1、根据接收的不同的按键信号,判断和决定计算器的工作状态。共有5种状态,输入运算数1(OP_1)、运算符(OPER)、输入运算数2(OP_2)、输出结果(RESULT)、结果错误(ERROR)

运算数1模块实现功能
1、当计算器处于运算数1状态下,任何连续输入的数字(不超过8位)都将存放在该模块中,作为运算数1.
2、当运算数已经到达8位时,此时无论输入任何数字,运算数1不变。
3、当计算器经过一次运算后(按下等号或者在运算数2状态下按下运算符),运算数去存放结果result。

运算符模块实现功能
1、保存最新按下的运算符。

运算数2模块实现功能
1、当计算器处于运算数2状态下,任何连续输入的数字(不超过8位)都将存放在该模块中,作为运算数2,默认值为0。
2、当运算数2已经到达8(包括符号位“-”),此时无论输入任何数字,运算数2不变。

运算单元模块实现功能
1、当计算器处于运算数2状态下按下运算符或者在任何状态下按下等号时,该模块根据此时运算数1、运算数2以及运算符的值,进行运算。
2、若运算结果溢出,或者长度大于8位(包括符号位“-”)或者除数为0时,输出8个F。
3、最多保留运算结果整数部分的8个有效数字,不保留任何小数。

显示对象选则模块实现功能
1、该模块的作用是根据当前计算器的工作状态来选择数码管的显示内容。

数码管显示模块实现功能
1、该模块的作用是对显示对象选择模块的显示数据输出信号进行数码管显示。

蜂鸣器模块实现功能
1、该模块的作用是对各种错误输入或输出进行响铃警告。

1.1.5 顶层信号

信号名接口方向定义
clk输入系统时钟,50Mhz
rst_n输入低电平复位信号
Key_col输入4位矩阵键盘列信号,默认高电平,开发板按键为普通按键时,不需要该信号
Key_row输出4位矩阵键盘行信号,默认低电平,开发板按键为普通按键时,不需要该信号
Segment输出8位数码管段选信号
Seg_sel输出8位数码管位选信号
beep输出1位蜂鸣器控制信号

1.1.6 参考代码

1.module  calc_project(  
2.    clk     ,  
3.    rst_n   ,  
4.    key_col ,  
5.    key_row ,  
6.    seg_sel ,  
7.    segment ,  
8.    beep      
9.    );  
10.  
11.    parameter   KEY_WID     =   4   ;  
12.    parameter   STATE_WID   =   5   ;  
13.    parameter   NUM_WID     =   27  ;  
14.    parameter   SEG_NUM     =   8   ;  
15.    parameter   SEG_WID     =   8   ;  
16.  
17.    input                       clk         ;  
18.    input                       rst_n       ;  
19.    input   [KEY_WID-1:0]       key_col     ;  
20.    output  [KEY_WID-1:0]       key_row     ;  
21.    output  [SEG_NUM-1:0]       seg_sel     ;  
22.    output  [SEG_WID-1:0]       segment     ;  
23.    output                      beep        ;  
24.  
25.    wire    [KEY_WID-1:0]       key_num     ;  
26.    wire                        key_vld     ;  
27.    wire    [KEY_WID-1:0]       key_num_out ;  
28.    wire    [KEY_WID-1:0]       key_vld_out ;  
29.    wire    [STATE_WID-1:0]     state_c     ;  
30.    wire    [NUM_WID-1:0]       op_1        ;  
31.    wire                        op_1_err    ;  
32.    wire    [KEY_WID-1:0]       oper        ;  
33.    wire    [NUM_WID-1:0]       op_2        ;  
34.    wire                        op_2_err    ;  
35.    wire    [NUM_WID-1:0]       result      ;  
36.    wire                        result_err  ;  
37.    wire                        result_neg  ;     
38.    wire    [SEG_NUM*4-1:0]     display     ;  
39.    wire                        display_vld ;  
40.  
41.    key_scan   key_scan_prj(  
42.                            .clk        (clk    )   ,  
43.                            .rst_n      (rst_n  )   ,  
44.                            .key_col    (key_col)   ,  
45.                            .key_row    (key_row)   ,  
46.                            .key_out    (key_num)   ,  
47.                            .key_vld    (key_vld)  
48.                            );  
49.  
50.    work_state  work_state_prj(  
51.                                .clk        (clk        )   ,  
52.                                .rst_n      (rst_n      )   ,  
53.                                .key_num    (key_num    )   ,  
54.                                .key_vld    (key_vld    )   ,  
55.                                .result_err (result_err )   ,  
56.                                .key_num_out(key_num_out)   ,  
57.                                .key_vld_out(key_vld_out)   ,  
58.                                .state_c    (state_c    )  
59.                                );  
60.  
61.    op_1    op_1_prj(  
62.                        .clk        (clk        )   ,  
63.                        .rst_n      (rst_n      )   ,  
64.                        .key_num_out(key_num_out)   ,  
65.                        .key_vld_out(key_vld_out)   ,  
66.                        .state_c    (state_c    )   ,  
67.                        .result     (result     )   ,  
68.                        .op_1       (op_1       )   ,  
69.                        .op_1_err   (op_1_err   )  
70.                    );  
71.  
72.    oper    oper_prj(  
73.                        .clk        (clk        )   ,  
74.                        .rst_n      (rst_n      )   ,  
75.                        .key_num_out(key_num_out)   ,  
76.                        .key_vld_out(key_vld_out)   ,  
77.                        .state_c    (state_c    )   ,  
78.                        .oper       (oper       )  
79.                    );  
80.      
81.    op_2    op_2_prj(  
82.                        .clk        (clk        )   ,  
83.                        .rst_n      (rst_n      )   ,  
84.                        .key_num_out(key_num_out)   ,  
85.                        .key_vld_out(key_vld_out)   ,  
86.                        .state_c    (state_c    )   ,  
87.                        .op_1       (op_1       )   ,  
88.                        .op_2       (op_2       )   ,  
89.                        .op_2_err   (op_2_err   )  
90.                    );  
91.  
92.    result  result_prj(  
93.                        .clk        (clk        )   ,  
94.                        .rst_n      (rst_n      )   ,  
95.                        .key_num_out(key_num_out)   ,  
96.                        .key_vld_out(key_vld_out)   ,  
97.                        .state_c    (state_c    )   ,  
98.                        .op_1       (op_1       )   ,  
99.                        .oper       (oper       )   ,  
100.                        .op_2       (op_2       )   ,  
101.                        .result     (result     )   ,  
102.                        .result_err (result_err )   ,  
103.                        .result_neg (result_neg )  
104.                    );  
105.  
106.    display_sel  display_sel_prj(  
107.                                .clk        (clk        )   ,  
108.                                .rst_n      (rst_n      )   ,  
109.                                .state_c    (state_c    )   ,  
110.                                .op_1       (op_1       )   ,  
111.                                .op_2       (op_2       )   ,  
112.                                .result     (result     )   ,  
113.                                .result_neg (result_neg )   ,  
114.                                .display    (display    )   ,  
115.                                .display_vld(display_vld)  
116.                                );  
117.  
118.    segment  segment_prj(  
119.                            .rst_n      (rst_n      )   ,  
120.                            .clk        (clk        )   ,  
121.                            .display    (display    )   ,  
122.                            .display_vld(display_vld)   ,  
123.                            .seg_sel    (seg_sel    )   ,  
124.                            .segment    (segment    )    
125.                        );  
126.  
127.    beep    beep_prj(  
128.                        .clk        (clk        )   ,  
129.                        .rst_n      (rst_n      )   ,  
130.                        .op_1_err   (op_1_err   )   ,  
131.                        .op_2_err   (op_2_err   )   ,  
132.                        .result_err (result_err )   ,  
133.                        .beep       (beep       )  
134.                    );  
135.  
136.endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

1.2 键盘扫描模块设计

1.2.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
key_col输入矩阵键盘列输入信号
Key_row输出矩阵键盘行输出信号
Key_out输出按键位置输出信号,key_vld有效时,该信号有效。
Key_vld输出按键有效指示信号,高电平有效

1.2.2 设计思路

在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=310

1.2.3 参考代码

1.always  @(posedge clk or negedge rst_n)begin  
2.    if(rst_n==1'b0)begin  
3.        key_col_ff0 <= 4'b1111;  
4.        key_col_ff1 <= 4'b1111;  
5.    end  
6.    else begin  
7.        key_col_ff0 <= key_col    ;  
8.        key_col_ff1 <= key_col_ff0;  
9.    end  
10.end  
11.  
12.  
13.always @(posedge clk or negedge rst_n) begin   
14.    if (rst_n==0) begin  
15.        shake_cnt <= 0;   
16.    end  
17.    else if(add_shake_cnt) begin  
18.        if(end_shake_cnt)  
19.            shake_cnt <= 0;   
20.        else  
21.            shake_cnt <= shake_cnt+1 ;  
22.   end  
23.end  
24.assign add_shake_cnt = key_col_ff1!=4'hf;  
25.assign end_shake_cnt = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;  
26.  
27.  
28.always  @(posedge clk or negedge rst_n)begin  
29.    if(rst_n==1'b0)begin  
30.        state_c <= CHK_COL;  
31.    end  
32.    else begin  
33.        state_c <= state_n;  
34.    end  
35.end  
36.  
37.always  @(*)begin  
38.    case(state_c)  
39.        CHK_COL: begin  
40.                     if(col2row_start )begin  
41.                         state_n = CHK_ROW;  
42.                     end  
43.                     else begin  
44.                         state_n = CHK_COL;  
45.                     end  
46.                 end  
47.        CHK_ROW: begin  
48.                     if(row2del_start)begin  
49.                         state_n = DELAY;  
50.                     end  
51.                     else begin  
52.                         state_n = CHK_ROW;  
53.                     end  
54.                 end  
55.        DELAY :  begin  
56.                     if(del2wait_start)begin  
57.                         state_n = WAIT_END;  
58.                     end  
59.                     else begin  
60.                         state_n = DELAY;  
61.                     end  
62.                 end  
63.        WAIT_END: begin  
64.                     if(wait2col_start)begin  
65.                         state_n = CHK_COL;  
66.                     end  
67.                     else begin  
68.                         state_n = WAIT_END;  
69.                     end  
70.                  end  
71.       default: state_n = CHK_COL;  
72.    endcase  
73.end  
74.assign col2row_start = state_c==CHK_COL  && end_shake_cnt;  
75.assign row2del_start = state_c==CHK_ROW  && row_index==3 && end_row_cnt;  
76.assign del2wait_start= state_c==DELAY    && end_row_cnt;  
77.assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;  
78.  
79.always  @(posedge clk or negedge rst_n)begin  
80.    if(rst_n==1'b0)begin  
81.        key_row <= 4'b0;  
82.    end  
83.    else if(state_c==CHK_ROW)begin  
84.        key_row <= ~(1'b1 << row_index);  
85.    end  
86.    else begin  
87.        key_row <= 4'b0;  
88.    end  
89.end  
90.  
91.  
92.always @(posedge clk or negedge rst_n) begin   
93.    if (rst_n==0) begin  
94.        row_index <= 0;   
95.    end  
96.    else if(add_row_index) begin  
97.        if(end_row_index)  
98.            row_index <= 0;   
99.        else  
100.            row_index <= row_index+1 ;  
101.   end  
102.   else if(state_c!=CHK_ROW)begin  
103.       row_index <= 0;  
104.   end  
105.end  
106.assign add_row_index = state_c==CHK_ROW && end_row_cnt;  
107.assign end_row_index = add_row_index  && row_index == 4-1 ;  
108.  
109.  
110.always @(posedge clk or negedge rst_n) begin   
111.    if (rst_n==0) begin  
112.        row_cnt <= 0;   
113.    end  
114.    else if(add_row_cnt) begin  
115.        if(end_row_cnt)  
116.            row_cnt <= 0;   
117.        else  
118.            row_cnt <= row_cnt+1 ;  
119.   end  
120.end  
121.assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;  
122.assign end_row_cnt = add_row_cnt  && row_cnt == 16-1 ;  
123.  
124.  
125.always  @(posedge clk or negedge rst_n)begin  
126.    if(rst_n==1'b0)begin  
127.        key_col_get <= 0;  
128.    end  
129.    else if(state_c==CHK_COL && end_shake_cnt ) begin  
130.        if(key_col_ff1==4'b1110)  
131.            key_col_get <= 0;  
132.        else if(key_col_ff1==4'b1101)  
133.            key_col_get <= 1;  
134.        else if(key_col_ff1==4'b1011)  
135.            key_col_get <= 2;  
136.        else   
137.            key_col_get <= 3;  
138.    end  
139.end  
140.  
141.  
142.always  @(posedge clk or negedge rst_n)begin  
143.    if(rst_n==1'b0)begin  
144.        key_out <= 0;  
145.    end  
146.    else if(state_c==CHK_ROW && end_row_cnt)begin  
147.        key_out <= {row_index,key_col_get};  
148.    end  
149.    else begin  
150.        key_out <= 0;  
151.    end  
152.end  
153.  
154.always  @(posedge clk or negedge rst_n)begin  
155.    if(rst_n==1'b0)begin  
156.        key_vld <= 1'b0;  
157.    end  
158.    else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin  
159.        key_vld <= 1'b1;  
160.    end  
161.    else begin  
162.        key_vld <= 1'b0;  
163.    end  
164.end  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164

1.3 工作状态选择模块设计

1.3.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
key_vld输入按键按下指示信号
Key_num输入按键位置输入信号,key_vld有效时,该信号有效
Result_err输入运算结果错误指示信号
Key_num_out输出计算器按下位置输出信号,key_vld_out有效时,该信号有效。
Key_vld_out输出计算器按键按下有效指示信号,高电平有效。
State_c输出计算器工作状态指示信号

1.3.2 设计思路

该模块的主要功能是根据按下的按键进行不同来判断和决定计算器的工作状态。一条等式可以写成:运算数1+操作符+运算数2+等号+结果的形式。考虑到结果错误的情况,我将本模块的状态划分为5个,分别是:输入运算数1(OP_1)、运算符(OPER)、输入运算数2(OP_2)、输出结果(RESULT)、结果错误(ERROR)。
下图为本模块的状态跳转图:
在这里插入图片描述

复位后,状态机进入OP_1状态,即初始状态为OP_1;

在OP_1状态下:
A.按下等号,跳到RESULT状态;
B.按下运算符,跳到OPER状态;

在OPER状态下:
A.按下数字,跳到OP_2状态;
B.按下等号,跳到RESULT状态;

在OP_2状态下:
A.按下等号,跳到RESULT状态;
B.按下运算符,跳到OPER状态;

在RESULT状态下:
A.按下数字,跳到OP_1状态;
B.按下运算符,跳到OPER状态;
C.按下等号,停留在RESULT状态;

在ERROR状态下:
A.按下数字,跳到OP_1状态;
B.按下其他按键,停留在ERROR状态;

无论当前处于什么状态,只要检测到运算结果错误指示信号有效,即刻跳转到ERROR状态。

1.3.3 参考代码

使用GVIM,在命令模式下输入如下内容,即可生成本模块所需要的状态机代码。
在这里插入图片描述

使用明德扬的状态机模板,可以很快速的写出此模块代码。

165.always  @(*)begin  
166.    case(key_num)  
167.        4'd0   :key_num_chg = 4'd7   ;  
168.        4'd1   :key_num_chg = 4'd8   ;  
169.        4'd2   :key_num_chg = 4'd9   ;  
170.        4'd3   :key_num_chg = 4'd10  ;  
171.        4'd7   :key_num_chg = 4'd11  ;  
172.        4'd8   :key_num_chg = 4'd1   ;  
173.        4'd9   :key_num_chg = 4'd2   ;  
174.        4'd10  :key_num_chg = 4'd3   ;  
175.        4'd11  :key_num_chg = 4'd14  ;  
176.        4'd12  :key_num_chg = 4'd0   ;  
177.        4'd13  :key_num_chg = 4'd12  ;  
178.        4'd14  :key_num_chg = 4'd13  ;  
179.        default:key_num_chg = key_num;  
180.    endcase  
181.end  
182.  
183.assign  key_num_en = (key_num_chg==0 || key_num_chg==1 || key_num_chg==2 || key_num_chg==3 || key_num_chg==4 || key_num_chg==5 || key_num_chg==6 || key_num_chg==7 || key_num_chg==8 || key_num_chg==9) && key_vld==1;  
184.assign  key_op_en = (key_num_chg==10 || key_num_chg==11 || key_num_chg==12 || key_num_chg==13) && key_vld==1;  
185.assign  key_cal_en = key_num_chg==15 && key_vld==1;   
186.assign  key_back_en = key_num_chg==14 && key_vld==1;  
187.  
188.  
189.  
190.always @(posedge clk or negedge rst_n) begin   
191.    if (rst_n==0) begin  
192.        state_c <= OP_1 ;  
193.    end  
194.    else begin  
195.        state_c <= state_n;  
196.   end  
197.end  
198.  
199.always @(*) begin  
200.    if(result_err)begin  
201.       state_n = ERROR;  
202.   end   
203.   else begin  
204.       case(state_c)    
205.        OP_1 :begin  
206.            if(op_12oper_start)   
207.                state_n = OPER ;  
208.            else if(op_12result_start)   
209.                state_n = RESULT ;  
210.            else   
211.                state_n = state_c ;  
212.        end  
213.        OPER :begin  
214.            if(oper2op_2_start)   
215.                state_n = OP_2 ;  
216.            else if(oper2result_start)   
217.                state_n = RESULT ;  
218.            else   
219.                state_n = state_c ;  
220.        end  
221.        OP_2 :begin  
222.            if(op_22oper_start)  
223.                state_n = OPER ;  
224.            else if(op_22result_start)   
225.                state_n = RESULT ;  
226.            else   
227.                state_n = state_c ;  
228.        end  
229.        RESULT :begin  
230.            if(result2op_1_start)   
231.                state_n = OP_1 ;  
232.            else if(result2oper_start)   
233.                state_n = OPER ;  
234.            else   
235.                state_n = state_c ;  
236.        end  
237.        ERROR :begin  
238.            if(error2op_1_start)   
239.                state_n = OP_1 ;  
240.            else   
241.                state_n = state_c ;  
242.        end  
243.        default : state_n = OP_1 ;  
244.    endcase  
245.end  
246.end  
247.  
248.assign op_12oper_start   = state_c==OP_1   && key_op_en ;  
249.assign op_12result_start = state_c==OP_1   && key_cal_en;  
250.assign oper2op_2_start   = state_c==OPER   && key_num_en;  
251.assign oper2result_start = state_c==OPER   && key_cal_en;  
252.assign op_22oper_start   = state_c==OP_2   && key_op_en ;  
253.assign op_22result_start = state_c==OP_2   && key_cal_en;  
254.assign result2op_1_start = state_c==RESULT && key_num_en;  
255.assign result2oper_start = state_c==RESULT && key_op_en ;  
256.assign error2op_1_start  = state_c==ERROR  && key_num_en;  
257.  
258.always  @(posedge clk or negedge rst_n)begin  
259.    if(rst_n==1'b0)begin  
260.        key_num_out <= 0;  
261.    end  
262.    else begin  
263.        key_num_out <= key_num_chg;  
264.    end  
265.end  
266.  
267.always  @(posedge clk or negedge rst_n)begin  
268.    if(rst_n==1'b0)begin  
269.        key_vld_out <= 0;  
270.    end  
271.    else begin  
272.        key_vld_out <= key_vld;  
273.    end  
274.end  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110

1.4 运算数1模块设计

1.4.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Key_num_out输入计算器按下位置输出信号,key_vld_out有效时,该信号有效。
Key_vld_out输入计算器按键按下有效指示信号,高电平有效。
State_c输入计算器工作状态指示信号
Op_1输出运算数1输出信号
Result输入运算结果输出信号
Op_1_err输出运算数1溢出信号

1.4.2 设计思路

该模块主要的作用是根据当前状态和输入的按键,来决定运算数1要输出的结果。由于本工程需要实现连续运算的功能,所以在这个模块中要区分是否已经得出了运算结果。

下面是计算完成指示信号flag_calc的设计思路:
1、该信号为高时,表示完成一次计算过程得到了结果。初始状态为低电平;
2、当输入操作数2后又按下了等号或者其他操作符的时候,变为高电平,所以变高的条件为(state_c_ffOP_2 && state_cOPER) || state_cRESULT;
3、当处在操作数1状态时,为低电平,所以变低的条件为state_c
OP_1。其他情况保持不变。

下面是运算数1输出信号op_1的设计思路:
1、该信号表示运算数1要输出的值。初始状态为0;
2、在结果错误状态的时候,给一个不超过范围的任意值,此代码中给的10;
3、在得到计算结果或者计算结果错误的时候,输入数字,输出为按下按键的对应值(key_num_out);
4、在输入操作数1之后,按下退格键,op_1输出的值除以10进行取整;
5、在输入操作数1状态下通过键盘输入数字,需要判断是否超过显示范围,如果没有超过的话就需要将当前op_1的值乘以10,然后加上按下的数字的值,进行输出;
6、当计算完成时,即flag_calc==1,操作数1输出计算的结果result;
7、其他时侯操作数1保持不变。

下面是运算数1溢出信号op_1_err的设计思路:
1、初始状态为0,表示没有溢出。
2、当一直处于操作数1状态,按下键盘输入数字之后,操作数1的值溢出了,则将运算数1溢出信号拉高。
3、其他时刻保持为低电平。

1.4.3 参考代码

275.assign  key_num_en = (key_num_out==0 || key_num_out==1 || key_num_out==2 || key_num_out==3 || key_num_out==4 || key_num_out==5 || key_num_out==6 || key_num_out==7 || key_num_out==8 || key_num_out==9) && key_vld_out==1;
276.assign  key_op_en = (key_num_out==10 || key_num_out==11 || key_num_out==12 || key_num_out==13) && key_vld_out==1;
277.assign  key_cal_en = key_num_out==15 && key_vld_out==1; 
278.assign  key_back_en = key_num_out==14 && key_vld_out==1;
279.
280.always  @(posedge clk or negedge rst_n)begin
281.    if(rst_n==1'b0)begin
282.        state_c_ff <= 0;
283.    end
284.    else begin
285.        state_c_ff <= state_c;
286.    end
287.end
288.
289.always  @(posedge clk or negedge rst_n)begin
290.    if(rst_n==1'b0)begin
291.        flag_calc <= 0;
292.    end
293.    else if(state_c==OP_1)begin
294.        flag_calc <= 1'b0;
295.    end
296.    else if(state_c_ff==OP_2 && state_c==OPER || state_c==RESULT)begin
297.        flag_calc <= 1'b1;
298.    end
299.    else begin
300.        flag_calc <= flag_calc;
301.    end
302.end
303.
304.always  @(posedge clk or negedge rst_n)begin
305.    if(rst_n==1'b0)begin
306.        op_1 <= 0;
307.    end
308.    else if(state_c==ERROR)begin
309.        op_1 <= 10;
310.    end
311.    else if((state_c_ff==RESULT || state_c_ff==ERROR) && state_c==OP_1)begin
312.        op_1 <= key_num_out;
313.    end
314.    else if(state_c==OP_1 && key_back_en==1)begin
315.        op_1 <= op_1 / 10;
316.    end
317.    else if(state_c==OP_1 && key_num_en==1)begin
318.        op_1 <= (op_1>9999999) ? op_1 : (op_1*10+key_num_out);
319.    end
320.    else if(flag_calc==1)begin
321.        op_1 <= result;
322.    end
323.    else begin
324.        op_1 <= op_1;
325.    end
326.end
327.
328.always  @(posedge clk or negedge rst_n)begin
329.    if(rst_n==1'b0)begin
330.        op_1_err <= 0;
331.    end
332.    else if(state_c==OP_1 && state_c_ff==OP_1 && key_num_en==1 && op_1>9999999)begin
333.        op_1_err <= 1'b1;
334.    end
335.    else begin
336.        op_1_err <= 1'b0;
337.    end
338.end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

1.5 运算符模块设计

1.5.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Key_num_out输入计算器按下位置输出信号,key_vld_out有效时,该信号有效。
Key_vld_out输入计算器按键按下有效指示信号,高电平有效。
State_c输入计算器工作状态指示信号
oper输出运算符输出信号

1.5.2 设计思路

本模块的设计思路比较简单,只需要判断哪些按键是运算符,然后再这些运算符被按下的时候,将他们对应的值输出就可以了。

下面是运算符指示信号设计思路:
1、当“加”“减”“乘”“除”四个按键的任意一个被按下之后,该信号置为高电平;
2、当“加”“减”“乘”“除”四个按键没有一个被按下的时候,该信号置为低电平。

下面是运算符输出信号oper设计思路:
初始状态,该信号输出0;
1、当处于操作数1状态时,输出0;
2、当“加”“减”“乘”“除”任意按键被按下之后,输出该按键对应的值;
3、其他时候保持不变;

1.5.3 参考代码

339.assign  key_op_en = (key_num_out==10 || key_num_out==11 || key_num_out==12 || key_num_out==13) && key_vld_out==1;
340.
341.always  @(posedge clk or negedge rst_n)begin
342.    if(rst_n==1'b0)begin
343.        oper <= 0;
344.    end
345.    else if(state_c==OP_1)begin
346.        oper <= 0;
347.    end
348.    else if(key_op_en==1)begin
349.        oper <= key_num_out;
350.    end
351.    else begin
352.        oper <= oper;
353.    end
354.End
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

1.6 运算数2模块设计

1.6.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Key_num_out输入计算器按下位置输出信号,key_vld_out有效时,该信号有效。
Key_vld_out输入计算器按键按下有效指示信号,高电平有效。
State_c输入计算器工作状态指示信号
Op_2输出运算数2输出信号
Op_2_err输出运算数2溢出信号

1.6.2 设计思路

该模块主要的作用是根据当前状态和输入的按键,来决定运算数2要输出的结果。

下面是运算数2输出信号op_2的设计思路:
1、该信号表示运算数2要输出的值。初始状态为0;
2、在运算符状态下,此时数码管不显示运算数2的值,让它输出0;
3、输入运算符之后,之后再输入的就是运算数2的值,此时运算数2就等于按下按键所对应的数值。
4、在输入运算数2之后,按下退格键,运算数2的值除以10进行取整;
5、在输入运算数2状态下通过键盘输入数字,需要判断是否超过显示范围,如果没有超过的话就需要将
当前运算数2的值乘以10,然后加上按下的数字的值,进行输出;
6、其他时侯运算数2保持不变。

下面是运算数2溢出信号op_2_err的设计思路:
1、初始状态为0,表示没有溢出。
2、当一直处于运算数2状态,按下键盘输入数字之后,运算数2的值溢出了,则将运算数2溢出信号拉高。
3、其他时刻保持为低电平。

1.6.3 参考代码

1.assign  key_num_en = (key_num_out==0 || key_num_out==1 || key_num_out==2 || key_num_out==3 || key_num_out==4 || key_num_out==5 || key_num_out==6 || key_num_out==7 || key_num_out==8 || key_num_out==9) && key_vld_out==1;  
2.assign  key_op_en = (key_num_out==10 || key_num_out==11 || key_num_out==12 || key_num_out==13) && key_vld_out==1;  
3.assign  key_cal_en = key_num_out==15 && key_vld_out==1;   
4.assign  key_back_en = key_num_out==14 && key_vld_out==1;  
5.  
6.always  @(posedge clk or negedge rst_n)begin  
7.    if(rst_n==1'b0)begin  
8.        state_c_ff <= 0;  
9.    end  
10.    else begin  
11.        state_c_ff <= state_c;  
12.    end  
13.end  
14.  
15.always  @(posedge clk or negedge rst_n)begin  
16.    if(rst_n==1'b0)begin  
17.        op_2 <= 0;  
18.    end  
19.    else if(state_c==OPER)begin  
20.        op_2 <= 0;  
21.    end  
22.    else if(state_c_ff==OPER && state_c==OP_2)begin  
23.        op_2 <= key_num_out;  
24.    end  
25.    else if(state_c==OP_2 && key_back_en==1)begin  
26.        op_2 <= op_2 / 10;  
27.    end  
28.    else if(state_c==OP_2 && key_num_en==1)begin  
29.        op_2 <= (op_2>9999999) ? op_2 : (op_2*10+key_num_out);  
30.    end  
31.    else begin  
32.        op_2 <= op_2;  
33.    end  
34.end  
35.  
36.always  @(posedge clk or negedge rst_n)begin  
37.    if(rst_n==1'b0)begin  
38.        op_2_err <= 0;  
39.    end  
40.    else if(state_c==OP_2 && key_num_en==1 && op_2>9999999)begin  
41.        op_2_err <= 1'b1;  
42.    end  
43.    else begin  
44.        op_2_err <= 1'b0;  
45.    end  
46.end  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

1.7 运算单元模块设计

1.7.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Key_num_out输入计算器按下位置输出信号,key_vld_out有效时,该信号有效。
Key_vld_out输入计算器按键按下有效指示信号,高电平有效。
State_c输入计算器工作状态指示信号
oper输出运算符输出信号
Op_1输入运算数1输入信号
Op_2输入运算数2输入信号
Result输出运算结果输出信号
Result_err输出运算结果错误信号,运算结果溢出或者除数为0时,该信号输出一个时钟周期的高电平
Result_neg输出运算结果符号位指示信号,当运算结果为负数时,该信号为高电平

1.7.2 设计思路

本模块的作用是根据运算符,对运算数1和运算数2进行操作得出结果。

由于再进行计算的时候考虑小数减去大数的情况,所以运算结果允许为负数,因此需要有符号位指示信号,下面是运算结果符号位指示信号result_neg的设计思路:
1、只有当运算结果为负数的时候,才显示“负号”,因此初始状态为低电平;
2、当算式输入完成按下等号之后,如果运算符是“减”,并且运算数1小于运算数2,则运算结果为负数,将result_neg信号拉高。
3、由于该计算器支持连续输入,如果当前计算的结果为负数,接着输入的运算符为“加”,下一次进行加法运算,并且运算数1(此时比较不考虑符号位)小于或等于运算数2,则表示运算结果为正数,此时将result_neg信号拉低。
4、在进行连续计算的时候,如果得到的结果超过显示上限,要进入错误状态,这个时候符号位指示信号应该为低电平。
5、无论在计算中得到的结果是正还是负,如果下次输入的为运算数1,都需要result_neg信号为低电平。
6、由于除法不支持小数显示,只取整数部分,所以当运算结果为负数,并进行除法运算的时候,如果得到的结果为0,不应该显示为“负0”,应当将符号位指示信号置为低电平。

本模块主要的功能是实现加减乘除运算,下面是对运算结果输出信号result的设计思路:
1、初始状态没有经过计算,自然输出为0。
2、在进行加法的时候,由于存在连续计算的情况,需要考虑符号位。当符号位指示信号为0,直接将运算数1和运算数2相加即可;当符号位指示信号为1,则需要判断运算数1和运算数2的大小,确保是大的减去小的。
3、在进行减法的时候,同样需要考虑符号位。当符号位指示信号为0的时候,需要判断运算数1和运算数2的大小,保证大的减去小的;当符号位指示信号位1的时候,直接将运算数1和运算数2相加即可。
4、乘法运算直接将运算数1和运算数2相乘即可。
5、在进行除法运算时,由于无法表示小数,因此这里需要采用运算数1除以运算数2取整的方法,即op_1/op_2。

在计算过程中,如果得到的结果超过显示上限或者错误的计算方法,需要做出错误提示,下面是对于运算结果错误信号result_err的设计思路:
1、初始状态下,该信号为0,表示没有错误。
2、得到运算结果后,若继续输入数字,则会进入到运算数1状态,这个时候不进行错误提示。
3、在运算数2状态下输入运算符,或者在结果不是错误的状态下输出“等号”(表示进行连续计算)。根据输入的运算符进行相应的判断:
加:如果运算结果为正数,则判断运算数1加上运算数2之后会不会溢出,若溢出则做出错误提示;如果运算结果为负数,则不进行错误提示。
减:如果运算结果为负数,则判断运算数1加上运算数2之后会不会溢出,若溢出则做出错误提示;如果运算结果为正数,则判断两个数相减之后的结果是否会溢出。
乘:无论运算结果为何值,都只需要判断两数相乘之后的的结果会不会溢出就可以了。
除:在进行除法运算的时候,需要避免出现除数为0的情况,如果出现此情况,则进行错误指示。

1.7.3 参考代码

1.assign  key_op_en = (key_num_out==10 || key_num_out==11 || key_num_out==12 || key_num_out==13) && key_vld_out==1;
2.assign  key_cal_en = key_num_out==15 && key_vld_out==1;
3.assign  calculate = (state_c_ff==OP_2 && state_c==OPER || key_cal_en==1);
4.
5.always  @(posedge clk or negedge rst_n)begin
6.    if(rst_n==1'b0)begin
7.        state_c_ff <= 0;
8.    end
9.    else begin
10.        state_c_ff <= state_c;
11.    end
12.end
13.
14.always  @(posedge clk or negedge rst_n)begin
15.    if(rst_n==1'b0)begin
16.        result <= 0;
17.    end
18.    else if(calculate==1)begin
19.        case(oper)
20.            ADD:begin
21.                if(result_neg==0)
22.                    result <= op_1 + op_2;
23.                else 
24.                    result <= (op_1>op_2) ? (op_1 - op_2) : (op_2 - op_1);
25.            end
26.            DEV:begin
27.                if(result_neg==0)
28.                    result <= (op_1>op_2) ? (op_1 - op_2) : (op_2 - op_1);
29.                else 
30.                    result <= op_1 + op_2;
31.            end
32.            MUL:begin
33.                result <= op_1 * op_2;
34.            end
35.            DIV:begin
36.                result <= op_1 / op_2;
37.            end
38.            default:result <= op_1;
39.        endcase
40.    end
41.    else begin
42.        result <= result;
43.    end
44.end
45.
46.always  @(posedge clk or negedge rst_n)begin
47.    if(rst_n==1'b0)begin
48.        result_neg <= 0;
49.    end
50.    else if(state_c==OP_1)begin
51.        result_neg <= 1'b0;
52.    end
53.    else if(state_c_ff==ERROR)begin
54.        result_neg <= 1'b0;
55.    end
56.    else if(calculate==1 && oper==DEV && op_1<op_2)begin
57.        result_neg <= 1'b1;
58.    end
59.    else if(calculate==1 && result_neg==1 && oper==ADD && op_1<=op_2)begin
60.        result_neg <= 1'b0;
61.    end
62.    else if(result==0)begin
63.        result_neg <= 1'b0;
64.    end
65.    else begin
66.        result_neg <= result_neg;
67.    end
68.end
69.
70.always  @(posedge clk or negedge rst_n)begin
71.    if(rst_n==1'b0)begin
72.        result_err <= 0;
73.    end
74.    else if(state_c==OP_1)begin
75.        result_err <= 1'b0;
76.    end
77.    else if((state_c_ff==OP_2 && state_c==OPER) || (key_cal_en==1 && state_c_ff!=ERROR))begin
78.        case(oper)
79.            ADD:begin
80.                if(result_neg==0)
81.                    result_err <= (op_1+op_2)>9999_9999 ? 1'b1 : 1'b0;
82.                else 
83.                    result_err <= 1'b0;
84.            end
85.            DEV:begin
86.                if(result_neg==1)
87.                    result_err <= (op_1+op_2)>999_9999 ? 1'b1 : 1'b0;
88.                else if(op_2>op_1)
89.                    result_err <= (op_2-op_1)>999_9999 ? 1'b1 : 1'b0;
90.                else 
91.                    result_err <= 1'b0;
92.            end
93.            MUL:begin
94.                if(result_neg==1)
95.                    result_err <= (op_1*op_2)>999_9999 ? 1'b1 : 1'b0;
96.                else 
97.                    result_err <= (op_1*op_2)>9999_9999 ? 1'b1 : 1'b0;
98.            end
99.            DIV:begin
100.                if(op_2==0)
101.                    result_err <= 1'b1;
102.                else 
103.                    result_err <= 1'b0;
104.            end
105.            default:result_err <= 1'b0;
106.        endcase 
107.    end 
108.    else begin
109.        result_err <= 1'b0;
110.    end
111.end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111

1.8 显示对象选择模块设计

1.8.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Op_1输入运算数1输入信号
Op_2输入运算数2输入信号
State_c输入计算器工作状态指示信号
Result_neg输入运算结果符号位指示信号
Disply输出显示数据输出信号
Display_vld输出显示数据有效指示信号

1.8.2 设计思路

该模块的作用是根据当前计算器的工作状态来选择数码管的显示内容。
1、复位后,该模块输出0;
2、当计算器处于OP_1状态下,该模块选择输出运算数1。
3、当计算器处于OPER状态下,该模块选择输出运算数1。
4、当计算器处于OP_2状态下,该模块选择输出运算数2。
5、当计算器处于RESULT状态下,该模块选择输出运算数1。
6、当计算器处于ERROR状态下,该模块选择输出8个F。
要将数据送到数码管显示,需要将收到的数据进行拆分,比如输入进来的是“12”,需要拆成一个4bit的“1”和一个4bit的“2”送给数码管显示模块。因此设计一个计数器的架构,如下图所示:
在这里插入图片描述

架构中使用到了一个时钟计数器dis_cnt、一个采集状态指示信号flag_add、dis_sel为输入要显示的数据、dis_sel_tmp为输入数据打一拍之后的数据、result_neg为运算结果符号位指示信号、result_neg_tmp为运算结果符号位指示信号打一拍之后的信号。下面分别介绍一下这些信号的设计思路:
采集状态指示信号flag_add:初始状态为0,表示不对数据进行采集显示。如果检测到输入的数据或者符号位发生变化,表示要在数码管上显示的数据有变化,该信号拉高,计数器可以进行计数,所以由0变1的条件为dis_sel!=dis_sel_tmp || result_neg!=result_neg_tmp。当计数器数完之后,表示要显示的数据已经全部显示,则将此信号拉低,所以由1变0的条件是end_dis_cnt。
显示数据dis_sel:该信号根据工作状态进行选择,当目前处于OP_2状态时,选择运算数2输入数据,其他情况都选择运算数1输入数据。
显示数据打一拍之后的信号dis_sel_tmp:该信号存在的目的就是为了检测显示数据是否发生变化。
运算结果符号位指示信号result_neg:输入信号。
符号位指示信号打一拍之后的信号result_neg_tmp:该信号存在的意义就是为了检测符号位是否发生变化。
时钟计数器dis_cnt:该计数器的作用有两个,延时和控制输入数据赋值给显示数据输出信号的对应位。加一条件为flag_add && (dis_seldis_sel_tmp && result_negresult_neg_tmp),表示处在采集状态时,如果显示数据和符号位指示信号稳定,则开始计数。结束条件为数10个,由于计数器刚开始计数的时候,显示数据存在变化的可能,因此这里选择延迟两个时钟在对显示数据输出信号进行赋值(由于后面数据都是保持不变的,因此这个延时时间不是固定的,可以多延时一些),共有8个数码管,因此要赋值8次,所以计数器共需要数10个。

前面提到过,需要将显示数据显示到数码管上的话,需要将每一个数字进行拆分,一般采用除以10取余和取整的方法,本工程使用除法器的IP核,该IP核的作用就是将输入的数据除以10,得到商和余数。生成过程如下:
第一步、使用软件为Quartus Prime Lite Edition 18.1版本。首先打开软件之后,在主页面的右边找到“IP Catalog”窗口,在搜索栏中输入“div”进行搜索,然后双击“LPM_DIVIDE”。如果没有找到“IP Catalog”窗口,可在上方工具栏“Tools”中选择“IP Catalog”调出。
在这里插入图片描述

第二步、选择IP核生成的路径,并将其命名为“div”,注意这里的名字不能有中文字符或者全数字。在下方文件类型中选择“Verilog”,然后点击OK。
在这里插入图片描述

第三步、在之后出现的IP核设置界面中,“How wide should the numerator input”表示需要设置的分子的位宽,这里设置为27。“How wide should the denominator input”表示需要设置的分母的位宽这里设置为4。在下方分子和分母的表示都选用“Unsigned”无符号类型。然后点击Next
在这里插入图片描述

第四步、下图中的1处表示是否需要对输出进行打拍,这里选择打一拍之后输出。2处表示要进行的优化,这里选择默认优化。3处表示是否总是返回正余数,选择是。然后点击Next。
在这里插入图片描述

第五步、方框出表示该IP核在仿真的时候需要调用的库,直接点击Next即可。
在这里插入图片描述

第六步、这一界面是设置需要生成的文件,本工程只需要生成默认的即可,所以不用勾选。点击Finish。
在这里插入图片描述

1.8.3 参考代码

112.always  @(posedge clk or negedge rst_n)begin
113.    if(rst_n==1'b0)begin
114.        result_neg_tmp <= 0;
115.    end
116.    else begin
117.        result_neg_tmp <= result_neg;
118.    end
119.end
120.
121.always  @(*)begin
122.    if(state_c==OP_2)begin
123.        dis_sel = op_2;
124.    end
125.    else begin
126.        dis_sel = op_1;
127.    end
128.end
129.
130.always  @(posedge clk or negedge rst_n)begin
131.    if(rst_n==1'b0)begin
132.        dis_sel_tmp <= 0;
133.    end
134.    else begin
135.        dis_sel_tmp <= dis_sel;
136.    end
137.end
138.
139.
140.div div_prj(
141.            .clock      (clk        )   ,
142.            .numer      (dis_tmp    )   ,
143.            .denom      (10         )   ,
144.            .quotient   (div_quo    )   ,
145.            .remain     (div_rem    )
146.            );
147.
148.always  @(posedge clk or negedge rst_n)begin
149.    if(rst_n==1'b0)begin
150.        flag_add <= 0;
151.    end
152.    else if(dis_sel!=dis_sel_tmp || result_neg!=rssult_neg_tmp)begin
153.        flag_add <= 1;
154.    end
155.    else if(end_dis_cnt)begin
156.        flag_add <= 0;
157.    end
158.end
159.
160.
161.always @(posedge clk or negedge rst_n) begin 
162.    if (rst_n==0) begin
163.        dis_cnt <= 0; 
164.    end
165.    else if(add_dis_cnt) begin
166.        if(end_dis_cnt)
167.            dis_cnt <= 0; 
168.        else
169.            dis_cnt <= dis_cnt+1 ;
170.   end
171.end
172.assign add_dis_cnt = flag_add && (dis_sel==dis_sel_tmp && result_neg==result_neg_tmp);
173.assign end_dis_cnt = add_dis_cnt  && dis_cnt == 10-1 ;
174.
175.
176.assign  dis_tmp = add_dis_cnt && dis_cnt==1 ? dis_sel : div_quo;
177.
178.always  @(posedge clk or negedge rst_n)begin
179.    if(rst_n==1'b0)begin
180.        display <= 4'b0;
181.    end
182.    else if(state_c==ERROR)begin
183.        display[4*(dis_cnt)-1 -:4] <= 4'b1111;
184.    end
185.    else if(end_dis_cnt && result_neg==1 && state_c!=OP_2)begin
186.        display[31:28] <= 4'b1010;
187.    end
188.    else begin
189.        display[4*(dis_cnt-1)-1 -:4] <= div_rem;
190.    end
191.end
192.
193.
194.always  @(posedge clk or negedge rst_n)begin
195.    if(rst_n==1'b0)begin
196.        display_vld <= 0;
197.    end
198.    else begin
199.        display_vld <= (dis_cnt==0 && (dis_sel==dis_sel_tmp)) ? 1'b1 : 1'b0;
200.    end
201.end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

1.9 数码管显示模块设计

1.9.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Display输入显示数据输入信号
Display_vld输入显示数据有效指示信号
Seg_sel输出数码管位选信号
Segment输出数码管段选信号

1.9.2 设计思路

本模块主要实现的功能是对显示对象选择模块的显示数据输出信号(display)进行数码管显示。
1、复位后,数码管默认显示运算数1;
2、当result_err有效时,数码管显示8个F;
3、当result_neg有效时,第8个数码管显示“—”;
4、数码管显示display;

由于数码管显示在前面已有案例介绍,所以这个就不做介绍。感兴趣的同学可以看一下往期的文章:【每周FPGA案例】至简设计系列_7段数码管显示

1.9.3 参考代码

1.always @(posedge clk or negedge rst_n) begin 
2.    if (rst_n==0) begin
3.        count_20us <= 0; 
4.    end
5.    else if(add_count_20us) begin
6.        if(end_count_20us)
7.            count_20us <= 0; 
8.        else
9.            count_20us <= count_20us+1 ;
10.   end
11.end
12.assign add_count_20us = 1;
13.assign end_count_20us = add_count_20us  && count_20us == TIME_20US-1 ;
14.
15.
16.always @(posedge clk or negedge rst_n) begin 
17.    if (rst_n==0) begin
18.        sel_cnt <= 0; 
19.    end
20.    else if(add_sel_cnt) begin
21.        if(end_sel_cnt)
22.            sel_cnt <= 0; 
23.        else
24.            sel_cnt <= sel_cnt+1 ;
25.   end
26.end
27.assign add_sel_cnt = end_count_20us;
28.assign end_sel_cnt = add_sel_cnt  && sel_cnt == SEG_NUM-1 ;
29.
30.
31.
32.always  @(posedge clk or negedge rst_n)begin
33.    if(rst_n==1'b0)begin
34.        seg_sel <= {SEG_NUM{1'b1}};
35.    end
36.    else begin
37.        seg_sel <= ~(1'b1 << sel_cnt);
38.    end
39.end
40.
41.always  @(posedge clk or negedge rst_n)begin
42.    if(rst_n==1'b0)begin
43.        display_ff0 <= 0;
44.    end
45.    else begin
46.        for(ii=0;ii<SEG_NUM;ii=ii+1)begin
47.            if(display_vld==1)begin
48.                display_ff0[(ii+1)*4-1 -:4] <= display[(ii+1)*4-1 -:4];
49.            end
50.            else begin
51.                display_ff0[(ii+1)*4-1 -:4] <= display_ff0[(ii+1)*4-1 -:4];
52.            end
53.        end
54.    end
55.end
56.
57.always  @(*)begin
58.    seg_tmp = display_ff0[(sel_cnt+1)*4-1 -:4]; 
59.end
60.
61.
62.always  @(posedge clk or negedge rst_n)begin
63.    if(rst_n==1'b0)begin
64.        segment <= NUM_0;
65.    end
66.    else begin
67.        case(seg_tmp)
68.            0 :segment <=NUM_0  ; 
69.            1 :segment <=NUM_1  ; 
70.            2 :segment <=NUM_2  ; 
71.            3 :segment <=NUM_3  ; 
72.            4 :segment <=NUM_4  ; 
73.            5 :segment <=NUM_5  ; 
74.            6 :segment <=NUM_6  ; 
75.            7 :segment <=NUM_7  ; 
76.            8 :segment <=NUM_8  ; 
77.            9 :segment <=NUM_9  ; 
78.            10:segment <=NUM_10 ;
79.           default:segment <= NUM_ERR;
80.       endcase 
81.    end
82.end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

1.10 蜂鸣器模块设计

1.10.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Op_1_err输入运算数1溢出信号,高电平有效
Op_2_err输入运算数2溢出信号,高电平有效
Result_err输入运算结果错误信号,高电平有效
Beep输出蜂鸣输出信号,高电平有效

1.10.2 设计思路

该模块的主要功能是根据接收到的各个错误指示信号,进行报警提示。当接收到错误信号有效的时候,蜂鸣器报警,持续1秒的时间,因此提出一个计数器的架构,如下图所示:
在这里插入图片描述

主要由时钟计数器cnt_1s和蜂鸣器输出组成,下面时两个信号的设计思路:
时钟计数器cnt_1s:该计数器的作用是计时1秒的时间。加一条件为flag_add,表示进入报警状态的时候便开始计数。结束条件为数5000_0000个,系统时钟为50M,一个时钟周期为20ns,5000_0000个时钟周期就是1秒。
蜂鸣器输出信号beep:初始状态为1,表示不报警。从1变0的条件为op_1_err || op_2_err || result_err,表示接收到这些错误指示信号之后,开始报警。从0变1的条件为end_cnt_1s,表示报警时间持续1秒,之后结束。

1.10.3 参考代码

1.always  @(posedge clk or negedge rst_n)begin
2.    if(rst_n==1'b0)begin
3.        flag_add <= 0;
4.    end
5.    else if(op_1_err || op_2_err || result_err)begin
6.        flag_add <= 1;
7.    end
8.    else if(end_cnt_1s)begin
9.        flag_add <= 0;
10.    end
11.end
12.
13.
14.always @(posedge clk or negedge rst_n) begin 
15.    if (rst_n==0) begin
16.        cnt_1s <= 0; 
17.    end
18.    else if(add_cnt_1s) begin
19.        if(end_cnt_1s)
20.            cnt_1s <= 0; 
21.        else
22.            cnt_1s <= cnt_1s+1 ;
23.   end
24.end
25.assign add_cnt_1s = flag_add;
26.assign end_cnt_1s = add_cnt_1s  && cnt_1s == CNT_1S-1 ;
27.
28.
29.always  @(posedge clk or negedge rst_n)begin
30.    if(rst_n==1'b0)begin
31.        beep <= 1'b1;
32.    end
33.    else if(flag_add)begin
34.        beep <= 1'b0;
35.    end
36.    else begin
37.        beep <= 1'b1;
38.    end
39.end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

1.11 效果和总结

1.11.1 db603开发板

由于计算器的演示是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
在这里插入图片描述

1.11.2 ms980试验箱

由于计算器的演示是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
在这里插入图片描述

感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/791739
推荐阅读
相关标签
  

闽ICP备14008679号