Search code examples
verilogfsm

States are moving too fast, I use pushbuttons in basys-2


I am simulating water vending machine. Now, we have 4 push-buttons under a seven-segment decoder. From left to right, the first 3 buttons will be used for increasing the cent total. The first one will increase the total by 25 cents, the second button will increase total by 10 cents and third button will increase the total by 5 cents. Finally, the fourth button will be used to reset the count.

The problem is: when I press the 5, 10 or 25 cent buttons, the total instantly goes to 50 cents and dispenses water.

Here is my code:

module DispenserFSM(clk, rst, five, ten, twentyfive, current, dispense);
input clk, rst;
input five, ten, twentyfive;

output [5:0] current;
reg [5:0] current;

output dispense;
reg dispense;

// state encoding
parameter S0= 4'b0000,
             S1= 4'b0001,
             S2= 4'b0010,
             S3= 4'b0011,
             S4= 4'b0100,
             S5= 4'b0101,
             S6= 4'b0110,
             S7= 4'b0111,
             S8= 4'b1000,
             S9= 4'b1001,
             S10= 4'b1010;

reg [3:0] state, nextState;

// state register logic
always@(posedge clk, posedge rst)
    if(rst == 1)
        state <= S0;
    else
        state <= nextState;

// combinational circuit
always@(state, five, ten, twentyfive)
    case(state)
        S0: begin
        if(five == 1)
            nextState <= S1;
        else if(ten == 1)
            nextState <= S2;
        else if(twentyfive == 1)
            nextState <= S5;
        else
            nextState <= S0;
        end

        S1: begin
        if(five == 1)
            nextState <= S2;
        else if(ten == 1)
            nextState <= S3;
        else if(twentyfive == 1)
            nextState <= S6;
        else
            nextState <= S1;
        end

        S2: begin
        if(five == 1)
            nextState <= S3;
        else if(ten == 1)
            nextState <= S4;
        else if(twentyfive == 1)
            nextState <= S7;
        else
            nextState <= S2;
        end

        S3: begin
        if(five == 1)
            nextState <= S4;
        else if(ten == 1)
            nextState <= S5;
        else if(twentyfive == 1)
            nextState <= S8;
        else
            nextState <= S3;
        end

        S4: begin
        if(five == 1)
            nextState <= S5;
        else if(ten == 1)
            nextState <= S6;
        else if(twentyfive == 1)
            nextState <= S9;
        else
            nextState <= S4;
        end

        S5: begin
        if(five == 1)
            nextState <= S6;
        else if(ten == 1)
            nextState <= S7;
        else if(twentyfive == 1)
            nextState <= S10;
        else
            nextState <= S5;
        end

        S6: begin
        if(five == 1)
            nextState <= S7;
        else if(ten == 1)
            nextState <= S8;
        else if(twentyfive == 1)
            nextState <= S10;
        else
            nextState <= S6;
        end

        S7: begin
        if(five == 1)
            nextState <= S8;
        else if(ten == 1)
            nextState <= S9;
        else if(twentyfive == 1)
            nextState <= S10;
        else
            nextState <= S7;
        end

        S8: begin
        if(five == 1)
            nextState <= S9;
        else if(ten == 1)
            nextState <= S10;
        else if(twentyfive == 1)
            nextState <= S10;
        else
            nextState <= S8;
        end

        S9: begin
        if(five == 1)
            nextState <= S10;
        else if(ten == 1)
            nextState <= S10;
        else if(twentyfive == 1)
            nextState <= S10;
        else
            nextState <= S9;
        end

        S10: begin
            nextState <= S10;
        end
    endcase

// output logic
always@(state)
    case(state)
        S0: begin
        current = 6'b000000;
        dispense = 1'b0;
        end

        S1: begin
        current = 6'b000101;
        dispense = 1'b0;
        end

        S2: begin
        current = 6'b001010;
        dispense = 1'b0;
        end

        S3: begin
        current = 6'b001111;
        dispense = 1'b0;
        end

        S4: begin
        current = 6'b010100;
        dispense = 1'b0;
        end

        S5: begin
        current = 6'b011001;
        dispense = 1'b0;
        end

        S6: begin
        current = 6'b011110;
        dispense = 1'b0;
        end

        S7: begin
        current = 6'b100011;
        dispense = 1'b0;
        end

        S8: begin
        current = 6'b101000;
        dispense = 1'b0;
        end

        S9: begin
        current = 6'b101101;
        dispense = 1'b0;
        end

        S10: begin
        current = 6'b110010;
        dispense = 1'b1;
        end
    endcase

    endmodule

and ucf

NET "clk" LOC = "B8";
NET "rst" LOC = "G12";

NET "five" LOC = "A7";
NET "ten" LOC = "M4";
NET "twentyfive" LOC = "C11";

NET "current[5]" LOC = "G1";
NET "current[4]" LOC = "P4";
NET "current[3]" LOC = "N4";
NET "current[2]" LOC = "N5";
NET "current[1]" LOC = "P6";
NET "current[0]" LOC = "P7";

NET "dispense" LOC = "M5";

Solution

  • The problem you are running into is that you are assuming pushing down the button onces results in the machine seeing the given button being pushed once. However, you are probably running off a clock running at something like 50 Mhz. Thus, when you push the button down for even half a second, the machine sees the button pressed for millions of cycles (25000000 cycles for a 50 Mhz clock to be exact). Thats way more than enough time to reach your final state and then some.

    You need to change how you handle the inputs, either by adding states to you machine to handle waiting for the button to go back up or adding a module to convert the raw button input into a pulsed input. Heres an example of doing the later in a module:

    module pb_pulse(input btn,
                    input clk,
                    output pbPulse);
    
      reg [1:0] btnState;
    
      // If we see a edge now, but didnt 1 cycle ago, pulse
      assign pbPulse = btnState[0] & ~btnState[1];
    
      // Shift register storing the btn state now and 1 cycle ago
      always @(posedge clk) begin
        btnState <= {btnState[0], btn};
      end
    endmodule
    

    Now you can use the pbPulse output just as you are using five, ten and twentyfive in your FSM presently (ie, button is connected to a line going into btn and one of those signals connects to pbPulse, note youll need three such modules).

    A few other notes, you should not use always @(state, five, ten, twentyfive) (ie, explicit sensitivity lists), use always @(*) instead. Also, do not use NBA (<=) in combinational blocks (ie, ones that arent always @(posedge clk)) nor blocking assignment (=) in sequential blocks (ie, ones that ARE always @(posedge clk)).