AlexThunder
AlexThunder

Reputation: 168

VHDL State machine stucks in an impossible state

I wrote a VHDL State Machine. It works some time until it gets stuck on an impossible state.

There are 7 states in total, but only two of them interesting for us. The state graph is simple:

IDLE2 <=> IDLE <=> Other states

In words, it means that initial state is IDLE. All other states can be reached through IDLE state. State IDLE2 can only jump to the IDLE state.

Fragment of the code:

ENTITY MC_PLD_vhd IS
    PORT
    (
        --INPUTS
        MC_WR: IN STD_LOGIC;
        MC_RD: IN STD_LOGIC;
        Tstart: IN STD_LOGIC;
        CLK: IN STD_LOGIC;
        --OUTPUTS
        PLD_WR: OUT STD_LOGIC;
        PLD_RD: OUT STD_LOGIC;
        STS: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
    );
END MC_PLD_vhd;

ARCHITECTURE MC_PLD_vhd_dff OF MC_PLD_vhd IS
    TYPE tSTATE is 
    (
        IDLE   --0
        ,REC1  --1
        ,SEND1 --2
        --I omit some not significant states
        ,IDLE2 --6
         
    );
    SIGNAL STATE :tSTATE := IDLE;
BEGIN
    PROCESS(CLK) IS
    BEGIN
        IF RISING_EDGE(CLK) THEN
            STS <= std_logic_vector(to_unsigned(tSTATE'pos(STATE), 8));
            CASE STATE IS
                WHEN IDLE =>
                    IF Tstart = '1' THEN
                        PLD_WR <= '1';
                        PLD_RD <= '0';
                        STATE <= SEND1; -- <!> stuck
                    ELSE
                        IF MC_WR = '1' THEN 
                            PLD_RD <= '1';
                            PLD_WR <= '0';
                            STATE <= REC1;
                        ELSE
                            PLD_WR <= '0';
                            PLD_RD <= '0';
                            STATE <= IDLE2;
                        END IF;
                    END IF;
                WHEN IDLE2 =>
                        PLD_WR <= '0';
                        PLD_RD <= '0';
                        STATE <= IDLE;
                WHEN IsReceiving_wait_RD_rise_1 => <...>
-- Other left away, as they don't add any information to my question.

As you may notice, the IDLE state is not 'stable'. Every cycle the machine must leave the IDLE state, even if nothing is happening: IDLE jumps to IDLE2, and from IDLE2 it returns to IDLE the next clock cycle. And it works pretty well!

Also you may notice that I output STATE through STS independently from state. In Altera SignalTap I see normal operation: states jumps back and forth, and enters to other states at appropriate input events.

PROBLEM

Suddenly after many cycles of normal operation I see (in SignalTap) that STS = 0 and doesn't change in time. This means that STATE = IDLE, and the machine does NOT jump to IDLE2 (STS = 6). And it does NOT jump to any state by any input event, it's just stuck in state IDLE. Judging by outputs (PLD_WR = 1 and PLD_RD = 0) the code is stuck near the comment <!> stuck in the code above, but STATE <= SEND1 doesn't occur.

How can this be possible??

Disclaimer: The state IDLE2 and the output STS were implemented for debugging this particular problem.

Upvotes: 2

Views: 1317

Answers (1)

AlexThunder
AlexThunder

Reputation: 168

Surprisingly, I managed to solve my own problem just after I posted this question, despite the fact that I struggled with it a several days!

ANSWER: I change switch-enum block to if-integer.

    PROCESS(CLK) IS
        CONSTANT STATE_IDLE :INTEGER := 0;
        CONSTANT STATE_RECEIVE :INTEGER := 1;
        --I omit other states
        CONSTANT STATE_IDLE2 :INTEGER := 6;
        VARIABLE STATE :INTEGER := 0;
    BEGIN
        IF RISING_EDGE(CLK) THEN
            STS <= std_logic_vector(to_unsigned(STATE, 8));
            IF STATE = STATE_IDLE THEN
                IF Tstart = '1' THEN
                    PLD_WR <= '1';
                    PLD_RD <= '0';
                    STATE := STATE_SEND_wait_RD;
                ELSE
                    IF MC_WR = '1' THEN 
                        PLD_RD <= '1';
                        PLD_WR <= '0';
                        STATE := STATE_RECEIVE;
                    ELSE
                        PLD_WR <= '0';
                        PLD_RD <= '0';
                        STATE := STATE_IDLE2;
                    END IF;
                END IF;
        ELSIF STATE = STATE_IDLE2 THEN
            PLD_WR <= '0';
            PLD_RD <= '0';
            STATE := STATE_IDLE;
        ELSIF STATE = STATE_RECEIVE THEN 
--Unsignificant states omitted

Further, I cut out debugging STS output and IDLE2 state because they are no longer needed. And it still works, fortunately!

But I'm still wondering why switch-enum does not properly work. May be it's compiler fault, I use "good old" Quartus II 8.1 Web

Upvotes: 0

Related Questions