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
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:
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.