1
\$\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;
asked Jul 19, 2018 at 15:01
\$\endgroup\$
5
  • \$\begingroup\$ You're interfacing between two different FPGAs? Are you properly synchronizing the inputs? \$\endgroup\$ Commented Jul 19, 2018 at 18:35
  • \$\begingroup\$ @alex.forencich Hi, No but I thought that I was not necesssary because the frequency of the signal that I sent was 2MHz and the FIFO working with 50MHz so I expect that detect the signal in one of those cycles, there is still a clock crossing problem with that?? \$\endgroup\$ Commented Jul 20, 2018 at 7:07
  • \$\begingroup\$ If that 2 MHz and that 50 MHz do not have a fixed phase relationship (implying that they must come from the same source) then you absolutely have a clock crossing issue to deal with. \$\endgroup\$ Commented Jul 20, 2018 at 7:17
  • \$\begingroup\$ @alex.forencich thank you! That's was the way to fix it, I thought wrong about clock domain crossing, but it has total sense right now to me. Thank you for your help. \$\endgroup\$ Commented Jul 20, 2018 at 8:20

1 Answer 1

1
\$\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.

answered Jul 19, 2018 at 18:38
\$\endgroup\$
1
  • \$\begingroup\$ Why not just grab the CDC FIFO IP provided by the FPGA tool vendor? \$\endgroup\$ Commented Sep 29, 2024 at 18:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.