解题思路
按照常规三段式,即可解题。
状态转移图
常规三段式,但值得注意的是,有两种饮料输出,相当于可以看成有两个状态机,所以画了两个状态转移图,分别是1.5元的和2,5元的,分别如下:
注:图中输入的钱用两位表示,高位表示1元,低位表示0.5元,一次只能投一个硬币;输出两位,高位表示出零钱0.5元,低位表示出饮料。
①对于饮料1,售价1.5元的:
②对于饮料2,售价2.5元的:
代码细节
①和单饮料的售货机相比,只有第二段和第三段有差别,即多了一个sel选择信号来分支,所以相等于double了一下。
②中间段组合逻辑中没有输入硬币时,不能直接给原状态,而是赋予next_state来保持,我百度有说是因为在clk=0时,钱输入为0,此时处于default状态,要让next_state保持不变,而不是重置,也可能是输入只有半个周期的缘故,这里我不太懂,欢迎大家评论交流。
③这种有冗余状态的状态机,第3段需要用next_state而不是state。
代码实现
`timescale 1ns/1ns
module seller2(
input wire clk ,
input wire rst ,
input wire d1 , //0.5元
input wire d2 , //1元
input wire sel , //高电平选饮料2,2.5元的
output reg out1 , //输出饮料1
output reg out2 , //输出饮料2
output reg out3 //输出0.5元零钱
);
reg [6:0] state, next_state;
parameter IDLE = 7'b000_0001;
parameter HALF = 7'b000_0010;
parameter ONE = 7'b000_0101;
parameter ONE_HALF = 7'b000_1000;
parameter TWO = 7'b001_0000;
parameter TWO_HALF = 7'b010_0000;
parameter THREE = 7'b100_0000;
always@(posedge clk or negedge rst) begin
if(!rst)
state <= IDLE;
else
state <= next_state;
end
always@(*) begin
if(sel) begin //饮料2,2.5元的
case(state) //d1表示投入0.5元,d2表示投入1元
IDLE : next_state = d1 ? HALF : (d2 ? ONE : next_state );
HALF : next_state = d1 ? ONE : (d2 ? ONE_HALF : next_state );
ONE : next_state = d1 ? ONE_HALF : (d2 ? TWO : next_state );
ONE_HALF: next_state = d1 ? TWO : (d2 ? TWO_HALF : next_state );
TWO : next_state = d1 ? TWO_HALF : (d2 ? THREE : next_state );
TWO_HALF,
THREE : next_state = IDLE;
default : next_state = IDLE;
endcase
end
else begin
case(state)
IDLE : next_state = d1 ? HALF : (d2 ? ONE : next_state );
HALF : next_state = d1 ? ONE : (d2 ? ONE_HALF : next_state );
ONE : next_state = d1 ? ONE_HALF : (d2 ? TWO : next_state );
ONE_HALF,
TWO ,
TWO_HALF,
THREE : next_state = IDLE;
default : next_state = IDLE;
endcase
end
end
//注意:这种三段式输出要看next_state而不是state!
always@(posedge clk or negedge rst) begin
if(!rst) begin
out1 <= 1'b0;
out2 <= 1'b0;
out3 <= 1'b0;
end
else if(sel) begin //饮料2, 2.5元的
out1 <= 1'b0;
if(next_state == TWO_HALF) begin
out2 <= 1'b1;
out3 <= 1'b0;
end
else if(next_state == THREE) begin
out2 <= 1'b1;
out3 <= 1'b1;
end
else begin
out2 <= 1'b0;
out3 <= 1'b0;
end
end
else begin //饮料1, 1.5元的
out2 <= 1'b0;
if(next_state == ONE_HALF) begin
out1 <= 1'b1;
out3 <= 1'b0;
end
else if(next_state == TWO) begin
out1 <= 1'b1;
out3 <= 1'b1;
end
else begin
out1 <= 1'b0;
out3 <= 1'b0;
end
end
end
endmodule