设计状态机时有一个很好用的问题:
"当前输入消失以后,我还需要记住什么信息?"
实现自动售卖机功能,A饮料5元钱,B饮料10元钱,售卖机可接收投币5元钱和10元钱,每次投币只可买一种饮料,考虑找零的情况。
电路的接口如下图所示。sel信号会先于din信号有效,且在购买一种饮料时值不变。
-
sel为选择信号,用来选择购买饮料的种类,sel=0,表示购买A饮料,sel=1,表示购买B饮料;
-
din表示投币输入,din=0表示未投币,din=1表示投币5元,din=2表示投币10元,不会出现din=3的情况;
-
drinks_out表示饮料输出,drinks_out=0表示没有饮料输出,drinks_out=1表示输出A饮料,drinks_out=2表示输出B饮料,不出现drinks_out =3的情况,输出有效仅保持一个时钟周期;
-
change_out表示找零输出,change_out=0表示没有找零,change_out=1表示找零5元,输出有效仅保持一个时钟周期。
`timescale 1ns/1ns
module sale(
input clk,
input rst_n,
input sel,
input [1:0] din,output reg [1:0] drinks_out, output reg change_out);
reg state;
localparam S_ZERO = 1'b0; // 当前余额0元
localparam S_FIVE = 1'b1; // 当前余额5元always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin state <= S_ZERO; drinks_out <= 2'd0; change_out <= 1'b0; end else begin // 输出只保持一个时钟周期 drinks_out <= 2'd0; change_out <= 1'b0; case(state) //============================ // 当前没有余额 //============================ S_ZERO: begin // 购买A饮料:5元 if (sel == 1'b0) begin // 投5元 if (din == 2'd1) begin drinks_out <= 2'd1; end // 投10元 else if (din == 2'd2) begin drinks_out <= 2'd1; change_out <= 1'b1; end end // 购买B饮料:10元 else begin // 投5元 if (din == 2'd1) begin // 钱不够,记住已经有5元 state <= S_FIVE; end // 投10元 else if (din == 2'd2) begin drinks_out <= 2'd2; end end end //============================ // 已经投入5元 // 这里实际上只可能是在购买B饮料 //============================ S_FIVE: begin // 再投入5元 if (din == 2'd1) begin // 5 + 5 = 10 drinks_out <= 2'd2; state <= S_ZERO; end // 再投入10元 else if (din == 2'd2) begin // 5 + 10 = 15 // 买B饮料10元,找5元 drinks_out <= 2'd2; change_out <= 1'b1; state <= S_ZERO; end end default: begin state <= S_ZERO; end endcase endend
endmodule