FPGA-Coke machine expansion training questions (state machine)

Topic: With the Coke machine as the background, the price of a bottle of Coke is still 2.5 yuan. Use the buttons to control the coin insertion (with button debounce function), you can insert 0.5 yuan coins and 1 yuan coins, one light will light up after 0.5 yuan is put in, 2 lights will light up after 1 yuan is put in, 3 lights will light up after 1.5 yuan is put in, and After 2 yuan, 4 lights will be on. If the coin operation is not continued for 10 seconds after the coin is inserted, the coke machine will return to the initial state. After putting in 2.5 yuan, the coke will be given out without change. At this time, the LED light will realize one-way flow operation, and it will stop automatically after 10 seconds; after 3 yuan is put in, the coke will be given change. At this time, the LED light will realize two-way flow operation, and the flow will stop automatically after 10 seconds. There is a reset button, whose function is to terminate this coin-operating operation, so that the coke machine returns to the initial state immediately.

Apply the three-element method to analyze:

Input: no coin, 0.5 coin, 1 coin;

Output: No Coke/No Change, Coke/No Change, Coke/Change;

Status: 0 yuan in the Coke machine, 0.5 yuan in the Coke machine, 1 yuan in the Coke machine, 1.5 yuan in the Coke machine, 2 yuan in the Coke machine, 2.5 yuan in the Coke machine , There is 3 yuan in the Coke machine.

1. Module block diagram

2. State transition diagram

3. RTL code

3.1 Key debounce part

Realize button debounce, note that there are two inputs so it is called twice. The effect is that when it is detected that the key is pressed, the output key_flag is pulled high for one clock, and is input to the next module as a flag signal.

`timescale 1ns/1ns

module key_filter
#(
    parameter CNT_MAX = 20'd999_999
)
(
    input wire sys_clk,
    input wire sys_rst_n,
    input wire key_in,

    output reg key_flag
                                    
);

reg [19:0] cnt_20ms ; //counter

//cnt_20ms: If the rising edge of the clock detects that the value of the external key input is low, the counter starts counting
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else if(cnt_20ms == CNT_MAX & amp; & amp; key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;

//key_flag: When the count is over 20ms, the valid key bit will be generated
//And key_flag is pulled high at 999_999, maintaining a high level of a clock
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

3.2 complex_fsm part

Realize various display effects of small lights after state switching. Note that the conditions that affect the state switching are valid for 10 seconds in addition to coin insertion. Use two types of always blocks that use sequential logic. The first type of always block describes the transition of state (state) as the first state machine, and the second type of always block describes the output of data as the second state machine, so write the state machine Generally, a two-stage approach is used.

Summarize the main parts of the applied format: the first part is the port list part; the second part is the state code part; the third part is the defined state variable; the fourth part is divided into the first state machine and the second part Two-stage state machine. There are four parts in total. When writing the state machine code, write it in sequence according to this format, which can be realized very easily.

`timescale 1ns/1ns

module complex_fsm
#(
    parameter CNT_MAX = 25'd24_999_999, //0.5s timer timing
    parameter CNT_KEY = 20'd999_999 //20ns button stable state
)
(
    input wire sys_clk,
    input wire sys_rst_n,
    input wire pi_money_one , //Coin 1 yuan
    input wire pi_money_half , //coin 0.5 yuan
                         
    output reg [3:0] led
);

//There are only seven states, using one-hot code
parameter IDLE = 7'b0000001;
parameter HALF = 7'b0000010;
parameter ONE = 7'b0000100;
parameter ONE_HALF = 7'b0001000;
parameter TWO = 7'b0010000;
parameter TWO_HALF = 7'b0100000;
parameter THREE = 7'b1000000;

reg [6:0] state ;

reg[3:0] led_a;
reg[3:0] led_b;
reg[3:0] led_c;

reg[24:0] cnt;
reg [4:0] cnt_10s = 5'd21;

reg move;

reg po_money;
reg po_cola;
                              
wire [1:0] pi_money;
wire po_money_half;
wire po_money_one;

wire cola_flag;

//pi_money: In order to reduce the number of variables, use bit splicing to splice the input two 1bit signals into one 2bit signal
//Coin input methods can be: no coin (00), 0.5 yuan (01), 1 yuan (10), only one coin at a time
assign pi_money = {po_money_one, po_money_half};

//Satisfy all the conditions of the coke
assign cola_flag = (state == TWO & amp; & amp; pi_money == 2'b01) || (state == TWO & amp; & amp;
          pi_money == 2'b10) || (state == ONE_HALF & amp; & amp; pi_money == 2'b10);
