AM93
AM93

Reputation: 55

Finite state machine VHDL reset

I am new to VHDL and I have a question about the implementation of a FSM. I would like the behaviour shown in the picture (where I implemented the same FSM with AHDL). When I implement it in VHDL I have a different behaviour of the reset : if it detects reset=1 and at the same time there is a rising edge the FSM does not go on but it keeps on putting PS at S0. I know the problem is that if... elsif (it detects right the 1st condition and does not enter in the 2nd I suppose). I have tried in many different ways but still is not working and the output stays at 00 also after the 1st rising edge.

Waveforms of AHDL implementation: Waveforms of AHDL implementation

Waveforms of VHDL implementation: Waveforms of VHDL implementation

LIBRARY ieee; -- Bibliotheksvereinbarung
USE ieee.std_logic_1164.all;

ENTITY sumconvol IS -- Schaltungssymbol
PORT
(
    x : IN STD_LOGIC; --input of 1st FF
    clk : IN STD_LOGIC; --clock of all the 3 FFs
    clrn : IN STD_LOGIC;
    y : OUT STD_LOGIC_VECTOR (1 downto 0) --vector of output data
);
END sumconvol;

ARCHITECTURE a OF sumconvol IS -- Creation of architecture
    --SIGNAL output_tmp : STD_LOGIC_VECTOR (1 downto 0); -- temporary variables (e.g. input/output between FFs)7
    TYPE state_type IS (S0,S1,S2,S3);
    SIGNAL NS,PS : state_type;
    SIGNAL stato : STD_LOGIC;

BEGIN
    sync_proc: PROCESS (clk,clrn)
    BEGIN
        if ((clrn='1')) THEN 
            PS<=S0;
            y <= "00";
        elsif (rising_edge(clk)) then 
            PS <= NS;
            CASE PS IS
            when S0 => 
                if ((x='0'))then
                        NS <= S0;
                        y <= "00";
                    else 
                        NS <= S1;
                        y <= "11";
                end if;
            when S1 => 
                if (x='0') then
                    NS <= S2;
                    y<="10";
                else 
                    NS <= S3;
                    y <= "01";
                end if;
            when S2 => 
                if (x='0') then
                    NS <= S0;
                    y <="11";
                else 
                    NS <= S1;
                    y <= "00";
                end if;
            when S3 => 
                if (x='0') then
                    NS <= S2;
                    y <="01";
                else 
                    NS <= S3;
                    y <= "10";
                end if;
            end case;
        end if;
    end process sync_proc;
END a;

Upvotes: 3

Views: 2544

Answers (1)

JHBonarius
JHBonarius

Reputation: 11261

One thing you might not noticed, is that you put both PS (previous state) and NS (next state) in a clocked process. That means registers are inferred for both signals. Thus, NS will be set to PS one clock later that you would probably expect. This can be solved two ways:

1) remove the PS->NS part, and just use state.

sync_proc: PROCESS (clk, clr)
BEGIN
    if clr = '1' THEN 
        state <= S0;
        y <= "00";
    elsif rising_edge(clk) then 
        CASE state IS
        when S0 => 
            if x = '0' then
                    state <= S0;
                    y <= "00";
                else 
                    state <= S1;
                    y <= "11";
            end if;
        when S1 => 
            if x = '0' then
                state <= S2;
                y<="10";
            else 
                state <= S3;
                y <= "01";
            end if;
        when S2 => 
            if x = '0' then
                state <= S0;
                y <="11";
            else 
                state <= S1;
                y <= "00";
            end if;
        when S3 => 
            if (x='0') then
                state <= S2;
                y <="01";
            else 
                state <= S3;
                y <= "10";
            end if;
        end case;
    end if;
end process sync_proc;

2) separate the process into a clocked and a combinatorial process.

clk_proc: PROCESS (clk, clr)
BEGIN
    if clr = '1' THEN 
        PS <= S0;
        y <= "00";
    elsif rising_edge(clk) then 
        PS <= NS;
        y <= next_y;
    end if;
end process;

comb_proc : process(PS, x)
begin
    CASE PS IS
        when S0 => 
            if x = '0' then
                    NS <= S0;
                    next_y <= "00";
                else 
                    NS <= S1;
                    next_y <= "11";
            end if;
        when S1 => 
            if x = '0' then
                NS <= S2;
                next_y <= "10";
            else 
                NS <= S3;
                next_y <= "01";
            end if;
        when S2 => 
            if x = '0' then
                NS <= S0;
                next_y <="11";
            else 
                NS <= S1;
                next_y <= "00";
            end if;
        when S3 => 
            if x = '0' then
                NS <= S2;
                next_y <="01";
            else 
                NS <= S3;
                next_y <= "10";
            end if;
    end case;
end process;

Next, I don't understand what you want with reset. The VHDL code is doing exactly what it should do. This is the proper way to use a reset: as long as the reset is asserted, y should display "00". Then, once it is deasserted, y should change on the next clock edge. That is proper design. What the first (AHDL) picture shows is not good: activity of y during reset.

But anyhow, if you are really stubborn, you can get the behavior that is in the first image using some tricks.

sync_proc: PROCESS (clk)
BEGIN
    if (rising_edge(clk)) then 
        if clr = '1' THEN 
            state <= S0;
            y <= "11";
        else
            case state is
[...]

p.s. you are calling the process sync_proc, as in "synchronous process". But this is not true, as the reset in your code is asynchronous...

p.s.2: give your signals some proper names, instead of e.g. x...

Upvotes: 4

Related Questions