Electrical – Problem FIFO in the implementation (VHDL)

fifofpgaintel-fpgavhdl

I was working in that for the past five days and I don't know what happened. I must implement a FIFO to send some information, I attach the FIFO that I use. As you can see in the code, this FIFO using three process update data, pointer process and send or receive data. The idea is send the information with a frequency a little lower than the clock of the FPGA, that's why the process uses the sensibility of wr_en and rd_en (the enable signals) to make two of the process. First of all I simulated all the different signals that the FIFO needs to send or receive the information and the simulation works fine without problem, all the signals that I expected were there (also I attach the FIFO testbench).

The problem is when I tried to write in the FIFO, if I made that with switches, there is no problem, the FIFO works fine. However, when I tried to send the information with another FPGA with a more higher frequency, the FIFO recognize some false data and for this reason the FIFO is full even when the wr_en signal was high just one (but with a lower frequency of the main FPGA). I searched a lot in the simulation and in the code and I don’t see a problem, I don’t know why the FIFO doesn’t work as I expect.

Any advice of what could happened I really appreciate. Thank you

Code FIFO in VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
------------------------------------------------------

----------------------------------------------------------------------------
entity fifo is
    Generic(
            ADDR_W  : integer   := 7;               -- address width in bits
            DATA_W  : integer   := 24;          -- data width in bits
            BUFF_L  : integer   := 80;          -- buffer length must be less than address space as in  BUFF_L <or= 2^(ADDR_W)
            ALMST_F : integer   := 3;               -- fifo flag for almost full regs away from empty fifo
            ALMST_E : integer   := 3                -- fifo regs away from empty fifo
            );
    Port ( 
            clk                 : in std_logic;
            n_reset             : in std_logic;
            rd_en           : in std_logic;         -- read enable 
            wr_en               : in std_logic;         -- write enable 
            data_in             : in std_logic_vector(DATA_W- 1 downto 0); 
            data_out            : out std_logic_vector(DATA_W- 1 downto 0); 
            data_count      : out std_logic_vector(ADDR_W downto 0);
            inputValid      : out std_logic; 
            full                : out std_logic;

            rd_ptr_out      : out std_logic_vector(ADDR_W-1 downto 0);      -- current pointers
            wr_ptr_out      : out std_logic_vector(ADDR_W-1 downto 0);      -- current pointers


            err             : out std_logic
);
end fifo;
----------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------------
architecture arch of fifo is

    type reg_file_type is array (0 to ((2**ADDR_W) - 1)) of std_logic_vector(DATA_W - 1 downto 0);

    -----------memory, pointers, and flip flops---------
    signal mem_array                    : reg_file_type ;
    signal rd_ptr, wr_ptr           : std_logic_vector(ADDR_W-1 downto 0);      -- current pointers
    signal rd_ptr_nxt                   : std_logic_vector(ADDR_W-1 downto 0);      -- next pointer
    signal wr_ptr_nxt               : std_logic_vector(ADDR_W-1 downto 0);      -- next pointer
    signal full_ff, empty_ff        : std_logic;                                            -- full and empty flag flip flops
    signal full_ff_nxt              : std_logic;                                            -- full and empty flag flip flops for next state
    signal empty_ff_nxt                 : std_logic;    
    signal q_reg, q_next                : std_logic_vector(ADDR_W downto 0);            -- data counter
    signal q_add, q_sub             : std_logic;
    signal wr_en_prev,rd_en_prev    : std_logic;
    ---------------------------------------------------