\t\t  
//The first state machine, how does the current state state jump to the next state according to the input
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;
    else case(state)
                IDLE : if(pi_money == 2'b01) //There are several jumps if there are several inputs
                              state <= HALF;
                          else if(pi_money == 2'b10)
                              state <= ONE;
                          else
                              state <= IDLE;
    
                HALF : if(pi_money == 2'b01)
                              state <= ONE;
                          else if(pi_money == 2'b10)
                              state <= ONE_HALF;
                          else if(cnt_10s == 5'd20)
                              state <= IDLE;
    
                ONE : if(pi_money == 2'b01)
                              state <= ONE_HALF;
                          else if(pi_money == 2'b10)
                              state <= TWO;
                          else if(cnt_10s == 5'd20)
                              state <= IDLE;
    
                ONE_HALF: if(pi_money == 2'b01)
                              state <= TWO;
                          else if(pi_money == 2'b10)
                              state <= TWO_HALF;
                          else if(cnt_10s == 5'd20)
                              state <= IDLE;
    
                TWO : if(pi_money == 2'b01)
                              state <= TWO_HALF;
                          else if(pi_money == 2'b10)
                              state <= THREE;
                          else if(cnt_10s == 5'd20)
                              state <= IDLE;
    
                TWO_HALF: if(cnt_10s == 5'd20) //No coin for 10 seconds
                              state <= IDLE;
                          else if(pi_money == 2'b01) //Continue to invest 0.5 yuan
                              state <= HALF;
else if(pi_money == 2'b10) //Continue to invest 1 yuan
state <= ONE;
\t\t\t\t\t     
      
                THREE : if(cnt_10s == 5'd20) //There is no coin for 10 seconds
                              state <= IDLE;
                          else if(pi_money == 2'b01) //Continue to invest 0.5 yuan
                              state <= HALF;
else if(pi_money == 2'b10) //Continue to invest 1 yuan
state <= ONE;
\t\t\t\t\t\t\t  
                default : state <= IDLE;
            end case

//The second state machine, cnt: counter counts 500ms
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 25'b0;
    else if(cnt == CNT_MAX)
        cnt <= 25'b0;
    else if(cnt_10s != 5'd21) //start timing
        cnt <= cnt + 1'b1;
//The second state machine, 10s counter
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_10s <= 5'd21;
    else if((pi_money != 2'd0) //start timing
        cnt_10s <= 5'd0;
    else if(cnt == CNT_MAX)
        cnt_10s <= cnt_10s + 1'b1;
    else if(cnt_10s == 5'd20)
        cnt_10s <= 5'd0;
//The second state machine, led_a: coin control led state
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_a <= 4'b1111;
    else if(state == TWO)
        led_a <= 4'b0000;
    else if(state == ONE_HALF)
        led_a <= 4'b0001;
    else if(state == ONE)
        led_a <= 4'b0011;
    else if(state == HALF)
        led_a <= 4'b0111;
    else if(state == IDLE)
        led_a <= 4'b1111;
//The second state machine, led_b:led one-way circulation flow
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_b <= 4'b0001;
    else if(led_b == 4'b1000 & amp; & amp; cnt == CNT_MAX)
        led_b <= 4'b0001;
    else if(cnt == CNT_MAX)
        led_b <= led_b << 1'b1; //shift left
//The second state machine, led_c: led two-way circulation flow
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_c <= 4'b0001;
    else if(move == 1'b0 & amp; & amp; cnt == CNT_MAX)
        led_c <= led_c >>1'b1; //shift right
    else if(move == 1'b1 & amp; & amp; cnt == CNT_MAX)
        led_c <= led_c <<1'b1; //shift left
    else
        led_c <= led_c;
//led circulation flow enable signal
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        move <= 1'b0;
    else if(led_c == 4'b1000)
        move <= 1'b0;
    else if(led_c == 4'b0001)
        move <= 1'b1;
\t\t
//Control the led state according to the state machine
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led <= 4'b1111;
    else if((state == THREE) & amp; & amp;(cnt_10s != 5'd20))
        led <= led_c;
    else if((state == TWO_HALF) & amp; & amp;(cnt_10s != 5'd20))
        led <= led_b;
    else
        led <=~led_a;
\t\t
//The second state machine, describing how the current state state and the input pi_money affect the po_cola output
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_cola <= 1'b0;
    else if(cola_flag == 1'b1)
        po_cola <= 1'b1;
    else
        po_cola <= 1'b0;
//The second state machine, describing how the current state and input pi_money affect the po_money output
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_money <= 1'b0;
    else if((state == TWO) & amp; & amp; (pi_money == 2'b10))
        po_money <= 1'b1;
    else
        po_money <= 1'b0;

\t\t
//Call two button modules separately
key_filter
#(
    .CNT_MAX(CNT_KEY)
)
key_filter_inst1
(
    .sys_clk (sys_clk ),
    .sys_rst_n (sys_rst_n ),
    .key_in(pi_money_half),

    .key_flag (po_money_half)
);
key_filter
#(
    .CNT_MAX(CNT_KEY)
)
key_filter_inst2
(
    .sys_clk (sys_clk ),
    .sys_rst_n (sys_rst_n ),
    .key_in(pi_money_one),

    .key_flag (po_money_one)

);
endmodule

5. Summary

1. Write the steps and format of the state machine.

2. How to draw a state transition diagram and grasp the “three elements”.

3. Process the LED output and output it after classification according to the display effect.