学艺不精的我又来记录了(doge
有何错误以及需要改进的地方希望大家不吝指出!
先看题目:
实验内容:
实验一个可以实现连续加法功能的计算器
具体要求如下:
1) 输入100MHz时钟,由EGO1板通过FPGA管脚直接输入;
2) 电路可实现连续加法功能,例如 A+B+C+……=S。其中A、B、C均为一位十进制数,采用8421码方式输入。加数的个数不超过10个,即S不超过100。但超过两位数时,显示后两位,舍弃百位。
3) 加数输入通过拨4开关SW3-SW0输入,SW3为MSB,通过按键开关确定输入。加号和等号及清零等由按键开关输入,输入数据及加法结果显示在最右侧两个数码管上。如下图所示。
(出题:北京理工大学 张延军老师)
实验思路:
①状态机的设计
解决本题的关键是如何设计状态机。为此可以从加法的过程入手,按下确认键后,电路读入数;按下加法,显示读入的数据;按下确认,读入加数;按下等号,显示结果…其实,可以将整个过程视为电路在“等待”哪个按键的过程。确认键之后,加数输入完毕,就在等待加号或等号;加号之后,下一步一定是输入另一个加数,因此在等待确认键;等号之后,等待确认键或者加号:如果是确认键,那么要清空计算结果,进行新一轮加法;如果是加号,则在这个基础上继续计算。当然,在任何时候按下清零键,都会清空所有结果,进入“清零”状态。
所以,状态机共有四个状态:
i.清零状态(clear),所有数据都是0;
ii.等待加号键或等号键状态(w_f_p_e,wait for plus or equal),在按下确认键之后会进入此状态;
iii.等待确认键状态(w_f_c,wait for confirm),在按下加号键之后会进入此状态;
iv.等待加号键或确认键状态(w_f_p_c,wait for plus or confirm),在按下等号键之后会进入该状态。
状态转换图如下:
这样设计状态机的优点是:过程容易考虑,规定了某一步之后的下一步是什么,其他按键不会影响当前状态的变化,因此避免了混乱。
这样设计的缺点是:状态发生变化后的下一个时钟上升沿才执行计算,这样造成的问题是:由于输出是由状态决定的(设计为摩尔型),那么就有一个时钟周期的时间(也就是10ns),输出是错误的。但是由于时间很短,实际上并不会影响最终的显示效果。
状态机部分代码如下:
1. //状态寄存器
2. always @(posedge clk) begin
3. if (clear_button)
4. current_state <= clear;
5. else
6. current_state <= next_state;
7. end
8.
9.
10. //次态的组合逻辑
11. always @* begin
12. case (current_state)
13. clear: begin
14. if (confirm_button) next_state = w_f_p_e;
15. else next_state = clear;
16. end
17. w_f_p_e: begin
18. if (plus_button) next_state = w_f_c;
19. else if (equal_button) next_state = w_f_p_c;
20. else next_state = w_f_p_e;
21. end
22. w_f_c: begin
23. if (confirm_button) next_state = w_f_p_e;
24. else next_state = w_f_c;
25. end
26. w_f_p_c:begin
27. if (plus_button) next_state = w_f_c;
28. else if (confirm_button) next_state = w_f_p_e;
29. else next_state = w_f_p_c;
30. end
31. default: next_state = 2'bxx;
32. endcase
33. end
②加法的设计
这也是本题的另一个关键之处。
加法的基本公式是:当前结果=输入+上次运算的结果。假设暂存结果的变量为result_temp,在本题中不能用result_temp <= result_temp + …的形式,因为result_temp是时序逻辑,写成自加会时钟的每一个上升沿都进行计算。
**试着用另一个变量去储存“上次运算的结果”,**因为输入是比较简单的。
因为按下确认键之后,进入wfpe状态,即下一个有效的键一定是加号或者等号,所以下一步一定是计算操作(除去清零,清零的优先级最高),因此在wfpe状态就将下一步的结果计算出来,在wfpe的第一个周期input_temp(暂存输入值的变量)还没变,但input_temp早晚会成为正确值,因此plus_result_temp
也是早晚会成为有效值。而plus_result_temp = result_temp +input_temp,即上次的结果和当前输入之和。
在摁下加号或者等号之后,进入的wfc或wfpc状态后把plus_result_temp赋给result_temp,这时的result_temp就是我们希望的值,并且在此后,result_temp又成为了“上次运算的结果”,供下次plus计算所用。当然,plus在wfc和wfpc的状态中是不能变更的。
此外,还有一个重要的问题:那便是当结果超过三位数之后,只显示后两位。因此加法的判断过程应当是:首先判断当前输入和result_temp中低4位加法的结果是否大于9,如果大于9那么高四位一定会发生进位,因此还要判断高四位加1后是否大于9,如果二者都大于9,说明加完的结果大于100,应该加0110_0110。
此外,在按下等号后如果按下确认键,则要进行新一轮加法。按照此前状态机的设计,这一点无法达到,必须利用另一个变量来满足要求,若检测到在wfpc(也就是按完等号的状态)的状态中按下了确认键,那么暂存结果的result_temp和plus_result_temp都要清零。
计算模块完整代码如下:
1. module calculate(
2. input clk,
3. //input rst_n,
4. //四个按键
5. input clear_button,
6. input confirm_button,
7. input plus_button,
8. input equal_button,
9. //输入加数
10. input [3:0] number,
11. //输出结果 8位8421数
12. output reg [7:0] result
13. );
14.
15. //暂存结果
16. reg [7:0] result_temp;
17. reg [3:0] input_temp;
18. reg [7:0] plus_result_temp;
19.
20. //此变量仅用于在摁下等号之后,再摁确认键将暂存结果清零,进行新一轮加法
21. reg if_clear_result;
22.
23.
24. initial begin
25. result_temp = 8'b0;
26. plus_result_temp = 8'b0;
27. input_temp = 4'b0;
28. end
29.
30. //定义状态
31. parameter w_f_p_e = 2'b00; //处于等待加号或等号状态 wait for plus or equal
32. parameter w_f_c = 2'b01; //处于等待确认键状态 wait for confirm
33. parameter w_f_p_c = 2'b10; //处于等待加号或确认键状态 wait for plus or confirm
34. parameter clear = 2'b11; //清零状态 clear
35.
36. reg [1:0] current_state;
37. reg [1:0] next_state;
38.
39. //状态寄存器
40. always @(posedge clk) begin
41. if (clear_button)
42. current_state <= clear;
43. else
44. current_state <= next_state;
45. end
46.
47.
48. //次态的组合逻辑
49. always @* begin
50. case (current_state)
51. clear: begin
52. if (confirm_button) next_state = w_f_p_e;
53. else next_state = clear;
54. end
55. w_f_p_e: begin
56. if (plus_button) next_state = w_f_c;
57. else if (equal_button) next_state = w_f_p_c;
58. else next_state = w_f_p_e;
59. end
60. w_f_c: begin
61. if (confirm_button) next_state = w_f_p_e;
62. else next_state = w_f_c;
63. end
64. w_f_p_c:begin
65. if (plus_button) next_state = w_f_c;
66. else if (confirm_button) next_state = w_f_p_e;
67. else next_state = w_f_p_c;
68. end
69. default: next_state = 2'bxx;
70. endcase
71. end
72.
73. //input_temp
74. always @(posedge clk) begin
75. if (current_state == clear)
76. input_temp <= 4'b0;
77. else if (current_state == w_f_p_e && confirm_button) //只有在wfpe状态时更新的input_temp才有效
78. input_temp <= number;
79. else if (current_state == w_f_p_c && equal_button) //按下等号后input_temp要清零
80. input_temp <= 4'b0;
81. end
82.
83. //if_clear_result
84. always @ * begin
85. if (current_state == w_f_p_c && confirm_button) //在等号之后摁下确认键,且状态切换前
86. if_clear_result = 1'b1;
87. else
88. if_clear_result = 1'b0;
89. end
90.
91. /*
92. 关于plus_result_temp和result_temp:
93. 加法的基本公式是:当前结果=输入+上次运算的结果
94. 在本题中不能用result_temp <= result_temp + ...的形式,因为result_temp是时序逻辑,写成自加会每一个上升沿都进行计算。
95. 试着用另一个变量去储存“上次运算的结果”,因为输入是比较简单的。plus_result_temp就是这个功能。
96. 因为按下确认键之后,进入wfpe状态,即下一个有效的键一定是加号或者等号,所以下一步一定是计算操作(除去清零,清零的优先级最高),
97. 因此在wfpe状态就将下一步的结果计算出来,在wfpe的第一个周期input_temp还没变,但input_temp早晚会成为正确值,因此plus_result_temp
98. 也是早晚会成为有效值。而plus_result_temp = result_temp +input_temp,即上次的结果和当前输入之和。
99. 在摁下加号或者等号之后,进入的wfc或wfpc状态后把plus_result_temp赋给result_temp,这时的result_temp就是我们希望的值,并且在
100. 此后,result_temp又成为了“上次运算的结果”,供下次plus计算所用。当然,plus在wfc和wfpc的状态中是不能变更的。
101. */
102. //plus_result_temp
103. always @(posedge clk) begin
104. if (current_state == clear)
105. plus_result_temp <= 8'b0;
106. else if (if_clear_result == 1'b1)
107. plus_result_temp <= 8'b0;
108. else if (current_state == w_f_p_e) begin
109. if (({4'b0000,input_temp} + {4'b0000,result_temp[3:0]} > 8'd9) && (result_temp[7:4] + 1'b1 > 4'b1001)) //这是加数达到三位数的情况,例如99+9
110. plus_result_temp <= result_temp + {4'b0000,input_temp} + 8'b0110_0110;
111. else if (({4'b0000,input_temp} + {4'b0000,result_temp[3:0]} > 8'd9) && (result_temp[7:4] + 1'b1 <= 4'b1001)) //这是需要进位但加数不到三位数的情况
112. plus_result_temp <= result_temp + {4'b0000,input_temp} + 8'b0000_0110;
113. else
114. plus_result_temp <= result_temp + {4'b0000,input_temp};
115. end
116.
117. end
118.
119. //result_temp
120. always @(posedge clk) begin
121. if (current_state == clear)
122. result_temp <= 8'b0;
123. else if (if_clear_result == 1'b1)
124. result_temp <= 8'b0;
125. //在输入加号或者等号的时候才进行计算,但加号只在wfc状态有效,等号只在wfpc状态有效
126. else if (current_state == w_f_c && plus_button)
127. result_temp <= plus_result_temp;
128. else if (current_state == w_f_p_c && equal_button)
129. result_temp <= plus_result_temp;
130. end
131.
132. //输出result
133. always @ * begin
134. if (current_state == clear)
135. result = 8'b0;
136. else if (current_state == w_f_p_e)
137. result = {4'b0000,input_temp};
138. else if (current_state == w_f_c || current_state == w_f_p_c)
139. result = result_temp;
140. else
141. result = 8'b0;
142. end
143. endmodule // calculate
控制数码管段选和顶层模块就略去啦!