begin

    ---------- Process to update read, write, full, and empty on clock edges
    reg_update :    
    process(clk) 
    begin
        if falling_edge(clk) then
            if (n_reset = '0')  then
                rd_ptr <= (others => '0');
                wr_ptr <= (others => '0');
                full_ff <= '0';
                empty_ff <= '1';
                q_reg <= (others => '0');
            else
                rd_ptr <=   rd_ptr_nxt; 
                wr_ptr <= wr_ptr_nxt;
                full_ff <= full_ff_nxt;
                empty_ff <= empty_ff_nxt;           
                q_reg <= q_next;
                wr_en_prev <= wr_en;
                rd_en_prev <= rd_en;
            end if; -- end of n_reset if
        end if; -- end of falling_edge(clk) if
    end process;

    ----------- Process to control read and write pointers and empty/full flip flops
    Ptr_Cont_Original : 
    process(wr_en, rd_en, q_reg)                     

    begin
        wr_ptr_nxt <= wr_ptr;                                           -- no change to pointers
        rd_ptr_nxt <= rd_ptr;
        full_ff_nxt <= full_ff;
        empty_ff_nxt <= empty_ff;
        q_add <= '0';
        q_sub <= '0';

        ---------- check if fifo is full during a write attempt, after a write increment counter
        ----------------------------------------------------
        if((wr_en /= wr_en_prev) or (rd_en /= rd_en_prev)) then
            if(wr_en = '1' and rd_en = '0') then
                if(full_ff = '0') then
                    q_add <= '1';
                    if(conv_integer(wr_ptr) < BUFF_L-1 ) then           
                        wr_ptr_nxt <= wr_ptr  + '1';
                        empty_ff_nxt <= '0';
                    else    
                        wr_ptr_nxt <= (others => '0');              
                        empty_ff_nxt <= '0';  
                    end if; 
                    -- check if fifo is full
                    if (conv_integer(wr_ptr + '1') = conv_integer(rd_ptr) or (conv_integer(wr_ptr) = (BUFF_L-1) and conv_integer(rd_ptr) = 0)) then      
                        full_ff_nxt <= '1';
                    end if ;
                end if;
            end if; 
            ---------- check to see if fifo is empty during a read attempt, after a read decrement counter
            ---------------------------------------------------------------
            if(wr_en = '0' and rd_en = '1') then    
                if(empty_ff = '0') then
                    if(conv_integer(q_reg) > 0) then
                        q_sub <= '1';
                    else
                        q_sub <= '0';
                    end if;
                    if(conv_integer(rd_ptr) < BUFF_L-1 ) then               
                        rd_ptr_nxt <= rd_ptr + '1';
                        full_ff_nxt <= '0';
                    else    
                        rd_ptr_nxt <= (others => '0');  
                        full_ff_nxt <= '0';     
                    end if;
                    -- check if fifo is empty
                    if (conv_integer(rd_ptr  + '1') = conv_integer(wr_ptr) or (conv_integer(rd_ptr) = (BUFF_L-1) and conv_integer(wr_ptr) = 0 )) then     
                        empty_ff_nxt <= '1';
                    end if ;
                end if;
            end if;
            -----------------------------------------------------------------
            if(wr_en = '1' and rd_en = '1') then
                if(conv_integer(wr_ptr) < BUFF_L-1 ) then       
                    wr_ptr_nxt <= wr_ptr  + '1';    
                else                                            
                    wr_ptr_nxt <=  (others => '0');
                end if;
                if(conv_integer(rd_ptr) < BUFF_L-1 ) then           
                    rd_ptr_nxt <= rd_ptr + '1';     
                else
                    rd_ptr_nxt <= (others => '0');
                end if;
            end if;
        end if;
    end process;

    -------- Process to control memory array writing and reading        
    mem_cont :  
    process(wr_en,rd_en,n_reset)        
    begin
        if( n_reset = '0') then
            mem_array <= (others => (others => '0'));           -- reset memory array
            data_out <= (others => '0');                                -- reset data out
            err <= '0';
        else
            -- if write enable and not full then latch in data and increment wright pointer
            if( wr_en = '1') and (full_ff = '0') then
                mem_array (conv_integer(wr_ptr)) <=  data_in ;
                err <= '0';
            elsif(wr_en = '1') and (full_ff = '1') then         -- check if full and trying to write
                err <= '1';
            end if ;
            -- if read enable and fifo not empty then latch data out and increment read pointer
            if( rd_en = '1') and (empty_ff = '0') then
                data_out <= mem_array(conv_integer(rd_ptr));
                err <= '0';
            elsif(rd_en = '1') and (empty_ff = '1') then        -- check if empty and trying to read 
                err <= '1';
            end if ;
        end if; -- end of n_reset if
end process;

------ counter to keep track of almost full and almost empty 
q_next <= q_reg + 1 when q_add = '1' else
                    q_reg - 1 when q_sub = '1' else
                    q_reg;

-------- connect ff to output ports
full <= full_ff;
inputValid <= not empty_ff;
data_count <= q_reg;
wr_ptr_out <= wr_ptr;
rd_ptr_out <= rd_ptr;
end arch;
---------------------------------------------------------------------------------------

Testbench

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY fifo_tb IS
END fifo_tb;

ARCHITECTURE behavior OF fifo_tb IS 

    COMPONENT fifo
        Generic(
            ADDR_W  : integer   := 4;       -- address width in bits
            DATA_W  : integer   := 24;  -- data width in bits
            BUFF_L  : integer   := 16;  -- buffer length must be less than address space as in  BUFF_L <= 2^(ADDR_W)
            ALMST_F : integer   := 3;       -- fifo regs away from full fifo
            ALMST_E : integer   := 3        -- fifo regs away from empty fifo
            );
    Port ( 
            clk                     : in std_logic;
            n_reset                 : in std_logic;
            rd_en               : in std_logic;     -- read enable 
            wr_en                   : in std_logic;     -- write enable 
            data_in                 : in std_logic_vector(DATA_W- 1 downto 0); 
            data_out                : out std_logic_vector(DATA_W- 1 downto 0); 
            data_count          : out std_logic_vector(ADDR_W downto 0);
            inputValid          : out std_logic; 
            full                    : out std_logic;
            err                 : out std_logic
            );
    END COMPONENT;

    SIGNAL clk                  :  std_logic;
    SIGNAL n_reset          :  std_logic;
    SIGNAL rd_en                :  std_logic;
    SIGNAL wr_en                :  std_logic;
    SIGNAL data_in          :  std_logic_vector(23 downto 0);
    SIGNAL data_out             :  std_logic_vector(23 downto 0);
    SIGNAL data_count           :  std_logic_vector(4 downto 0);
    SIGNAL inputValid           :  std_logic;
    SIGNAL err                  :  std_logic;
    SIGNAL full                 :  std_logic;

    constant PERIOD             : time := 20 ns;



