解题思路

按照常规三段式,即可解题。

状态转移图

常规三段式,但值得注意的是,有两种饮料输出,相当于可以看成有两个状态机,所以画了两个状态转移图,分别是1.5元的和2,5元的,分别如下:

注:图中输入的钱用两位表示,高位表示1元,低位表示0.5元,一次只能投一个硬币;输出两位,高位表示出零钱0.5元,低位表示出饮料。

①对于饮料1,售价1.5元的:

alt

②对于饮料2,售价2.5元的: alt

代码细节

①和单饮料的售货机相比,只有第二段和第三段有差别,即多了一个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