1
votes

I'm trying to do a handshake protocol for 2 modules in quartus II. First I used a simple IF-ELSE architecture to do this and it works, now I'm trying to adapt this architecture of IF-ELSE to a state machine. The circuito works basically like this: the PRODUCER module sends a 4-bit signal to the CONSUMER module, the CONSUMER module waits the REQ signal that comes from the PRODUCER, when he REQ signal is '1' a "RDY" led needs to be activated, when the user tightens de RCV button de output from the CONSUMER shows the 4-bit data received from the PRODUCER on 4 leds, and sends a confirmation signal to the producer by an output call ACK. The state machine from the PRODUCER module it's working correctly, but when I simulate the CONSUMER state machine the outputs doesn't works.

Below the two codes from the PRODUCER and CONSUMER module:

PRODUCER:

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.std_logic_unsigned.ALL;
use IEEE.std_logic_arith.ALL;

ENTITY produtor IS
  GENERIC(W : NATURAL := 4);
  PORT (o_RDY   : OUT  BIT; 
        i_SND   : IN  BIT;
        i_DIN   : IN  STD_LOGIC_VECTOR(W-1 DOWNTO 0);
        o_DOUT  : OUT  STD_LOGIC_VECTOR(W-1 DOWNTO 0);
        o_REQ   : OUT BIT;
        i_ACK   : IN BIT);
END produtor;

ARCHITECTURE arch_1 OF produtor IS
TYPE state_type IS (s0, s1);
SIGNAL state_reg : state_type;
SIGNAL next_state: state_type;

BEGIN
p_state_reg: PROCESS(i_SND,i_DIN,i_ACK)
  BEGIN
    IF (i_ACK ='0') THEN
      state_reg <= s0;
    ELSIF (i_ACK ='1') THEN
      state_reg <= next_state;
    END IF;
  END PROCESS;


 p_next_state: PROCESS(state_reg,i_SND,i_ACK)
  BEGIN
    CASE (state_reg) IS
      WHEN s0 => IF (i_ACK = '1') THEN 
                   next_state <= s1;
                 ELSIF (i_ACK = '0') THEN 
                  next_state <= s0;
                 END IF;

      WHEN s1 => IF (i_SND ='1') THEN 
                   next_state <= s1;
                 ELSIF (i_SND='0') THEN
                   next_state <= s0;
                 END IF;
   WHEN OTHERS=> next_state <= s0;
   END CASE;
  END PROCESS;

o_DOUT <= i_DIN WHEN (state_reg = s1);
o_REQ <= '1' WHEN (state_reg = s1) ELSE '0';
o_RDY <= '0'  WHEN (state_reg = s1) ELSE '1';

END arch_1;

CONSUMER:

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.std_logic_unsigned.ALL;
use IEEE.std_logic_arith.ALL;

ENTITY consumidor IS
  GENERIC(W : NATURAL := 4);
  PORT (o_RDY   : OUT  BIT;
        i_RCV   : IN  BIT;
        i_DIN   : IN  STD_LOGIC_VECTOR(W-1 DOWNTO 0); 
        o_DOUT  : OUT  STD_LOGIC_VECTOR(W-1 DOWNTO 0); 
        i_REQ   : IN BIT;  
        o_ACK   : OUT BIT);
END consumidor;

ARCHITECTURE arch_1 OF consumidor IS

TYPE state_type IS (s0, s1, s2);
SIGNAL state_reg : state_type;
SIGNAL next_state: state_type;

BEGIN
p_state_reg: PROCESS(i_RCV,i_REQ,i_DIN)
  BEGIN
    IF (i_REQ ='0') THEN
      state_reg <= s0;
    ELSIF (i_REQ ='1') THEN
      state_reg <= next_state;
    END IF;
  END PROCESS;


 p_next_state: PROCESS(state_reg,i_RCV,i_REQ,i_DIN)
  BEGIN
    CASE (state_reg) IS
      WHEN s0 => IF (i_REQ = '1') THEN 
                   next_state <= s1;
                 ELSIF (i_REQ = '0') THEN 
                  next_state <= s0;
                 END IF;
                 o_RDY <= '1';
                 o_ACK <= '0';
      WHEN s1 => IF (i_RCV ='1') THEN 
                   next_state <= s2;
                 ELSIF (i_RCV='0') THEN
                   next_state <= s0;
                 END IF;
                  o_RDY <= '0'; 
                 o_ACK <= '1';
      WHEN s2 =>  o_DOUT <= i_DIN;
                  o_ACK <= '0';
                  o_RDY <= '0';
                  next_state <= s0;



   WHEN OTHERS=> next_state <= s0;
   END CASE;
  END PROCESS;

