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.
--these are evil:
ReplyDeleteuse IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
--use this instead:
use ieee.numeric_std.all;
@anonymous : please see this post,
ReplyDeletehttp://vhdlguru.blogspot.com/2010/03/why-library-numericstd-is-preferred.html
library IEEE;
ReplyDeleteuse IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity fifo is
GENERIC
(
ADDRESS_WIDTH : integer:=8;---8 bit
DATA_WIDTH : integer:=32 ---32 bit
);
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.
dataout : out std_logic_vector(DATA_WIDTH-1 downto 0); --output data
datain : in std_logic_vector (DATA_WIDTH-1 downto 0); --input data
empty : out std_logic; --set as '1' when the queue is empty
err : out std_logic;
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 ((2**ADDRESS_WIDTH)-1)) of std_logic_vector(DATA_WIDTH-1 downto 0);
-----distributed-------
signal memory : memory_type ;-- :=(others => (others => '0')); --memory for queue.-----
signal readptr,writeptr : std_logic_vector(ADDRESS_WIDTH-1 downto 0); --read and write pointers.
signal full0 : std_logic;
signal empty0 : std_logic;
begin
full <= full0;
empty <= empty0;
fifo0: process(clk,reset)
begin
if reset='1' then
readptr <= (others => '0');
writeptr <= (others => '0');
empty0 <='1';
full0<='0';
err<='0';
elsif rising_edge(clk) then
if (writeptr + '1' = readptr) then
full0<='1';
else
full0<='0';
end if ;
if (readptr = writeptr ) then
empty0<='1';
else
empty0<='0';
end if ;
if (empty0='0' and enr='1') or (full0='0' and enw='1') then
err<='1';
end if ;
if enw='1' and full0='0' then
memory (conv_integer(writeptr)) <= datain ;
writeptr <= writeptr + '1' ;
end if ;
if enr='1' and empty0='0' then
dataout <= memory (conv_integer(readptr));
readptr <= readptr + '1' ;
end if ;
end if;
end process;
end Behavioral;
fix fifo doal port ram
it is ram block and not distributed in my comment
ReplyDeleteThis example is wrong, please correct or remove it; many people have copied it from your website and are wasting a lot of time wondering why it does not work.
ReplyDeleteOne of the reasons why it is wrong: to generate the "empty" and "full" flags, signals "readptr" and "writeptr" should be compared with one another, and NOT with absolute values. Cohen's example above is on the right direction.
To be fair, the presented code is a FIFO in the sense that the first value in is the first value out, but it requires a reset every 256 elements. This is *not* what people want, in 99.99% of the cases.
Agree with Jasinski. The FIFO full/empty conditions should be more like the following: (note that in this example, the pointers are definted as integers by the ieee.numeric.std)
ReplyDeleteif(writeptr = readptr) then
FIFO_EMPTY <='1';
else
FIFO_EMPTY <='0';
end if;
if(writeptr = (readptr - 1)) then
FIFO_FULL <='1';
else
FIFO_FULL <='0';
end if;
The empty condition occurs when you read the last value written, hence comparing them to each other. Full occurs when you write so many values that you fill up to one less than the last value read.
roei cohen variant is mostly correct except one thing: active "full" value will be sensed by wrighting block on 1 clk later, so it will try to write data to FIFO when it's already restricted.
ReplyDeleteWhat is the use of signal error here ?
ReplyDeleteI mean why error <= '0'; is required ?
ReplyDeleteI think in stead of error <=0; ,enr<=0; will be there;
ReplyDeletehi I need a READY QUEUE with priority any help?????????
ReplyDelete--rolling over of the indices !!!!!!!! ERROR !!!!!!!!!
ReplyDeleteresetting indices do not analyze enw and enr signals
right code for readptr:
if(enr = '1' and empty = '0') then --read
data_out <= memory(readptr);
if(readptr = depth-1) then
readptr <= 0;
else readptr <= readptr + 1;
end if;
num_elem := num_elem-1;
end if;
your fifo code has bug. The pointers will roll over from depth-1 to 0 when both write n read are '0'
ReplyDeletecould you share your testbench code please.
Delete