Can't get simple Bit Sequence Recognizer circuit to work (FSM)

421 views Asked by At

This is a simple exercise for a Hardware course that I am taking. We are required to use Altera Quartus II and ModelSim to test the implementation; tools that I've never used before, so I might be missing something, and my explanations, lacking.

The circuit has 3 inputs (Data, Clock and Reset) and 2 outputs (Locked, Error). The sequence used in this exercise is 10001.

The problem ask to design a circuit that will recognize a sequence of bits. When the correct sequence is entered, you are granted access (the circuit enters the "UNLOCK" state; Locked output is 0). Otherwise, if at any point you enter the wrong bit, you trigger an alarm and you're supposed to remain in the "ERROR" state until the circuit is manually reset.

"Locked" is always 1 unless it gets to the "UNLOCK" state. "Error" is always 0 unless it gets to the "ERROR" state.

The circuit is supposed to always start out in a "RESET" state. Once it gets in the "UNLOCK" state, it stays there as long as the bits provided are 1, or goes to "RESET" if a 0 is encountered.

This is the state diagram that I've worked out:

enter image description here

Any help or ideas are welcome!

It turned out that almost all the logic behind my implementation is correct, the problem was a misunderstanding in using the CLRNs on the flip-flops and a typo in one of the assignments. As such, most of the images were removed to get rid of the clutter.

-- EDIT 1

With the following code (which should be correct), the waveform is not as expected (at least the lock is not)

LIBRARY ieee;
USE ieee.std_logic_1164.all; 

entity dlock is 
    port
    (
        DATA  :  IN   STD_LOGIC;
        RESET :  IN   STD_LOGIC;
        CLOCK :  IN   STD_LOGIC;
        LOCK  :  OUT  STD_LOGIC;
        ALARM :  OUT  STD_LOGIC
    );
end dlock;

architecture bdf_type of dlock is 

type STATE_type is (S_RESET, S1, S2, S3, S4, UNLOCK, S_ALARM);
signal state : STATE_type := S_RESET;

begin

process (clock) is
begin
  if (rising_edge(clock)) then
    -- `reset` always puts us back in the reset state
    if (RESET = '1') then
      state <= S_RESET;
    else
      case state is
        when S_RESET =>
          -- Reset; lock active and alarm off
          LOCK  <= '1';
          ALARM <= '0';
          if (DATA = '1') then
            -- Correct bit, proceed to next state
            state <= S1;
          else
            -- Incorrect bit; ALARM
            state <= S_ALARM;
          end if;
        when S1 => 
          if (DATA = '0') then
            state <= S2;
          else
            state <= S_ALARM;
          end if;
        when S2 => 
          if (DATA = '0') then
            state <= S3;
          else
            state <= S_ALARM;
          end if;
        when S3 => 
          if (DATA = '0') then
            state <= S4;
          else
            state <= S_ALARM;
          end if;
        when S4 => 
          if (DATA = '1') then
            state <= UNLOCK;
          else
            state <= S_ALARM;
          end if;
        when UNLOCK =>
          -- Lock inactive!
          LOCK <= '0';
          if (data = '0') then
            state <= S_RESET;
          else
            state <= UNLOCK;
          end if;
        when S_ALARM =>
          -- Alarm active in ALARM state
          ALARM <= '1';
      end case;
    end if;
  end if;
end process;
end bdf_type;
2

There are 2 answers

8
QuantumRipple On BEST ANSWER
  1. Your reset, as written in the VHDL, is active low. This means you're holding the circuit in reset most of the time. Your data pattern looks like you thought your reset was active high.

  2. Your error signal, insofar as I can see in the image of the waveform posted, is behaving correctly. Every time you exit reset for a cycle, your data is 0, which sends you to the error state. Of course this only persists for one cycle since you immediately reset again.

  3. These are just glitches, if you zoom in you'll see that the phantom unlocks are happening for 0 time (or very small time periods depending on your gate models). This is one reason why the output of combinational logic isn't used for clocking data. Passing the value through a flip-flop will remove glitches.

EDIT: Furthermore, your state assignment table and your state output table disagree with each other. One lists the Q values from Q2 downto Q0 and the other lists from Q0 to Q2, but both list the unlocked state as "110". This doesn't cause issues for the Error state since "111" reads the same forwards and backwards.

EDIT2: As far as avoiding glitches... glitches are the nature of combinational logic.

You could have locked sourced directly from a flop without adding latency by having the input to a "locked" flop be dictated by the same preconditions of the unlocked state (i.e. locked_d = not((state=s4 or state=locked) and data=1) and use locked_q.

You could just avoiding having locked be a function of multiple state bits by converting the state machine machine encoding to a one-hot or hybrid one-hot (where there is a dedicated bit for the locked/error states because they drive output bits , but the other 5 states use 3 shared state bits).

Think of a state table like this:

Q4 Q3 Q2 Q1 Q0   State
 0  1  0  0  0   Reset
 0  1  0  0  1   S1
 0  1  0  1  0   S2
 0  1  0  1  1   S3
 0  1  1  0  0   S4
 0  0  X  X  X   Unlock
 1  1  X  X  X   Error
 1  0  X  X  X   X
 0  1  1  0  1   X
 0  1  1  1  X   X

Where Q4 is your error bit and Q3 is your locked bit

That said, avoiding glitches is usually not important because they don't cause problems when used in sequential logic as the D inputs or clock enables.

8
scary_jeff On

I would say you have made your life needlessly more difficult with your approach. You don't need these D and Q signals at all, just code the state machine exactly as you see it in the excellent diagram at the start of your question. I haven't written the full code, but this should show the basic approach that leads to a minimal, easy to read result:

type STATE_type is (S_RESET, S1, UNLOCK, ERROR);
signal state : STATE_type := S_RESET;

...

process (clock) is
begin
  if (rising_edge(clock)) then
    -- `reset` always puts us back in the reset state
    if (reset = '1') then
      state <= S_RESET;
    else
      case state is
        when S_RESET =>
          -- Reset; lock active and alarm off
          lock <= '1';
          alarm <= '0';
          if (data = '1') then
            -- Correct bit, proceed to next state
            state <= S1;
          else
            -- Incorrect bit; error
            state <= ERROR;
          end if;
        when S1 => 
          if (data = '0') then
            state <= UNLOCK;
          else
            state <= ERROR;
          end if;
        when UNLOCK =>
          -- Lock inactive!
          lock <= '0';
          if (data = '0') then
            state <= RESET;
          end if;
        when ERROR =>
          -- Alarm active in error state
          alarm <= '1';
      end case;
    end if;
  end if;
end process;

You should easily be able to add the other states S2 and onward to this.


If you need the lock and alarm to change state as soon as reset is asserted, you should implement an asynchronous reset, instead of the synchronous reset in the example above:

  if (reset = '1') then
    state <= S_RESET;
    alarm <= '0';
    lock <= '1';
  elsif (rising_edge(clock)) then
    case state is
      -- `when` statements
    end case;
  end if;

Another advantage of writing it this way is that you can easily make the required pattern a constant:

constant PATTERN : std_logic_vector(0 to 4) := "10001";

Then your data comparisons in the various states would look like:

when S_RESET =>
  if (data = PATTERN(0)) then

...

when S1 =>
  if (data = PATTERN(1)) then

etc. You can then alter the required pattern with a simple one-line change.