BEGIN

-- Please check and add your generic clause manually
    uut: fifo PORT MAP(
        clk => clk,
        n_reset => n_reset,
        rd_en => rd_en,
        wr_en => wr_en,
        data_in => data_in,
        data_out => data_out,
        data_count => data_count,
        inputValid => inputValid,
        err => err,
        full => full
    );


    -- PROCESS TO CONTROL THE CLOCK
    clock : PROCESS
    BEGIN

        clk <= '1';
        WAIT FOR PERIOD/2;
        clk <= '0';
        WAIT FOR PERIOD/2;

    END PROCESS;



-- *** Test Bench - User Defined Section ***
   tb : PROCESS
   BEGIN

        n_reset <= '0';
        rd_en <= '0';

        WAIT FOR 40 NS;

        n_reset <= '1';
    wr_en <= '0';

        WAIT FOR 20 NS;

        wr_en <= '1';
        data_in <= std_logic_vector(to_unsigned(10,24));
        wait for 10*PERIOD; 

        wr_en <= '0';
        wait for PERiOD; 

        wr_en <='1';
        data_in <= std_logic_vector(to_unsigned(5,24));

        WAIT FOR 40 NS;

        n_reset <= '1';
    wr_en <= '0';

        WAIT FOR 20 NS;

        -- write to fifo
        for test_vec in 0 to 17 loop
            WAIT FOR 20 NS;
            wr_en <= '1';
            data_in <= std_logic_vector(to_unsigned(test_vec,24));
            WAIT FOR 20 NS;
            wr_en <= '0';       
        end loop;   

        wait for 10 ns;

        WAIT FOR 10 NS;
        rd_en <= '1';
        WAIT FOR 10 NS;
        rd_en <= '0';

        wait for 3*PERIOD;


        -- read from fifo   
        for test_vec in 0 to 8 loop
            WAIT FOR 10 NS;
            rd_en <= '1';
            WAIT FOR 10 NS;
            rd_en <= '0';       
        end loop;   

        -- write to fifo        
        for test_vec in 0 to 15 loop
            WAIT FOR 10 NS;
            wr_en <= '1';
            data_in <= std_logic_vector(to_unsigned(test_vec,24));
            WAIT FOR 10 NS;
            wr_en <= '0';       
        end loop;       

        -- read from fifo   
        for test_vec in 0 to 11 loop
            WAIT FOR 10 NS;
            rd_en <= '1';
            WAIT FOR 10 NS;
            rd_en <= '0';       
        end loop;   

        WAIT FOR 80 NS;


        -- read and write to fifo       
        for test_vec in 0 to 11 loop
            WAIT FOR 10 NS;
            wr_en <= '1';
            rd_en <= '1';
            data_in <= std_logic_vector(to_unsigned(test_vec,24));
            WAIT FOR 10 NS;
            wr_en <= '0';
            rd_en <= '0';           
        end loop;           

        -- read from fifo
        for test_vec in 0 to 7 loop
            WAIT FOR 10 NS;
            rd_en <= '1';
            WAIT FOR 10 NS;
            rd_en <= '0';       
        end loop;       

        -- write to fifo    
        for test_vec in 0 to 13 loop
            WAIT FOR 10 NS;
            wr_en <= '1';
            data_in <= std_logic_vector(to_unsigned(test_vec,24));
            WAIT FOR 10 NS;
            wr_en <= '0';       
        end loop;           

      wait; -- will wait forever
   END PROCESS;
-- *** End Test Bench - User Defined Section ***

END;

Best Answer

when I tried to send the information with another FPGA with a more higher frequency..

As per your own statement, your external signals are unrelated to your FPGA clock. You are thus trying to process asynchronous signals with in a synchronous design without taking any precautions. That will not work.

In your test-bench everything is 'perfectly' defined, as is the nature of pure theoretical signals. Your signals (wr_en, rd_en and all 24 data lines) change exactly after 20ns and exactly in-sync with the clock. Real life is, unfortunately, not so perfect.

The problem I have is in giving you a solution as it take some experience to solve this issue.

I would normally advise you to use an asynchronous FIFO, that is a FIFO which you write using one clock, and read using a different clock. But this here is a FIFO. Which means I would have to teach you how to design an asynchronous FIFO and that goes beyond the scope of Stack-exchange.

Start with looking up subjects like:

  • Synchronization.
  • Clock domain crossing.

Or look for an example of an asynchronous FIFO on the WWW.

A second way is to use a high FPGA sample frequency and have a source with a 'ready' signal when the input is valid. But then you still need to learn about synchronization and clock domain crossing logic to correctly handle the 'ready' signal.

Theoretical a solution is to make the source generate data using the same clock as your FPGA, (Note: the same clock, not the same clock frequency). But for most data streams that is not possible and you still need to be wary of clock skew.

Related Topic