0
\$\begingroup\$

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;
\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.