--o_DOUT <= i_DIN WHEN (state_reg = s2);
--o_ACK <= '1' WHEN (state_reg = s1) ELSE '0';
--o_RDY <= '1'  WHEN (state_reg = s0) ELSE '0';
END arch_1;

I used three states to do the state machine of consumer:

         s0 --> Ready to receive  
         s1 --> Waiting authorization to receive (authorization is send by the RCV input)
         s2 --> Receiving

* The two modules are connected by a BDF file using wires.

The architecture IF-ELSE of the CONSUMER module below:

ARCHITECTURE arch_1 OF consumidor IS

BEGIN
  PROCESS(i_RCV, i_DIN, i_REQ)
  BEGIN
    IF (i_REQ = '1') THEN
        o_RDY <= '1';
    ELSE
        o_RDY <= '0';
    END IF;
    IF (i_RCV = '1') THEN
        o_DOUT <= i_DIN;
        o_ACK <= '1';
    ELSE
        o_ACK <= '0';
    END IF;
  END PROCESS;
END arch_1;

The error is shown in the images below:

1) Error with the producer-consumer state machine on the outputsenter image description here:

2) Simulation working with the PRODUCER using state machine architecture and consumer module using IF-ELSE architecture:

enter image description here

3) BDF file conecting the two modules:

enter image description here

If the architecture IF-ELSE of the PRODUCER module is necessary to solve this, It's below:

ARCHITECTURE arch_1 OF produtor IS
SIGNAL entrada : STD_LOGIC_VECTOR (W-1 DOWNTO 0);
BEGIN
  PROCESS(i_SND,i_DIN,i_ACK)
  BEGIN
    IF (i_ACK = '1') THEN
        o_RDY <= '1';
    ELSE
        o_RDY <= '0';
    END IF;
    IF (o_RDY = '1') THEN
       IF (i_DIN(0) = '1') THEN
          entrada(0) <= '1';
       END IF;
       IF (i_DIN(0) = '0') THEN
          entrada(0) <= '0';
       END IF;
       IF (i_DIN(1) = '1') THEN
          entrada(1) <= '1';
       END IF;
       IF (i_DIN(1) = '0') THEN
          entrada(1) <= '0';
       END IF;
       IF (i_DIN(2) = '1') THEN
          entrada(2) <= '1';
       END IF;
       IF (i_DIN(2) = '0') THEN
          entrada(2) <= '0';
       END IF;
       IF (i_DIN(3) = '1') THEN
          entrada(3) <= '1';
       END IF;
       IF (i_DIN(3) = '0') THEN
          entrada(3) <= '0';
       END IF;
       IF (i_SND = '1') THEN
          o_DOUT <= entrada;
          o_REQ <= '1';
          o_RDY <= '0';
       ELSE
          o_REQ <= '0';
          o_RDY <= '1';
       END IF;
    END IF;
  END PROCESS;
END arch_1;

I believe the error is on the state machine of consumer, after doing this state machine the simulation doesn't works anymore.


                            *****UPDATE*****

Changing the circuit to a Synchronous mode with Clock and Reset Entrance. Now the simulation works but the Leds and the output stay always with the same value...

The new architecture

CONSUMER:

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.std_logic_unsigned.ALL;
use IEEE.std_logic_arith.ALL;

ENTITY consumidor IS
  GENERIC(W : NATURAL := 4);
  PORT (o_RDY   : OUT  BIT;-- data input
        i_RST   : IN BIT;
        i_CLK   : IN STD_ULOGIC;
        i_RCV   : IN  BIT;-- data input
        i_DIN   : IN  STD_LOGIC_VECTOR(W-1 DOWNTO 0);  -- clock
        o_DOUT  : OUT  STD_LOGIC_VECTOR(W-1 DOWNTO 0);  -- clear
        i_REQ   : IN BIT;  -- enable
        o_ACK   : OUT BIT);-- data output
END consumidor;

ARCHITECTURE arch_1 OF consumidor IS

TYPE state_type IS (s0, s1, s2);
SIGNAL stateT : state_type;


BEGIN
PROCESS(i_CLK)
    BEGIN
        IF rising_edge(i_CLK) THEN
          IF (i_RST = '1') THEN
            CASE stateT IS
                WHEN s0 => IF (i_REQ = '0') THEN 
                                stateT <= s0;
                           ELSE
                                stateT <= s1;
                            END IF;
                WHEN s1 => IF (i_RCV = '1') THEN
                                stateT <= s2;
                           ELSE 
                                stateT <= s0;
                           END IF;
                WHEN s2 => stateT <= s0;
             END CASE;
           END IF;
        END IF;
