Problema FIFO en la implementación (VHDL)

0

Estuve trabajando en eso durante los últimos cinco días y no sé qué pasó. Debo implementar un FIFO para enviar alguna información, adjunto el FIFO que utilizo. Como puede ver en el código, este FIFO utiliza tres procesos actualizar datos , proceso de puntero y enviar o recibir datos . La idea es enviar la información con una frecuencia un poco más baja que el reloj del FPGA, por eso el proceso utiliza la sensibilidad de wr_en y rd_en (las señales de habilitación) para realizar dos de los procesos. En primer lugar simulé todas las diferentes señales que el FIFO necesita para enviar o recibir la información y la simulación funciona bien sin problemas, todas las señales que esperaba estaban allí (también adjunto el banco de pruebas FIFO).

El problema es cuando intenté escribir en el FIFO, si hice eso con los interruptores, no hay problema, el FIFO funciona bien. Sin embargo, cuando intenté enviar la información con otro FPGA con una frecuencia más alta, el FIFO reconoce algunos datos falsos y, por este motivo, el FIFO está lleno incluso cuando la señal de wr_en era alta solo una (pero con una frecuencia más baja del principal). FPGA). Busqué mucho en la simulación y en el código, y no veo ningún problema, no sé por qué el FIFO no funciona como lo esperaba.

Cualquier consejo sobre lo que podría pasar realmente lo aprecio. Gracias

Código FIFO en 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;
    
pregunta Jairo Mejia

1 respuesta

0
  

cuando intenté enviar la información con otro FPGA con una frecuencia más alta ..

Según su propia declaración, sus señales externas no están relacionadas con su reloj FPGA. Por lo tanto, está intentando procesar señales asíncronas en un diseño sincrónico sin tomar ninguna precaución. Eso no funcionará.

En su banco de pruebas, todo está "perfectamente" definido, al igual que la naturaleza de las señales teóricas puras. Sus señales (wr_en, rd_en y las 24 líneas de datos) cambian exactamente después de las 20 ns y se sincronizan exactamente con el reloj. Lamentablemente, la vida real no es tan perfecta.

El problema que tengo es darte una solución, ya que se necesita algo de experiencia para resolver este problema.

Normalmente, le aconsejaría que utilice un FIFO asíncrono, que es un FIFO que se escribe con un reloj, y se lee con un reloj diferente. Pero esto aquí es un FIFO. Lo que significa que tendría que enseñarle cómo diseñar un FIFO asíncrono y eso va más allá del alcance del intercambio de pila.

Comience con la búsqueda de temas como:

  • Sincronización.
  • Cruce de dominio del reloj.

O busque un ejemplo de un FIFO asíncrono en la WWW.

Una segunda forma es usar una frecuencia de muestreo FPGA alta y tener una fuente con una señal de "listo" cuando la entrada es válida. Pero entonces aún necesita aprender acerca de la sincronización y la lógica de cruce del dominio del reloj para manejar correctamente la señal de "listo".

La solución teórica es hacer que la fuente genere datos utilizando el mismo reloj que su FPGA, (Nota: el mismo reloj, no la misma frecuencia de reloj). Pero para la mayoría de los flujos de datos eso no es posible y aún debe tener cuidado con el sesgo del reloj.

    
respondido por el Oldfart

Lea otras preguntas en las etiquetas