FSM issues due to hidden race hazards with LUTs

I encountered infrequent sporadic failures of input buttons where the machine stopped responding to button presses. After thinking this through and looking at my FSM logic, I realized that my signal that chose the next state of the debouncer logic was susceptible to glitches.

My FSM picked the next signal based on a series of triggers - if the current state is X and event Y occurs, the new state should be Z. At the end , if none of the conditions matched, it would make the new state equal to the current state. Some of the conditions could change near the clock edge, particularly the external button input.

Knowing that the 'compiler' created LUTs to represent the current and next states, with some input conditions as the lookup address, I realized that I could very rarely have a glitch that generated a unintended address. The result was some output that became the new state. If that was not one of the explicit states I had defined, then no transition would exist from this invisible state to any of the known states. It would sit forever in the hidden state, ignoring all future button pushes.

The problem was not simply the rare invalid state, but the code that took whatever state the machined and continued it unless specific transition conditions were matched. This was what kept it locked up.

My FSM might have code like
                  nextstate <= state2     when currentstate = state1 and conditionA = '1' else
                                     state3     when currentstate = state2 and conditionB = '1' else
                                     currentstate;

 I decided it prudent to remove the catchall of remaining in the current state, whatever value of the FSM state variable that might be, since the variable can reach values other than the ones I listed.

My replacement code looked more like this:
             nextstate <= state2    when currentstate = state1 and conditionA = '1' else
                                state1     when currentstate = state1   else
                                state3    when currentstate = state2 and conditionB = '1' else
                               state2     when currentstate = state2 else
                              state1;

Note that I explicitly set the continuation of a current state after covering all the transitions that can happen out of that state. Note that my final catchall is the most reasonable state to enter if the FSM gets into a weird condition. There is no way it can continue forever in some hidden state. If it reaches a hidden state, the next cycle puts it into state1 again.

That ended the problems with the debounced inputs. I planned to make the same type of change to my other FSMs as I updated and tested circuits, to remove the potential for unreliable states and lockups.

No comments:

Post a Comment