[FPGA] state machine write key jitter elimination

Posted by shuka79 on Wed, 08 Dec 2021 07:53:29 +0100

1, Principle of state machine

State machine (FSM), also known as finite state machine
One stage state machine
The one-stage state machine seems to solve all logic (including input, output and state) in one always. This writing method seems very concise, but it is often not conducive to maintenance. This writing method is not recommended, but it can still be used in some simple state machines.
Two stage state machine
Two stage state machine is a common writing method. It divides sequential logic and combinatorial logic. The current logic and the next logic are switched in sequential logic, and each input and output and state judgment are realized in combinatorial logic. This writing method is relatively easy to maintain, but the combinatorial logic output is prone to common problems such as burrs.
Three stage state machine
The writing method of three-stage state machine is a recommended writing method. The code is easy to maintain. The output of sequential logic solves the burr problem of combinatorial logic in the two-stage writing method. However, in terms of resource consumption, three-stage consumes more resources. In addition, three-stage input will delay one clock cycle from input to output than one-stage. The three-stage state machine separates sequential logic from combinational logic, and separates state from output, which is clear and easy to understand.

My recommendation is to write a three-stage state machine

2, Design ideas

Key debounce is a relatively simple module with four states
1. Idle (initial state)
2.K_ D (key pressed state)
3.H_ D (press and hold the key to stabilize the value)
4.K_ U (state of key Pop-Up) (this is optional)

Status diagram:

State transition diagram:

Then think about the jump conditions of these States, which is nothing more than the 20ms delay of edge detection and key shaking elimination

assign idle2down = (state_c == IDLE) && nedge;//Falling edge detected
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//When the timing is less than 20ms and the rising edge appears, it indicates that the key jitters unexpectedly and returns to the initial state
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//When the timing reaches 20ms, there is no rising edge sign, and it remains stable after pressing the key
assign hold2up   = (state_c == HOLD) && (pedge);//Jump from rising edge to rising state detected
assign up2idle   = (state_c == UP)   && end_cnt_20ms;//The counter counts to 20ms and jumps to the initial state

The rest of the logic is the same as the usual key shaking. You can see the code of my previous blog

[FPGA] key shake elimination in actual combat

3, Code part

The code here is based on the code of my classmates. It's too messy when my code is written. Anyway, this key anti shake module directly copies the code when it needs to be used. The instantiation is finished. Use the key inside_ Just the out signal.

module fsm_key_debounce # (parameter KEY_W = 3,TIME_20MS = 1_000_000)(
    input 			            clk		,
    input 			            rst_n	,
    input 		[KEY_W - 1:0]	key_in	,

    output 		[KEY_W - 1:0]	key_out	 
);
//Parameter definition
localparam IDLE  = 4'b0001;//Initial state 
localparam DOWN  = 4'b0010;//Key press jitter
localparam HOLD  = 4'b0100;//Stable after pressing the key
localparam UP    = 4'b1000;//Key up jitter
//Signal definition
reg [3:0] state_c;//Present state
reg [3:0] state_n;//Secondary state

//State transition condition definition
wire idle2down;
wire down2idle;
wire down2hold;
wire hold2up  ;
wire up2idle  ;

reg [KEY_W - 1:0] key_r0;//synchronization
reg [KEY_W - 1:0] key_r1;//Beat
wire [KEY_W - 1:0] nedge;//Falling edge
wire [KEY_W - 1:0] pedge;//Rising edge

//20ms counter
reg [19:0] cnt_20ms;
wire add_cnt_20ms;
wire end_cnt_20ms;

reg [KEY_W - 1:0] key_out_r;//Output register

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

always@(*)begin
    case(state_c)
        IDLE:begin
            if(idle2down)begin
                state_n = DOWN;
            end
            else begin
                state_n = state_c;
            end
        end
        DOWN:begin
            if(down2idle)begin
                state_n = IDLE;
            end
            else if(down2hold)begin
                state_n = HOLD;
            end
            else begin
                state_n = state_c;
            end
        end
        HOLD:begin
            if(hold2up)begin
                state_n = UP;
            end
            else begin
                state_n = state_c;
            end
        end
        UP:begin
            if(up2idle)begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end
        default:state_n = state_c;
    endcase
end

assign idle2down = (state_c == IDLE) && nedge;//Falling edge detected
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//When the timing is less than 20ms and the rising edge appears, it indicates that the key jitters unexpectedly and returns to the initial state
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//When the timing reaches 20ms, there is no rising edge sign, and it remains stable after pressing the key
assign hold2up   = (state_c == HOLD) && (pedge);//Jump from rising edge to rising state detected
assign up2idle   = (state_c == UP)   && end_cnt_20ms;//The counter counts to 20ms and jumps to the initial state
//20ms counter
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 0;
    end
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)begin
            cnt_20ms <= 0;
        end
        else begin
            cnt_20ms <= cnt_20ms + 1'b1;
        end
    end
end
assign add_cnt_20ms = state_c == DOWN || state_c == UP;//Start counting when the key is pressed or bounced
assign end_cnt_20ms = add_cnt_20ms && ((cnt_20ms == TIME_20MS - 1) || pedge);//When the maximum value is counted or a rising edge is detected, the counter is cleared



//Synchronous beat
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= {KEY_W{1'b1}};
        key_r1 <= {KEY_W{1'b1}};
    end
    else begin
        key_r0 <= key_in;
        key_r1 <= key_r0;
    end
end

assign nedge = ~key_r0 &  key_r1;//Detect falling edge
assign pedge =  key_r0 & ~key_r1;//Detect rising edge

//Key assignment
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_out_r <= {KEY_W{1'b0}};
    end
    else if(state_c == HOLD && hold2up)begin
        key_out_r <= ~key_r1;
    end
    else begin
        key_out_r <= {KEY_W{1'b0}};
    end
end
assign key_out = key_out_r;

endmodule

4, Simulation verification

There is no verification here. You can still go to my previous blog simulation, which is nothing more than adding 4 states and 4 state transition conditions.

[FPGA] key shake elimination in actual combat

Topics: FPGA