VHDL coding tips and tricks: FIFO
Showing posts with label FIFO. Show all posts
Showing posts with label FIFO. Show all posts

Wednesday, March 10, 2010

VHDL: Generic FIFO with testbench

   CODE UPDATED AFTER DEBUGGING ON 9th Mar 2024!

FIFO's(first in first out) are generally used as a buffer for data transfer between two modules operating at different speeds. The maximum number of elements a FIFO can hold at a time is known as the depth of the FIFO. This must be set based on the difference in reading and writing rate of the FIFO. The larger the difference, the greater the FIFO depth must be.

This particular FIFO has two enable signals, enw for writing and enr for reading, respectively. They must be high to write to the FIFO or read from it. There are two output bits, fifo_full and fifo_empty, which act as status bits to indicate whether the FIFO is full or empty. Users are supposed to check these status signals before performing read or write operations on the FIFO.

The width of the FIFO entity shared here is adjustable via a generic parameter called DEPTH. The width of the data input or output cannot be changed via a generic parameter, but its quite easy to make the necessary changes if you have basic knowledge of VHDL.

fifo.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity fifo is
generic (DEPTH : integer := 16);  --depth of fifo
port (clk : in std_logic;
      reset : in std_logic;
      enr : in std_logic;   --enable read,should be '0' when not in use.
      enw : in std_logic;    --enable write,should be '0' when not in use.
      data_in : in std_logic_vector (7 downto 0);     --input data
      data_out : out std_logic_vector(7 downto 0);    --output data
      fifo_empty : out std_logic;     --set as '1' when the queue is empty
      fifo_full : out std_logic     --set as '1' when the queue is full
     );
end fifo;

architecture Behavioral of fifo is

type memory_type is array (0 to DEPTH-1) of std_logic_vector(7 downto 0);
signal memory : memory_type :=(others => (others => '0'));   --memory for queue.
signal readptr,writeptr : integer := 0;  --read and write pointers.
signal empty,full : std_logic := '0';

begin

fifo_empty <= empty;
fifo_full <= full;

process(Clk,reset)
--this is the number of elements stored in fifo at a time.
--this variable is used to decide whether the fifo is empty or full.
variable num_elem : integer := 0;  
begin
if(rising_edge(Clk)) then
    if(reset = '1') then
        data_out <= (others => '0');
        empty <= '0';
        full <= '0';
        readptr <= 0;
        writeptr <= 0;
        num_elem := 0;
    else
        if(enr = '1' and empty = '0') then  --read
            data_out <= memory(readptr);
            readptr <= readptr + 1;      
            num_elem := num_elem-1;
        end if;
        if(enw ='1' and full = '0') then    --write
            memory(writeptr) <= data_in;
            writeptr <= writeptr + 1;  
            num_elem := num_elem+1;
        end if;
        --rolling over of the indices.
        if(readptr = DEPTH-1) then      --resetting read pointer.
            readptr <= 0;
        end if;
        if(writeptr = DEPTH-1) then        --resetting write pointer.
            writeptr <= 0;
        end if; 
        --setting empty and full flags.
        if(num_elem = 0) then
            empty <= '1';
        else
            empty <= '0';
        end if;
        if(num_elem = DEPTH) then
            full <= '1';
        else
            full <= '0';
        end if;
    end if;
end if; 
end process;

end Behavioral;

The program above demonstrates the working of a simple FIFO. In actual projects where its not mandatory to implement a FIFO from scratch, I recommend utilizing CoreGen software from Xilinx, which is capable of generating code for complex FIFOs with a lot of customizable properties. 

The following testbench tests the FIFO code shared above. Please be aware that the code may still contain bugs. If you encounter any issues, kindly leave a comment and I will look into it as soon as possible. 

fifo_tb.vhd:


LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_arith.ALL;

ENTITY fifo_tb IS
END fifo_tb;

ARCHITECTURE behavior OF fifo_tb IS 

   --Inputs and outputs
   signal Clk,reset,enr,enw,empty,full : std_logic := '0';
   signal data_in,data_out : std_logic_vector(7 downto 0) := (others => '0');
   --temporary signals
   signal i : integer := 0;
   -- Clock period definitions
   constant Clk_period : time := 10 ns;
   constant DEPTH : integer := 16;  --specify depth of fifo here.

BEGIN

   -- Instantiate the Unit Under Test (UUT)
   uut: entity work.fifo 
            generic map(DEPTH => DEPTH) 
            PORT MAP (clk => clk,
                    reset => reset,
                    enr => enr,
                    enw => enw,
                    data_in => data_in,
                    data_out => data_out,
                    fifo_empty => empty,
                    fifo_full => full);

   -- Clock process definitions
   Clk_process :process
   begin
      Clk <= '0';
      wait for Clk_period/2;
      Clk <= '1';
      wait for Clk_period/2;
   end process;

   -- Stimulus process
   stim_proc: process
   begin        
      reset <= '1';  --apply reset for one clock cycle.
      wait for clk_period;
      reset <= '0';
      wait for clk_period*3;  --wait for 3 clock periods(simply)
      enw <= '1';     enr <= '0';         --write 10 values to fifo.
      for i in 1 to 10 loop  
         data_in <= conv_std_logic_vector(i,8);
         wait for clk_period;
      end loop; 
      enw <= '0';     enr <= '1';         --read 4 values from fifo.
      wait for clk_period*4;
      enw <= '1';     enr <= '1';         --read and write at the same time for 2 clock cycles
      wait for clk_period*2;
      enw <= '0';     enr <= '0'; 			 --neither read nor write
      wait for clk_period*10;  --wait for some clock cycles.
      enw <= '1';     enr <= '0';         --write 10 values to fifo.
      for i in 11 to 20 loop  
         data_in <= conv_std_logic_vector(i,8);
         wait for clk_period;
      end loop; 
      enw <= '0';     enr <= '0'; 			 --neither read nor write
      wait for clk_period*10;  --wait for some clock cycles.
      enw <= '0';     enr <= '1';         --read 4 values from fifo.
      wait for clk_period*4;
      enw <= '0';     enr <= '0';      --neither read nor write
      wait for clk_period;
      enw <= '0';     enr <= '1';         --read 4 values from fifo.
      wait for clk_period*8;
      enw <= '0';     enr <= '0'; 		 --neither read nor write
      wait for clk_period;
      enw <= '0';     enr <= '1';         --read 8 values from fifo.
      wait for clk_period*4;
      enw <= '0';     enr <= '0';         --neither read nor write
      wait for clk_period;
      enw <= '0';     enr <= '1';         --read 4 values from fifo.
      wait for clk_period*4;
      enw <= '0';     enr <= '0';          --neither read nor write
      wait;
   end process;

END;

Simulation Waveform:

The following waveform was obtained from Xilinx Vivado 2023.2.

simulation waveform of fifo.vhd using xilinx vivado software


The design was tested successfully using Xilinx ISE 14.6. Synthesizing the design for Virtex 6 fpga, showed a maximum clock frequency of 250 MHz.