END PROCESS;

o_DOUT <= i_DIN WHEN (stateT = s2);
o_ACK <= '1' WHEN (stateT = s1) ELSE '0';
o_RDY <= '1'  WHEN (stateT = s0) ELSE '0';

END arch_1;

PRODUCER:

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.std_logic_unsigned.ALL;
use IEEE.std_logic_arith.ALL;

ENTITY produtor IS
  GENERIC(W : NATURAL := 4);
  PORT (
        i_RST   : IN BIT;
        i_ACK   : IN BIT;
        i_CLK   : IN STD_ULOGIC;
        i_SND   : IN  BIT;-- data input
        i_DIN   : IN  STD_LOGIC_VECTOR(W-1 DOWNTO 0);  -- clock
        o_DOUT  : OUT  STD_LOGIC_VECTOR(W-1 DOWNTO 0);  -- clear
        o_REQ   : OUT BIT;  -- enable
        o_RDY   : OUT  BIT);-- data output
END produtor;

ARCHITECTURE arch_1 OF produtor IS
TYPE state_type IS (s0, s1, s2);
SIGNAL stateT : state_type;

BEGIN
PROCESS(i_CLK)
    BEGIN
        IF rising_edge(i_CLK) THEN
          IF (i_RST = '1') THEN
            CASE stateT IS
                WHEN s0 => IF (i_ACK = '0') THEN 
                                stateT <= s0;
                           ELSE
                                stateT <= s1;
                            END IF;
                WHEN s1 => IF (i_SND = '1') THEN
                                stateT <= s2;
                           ELSE 
                                stateT <= s0;
                           END IF;
                WHEN s2 => stateT <= s0;
             END CASE;
           END IF;
        END IF;
END PROCESS;


o_DOUT <= i_DIN WHEN (stateT = s2);
o_REQ <= '1' WHEN (stateT = s1) ELSE '0';
o_RDY <= '1'  WHEN (stateT = s0) ELSE '0';

END arch_1;

The Clock and Reset signal are the same for the two modules in BDF. Now the simulation goes like this:

Simulation

What's happening now to the output stay with XXXX value, the logic of the two modules seems to be correct.

1
When you simulate with a Vector Waveform File (VWF), Quartus-II actually simulates the behavior of the synthesized netlist as described in my answer here. The synthesizer reports warnings about latches for state and next_state (both state machines) and o_DOUT (consumidor). As long as these warnings persist, the behavior of the synthesized netlist may differ from the VHDL description. You will have to fix the warnings first before worrying about the simulation result. Do you really want to save the current state in a latch? - Martin Zabel
Martin, I was trying to do this asychronous, but if latch becomes a problem I'm going to do this sychronous, I just need to use a clock signal, but I really don't know where I'm going to put the state machine in the code using a global Clock signal for the consumer and producer module. How can I do this? Thanks for the attention! - user5410082
I have tried to reproduce your simulation with both state machines, but the synthesizer always crashes after issuing the latch warnings. To make both units synchronous, you will have to replace the p_state_reg process with pastebin.com/aarkTqxJ . Now two more inputs i_clock and i_reset in both components are required, and this inputs must be connected to the same CLOCK and RESET signal on the top-level. In the VWF file you will have to apply a clock waveform on CLOCK and a high pulse on RESET for at least one clock cycle. - Martin Zabel
Martin, I've changed the circuit like you said before. Now the simulation goes ok but the output signal stay always with XXXX value, I removed the two signals used before to do the states, now I'm using only 1 signal, I've edited the code to you see. - user5410082
The code template in my comment above is wrong. I have fixed it in my answer. - Martin Zabel

1 Answers

0
votes

On Altera FPGAs, latches are implemented using the look-up table (LUT) and a combinational feedback path within the logic element (LE). The state of such a latch is undefined after programming the FPGA. The simulation of the synthesized netlist with an Vector Waveform File (VWF) shows this as 'X'es. In your original code, the synthesizer reports warnings about latches for state and next_state (both state machines) and o_DOUT (consumidor). Thus, signals such as RDY_PRODUCER and RDY_CONSUMER which depend on the (unknown) state will be unknown too (as observed by you).


An asynchronous design using latches is possible, but only if the logic is free of timing hazards. The actual requirements for Altera FPGAs are described in the Altera Quartus-II / Quartus Prime Handbook, Volume 1, Recommended HDL Coding Styles, Register and Latch Coding Guidelines.

If your design contains latches, then the Compilation Report will denote if they are free of timing hazards. The information is found in the section Analysis & Synthesis -> Optimization Results -> Register Statistics -> User-Specified and Inferred Latches.

