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