Altera and I recommend to use a synchronous design instead. I have already given a code template in the comments, but it was wrong. (Sorry!)) You have used it in the updated code in your question, but it must be changed another time to:

PROCESS(i_CLK)
BEGIN
    IF rising_edge(i_CLK) THEN
      IF (i_RST = '1') THEN
        stateT <= s0;
      ELSE
        CASE stateT IS
        -- description of state machine here
        END CASE;
      END IF;
    END IF;
END PROCESS;

A reset i_RST needs to be asserted during the startup of the FPGA only, if state machines may toggle states immediately. The initial state will always be defined by the declaration of signal stateT.

But even fixing this does not help. The producer keeps staying in state s0 because it is awaiting an acknowledge. But, the consumer is waiting for an request first before replying with an acknowledge. Thus, the state machines are in a dead-lock.

A producer-consumer scheme will work as follows instead:

  1. The producer issues a new request (o_REQ <= '1') if sending is enabled (i_SND = '1').

  2. The consumer waits for a request and then saves the incoming data if receiving is enabled (i_RCV = '1'). The consumer replies with an acknowledge afterwards (o_ACK <= '1')

  3. The producer waits for the acknowledge and then finished the request (o_REQ <= '0') afterwards.

  4. The consumer finishes the acknowledge as well (o_ACK <= '0') either after 1 clock cycle or after the request has been finished.

  5. Continue with step 1. But, your waveforms indicates that a new transaction should be issued by the producer only if i_SND is low in between. This is necessary to detect when new data DIN is available.

The implementation of the producer will now be:

ARCHITECTURE arch_1 OF produtor IS
  TYPE state_type IS (s0, s1, s2);
  SIGNAL stateT : state_type;
BEGIN
  PROCESS(i_CLK)
  BEGIN
    IF rising_edge(i_CLK) THEN
      IF (i_RST = '1') THEN
        stateT <= s0;
      ELSE
        CASE stateT IS
          when s0 => if (i_SND = '1') then -- wait until sending enabled
                       stateT <= s1; -- issue new request
                     end if;

          when s1 => if (i_ACK = '1') then -- wait until acknowledge
                       stateT <= s2; -- finish request
                     end if;

          when s2 => if (i_ACK = '0') and (i_SND = '0') then
                       -- wait until acknowledge finished and `i_SND` low
                       -- before starting a new transaction.
                       stateT <= s0;
                     end if;
        END CASE;
      END IF;
    END IF;
  END PROCESS;

  o_DOUT <= i_DIN WHEN (stateT = s0); -- latch open when ready
  o_REQ <= '1' WHEN (stateT = s1) ELSE '0';
  o_RDY <= '1'  WHEN (stateT = s0) ELSE '0';
END arch_1;

And the implementation of the consumer (code updated to signal ready only after request):

ARCHITECTURE arch_1 OF consumidor IS
  TYPE state_type IS (s0, s1, s2);
  SIGNAL stateT : state_type;
BEGIN
  PROCESS(i_CLK)
  BEGIN
    IF rising_edge(i_CLK) THEN
      IF (i_RST = '1') THEN
        stateT <= s0;
      ELSE
        CASE stateT IS
          WHEN s0 => IF (i_REQ = '1') then -- wait until request
                       stateT <= s1; -- signal ready
                     END IF;

          when s1 => if (i_RCV = '1') then -- wait until receiving enabled
                       stateT <= s2; -- save data and acknowledge
                     end if;

          WHEN s2 => IF (i_REQ = '0') then -- wait until request finished
                       stateT <= s0; -- finish acknowledge
                     END IF;
        END CASE;
      END IF;
    END IF;
  END PROCESS;

  o_DOUT <= i_DIN WHEN (stateT = s2); -- latch open during acknowledge
  o_ACK <= '1'  WHEN (stateT = s2) ELSE '0';
  o_RDY <= '1'  WHEN (stateT = s1) ELSE '0';
END arch_1;

I have used the inputs from your waveform, but some changes are required:

  • RCV and SND must be low at the beginning otherwise a reset is needed.

  • The clock frequency must be 2 times, so that, inputs due not change near the rising edge of the clock.

  • RCV is disabled at the beginning of the third transaction to demonstrate the delay of the data output and the acknowledge.

The simulation output will now be:

simulation output with o_DOUT latch

My waveform also includes the REQ and ACK signal between both state-machines to demonstrate the hand-shaking (named DEBUG_REQ and DEBUG_ACK here).

The assignments of signal o_DOUT in both state-machines still describe latches. According to the Compilation Report, Quartus can implement them without timing hazards. But, it would be better to implement them as synchronous registers as well. I will leave this as an exercise.