VHDL Code for Asynchronous FIFO: Syntax and Example
write_clock and read_clock control independent write and read processes, ensuring data integrity across clock domains.Syntax
An asynchronous FIFO in VHDL typically includes signals for separate write and read clocks, write and read pointers, memory array, and synchronization registers. The write process uses write_clock to store data, while the read process uses read_clock to output data. Gray code counters are used for pointers to safely cross clock domains.
- write_clock: Clock for writing data
- read_clock: Clock for reading data
- write_pointer: Tracks write position in memory
- read_pointer: Tracks read position in memory
- memory: Array storing FIFO data
- gray_code: Used for pointer synchronization
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity async_fifo is
generic(
DATA_WIDTH : integer := 8;
FIFO_DEPTH : integer := 16
);
port(
write_clock : in std_logic;
write_reset : in std_logic;
write_enable : in std_logic;
write_data : in std_logic_vector(DATA_WIDTH-1 downto 0);
read_clock : in std_logic;
read_reset : in std_logic;
read_enable : in std_logic;
read_data : out std_logic_vector(DATA_WIDTH-1 downto 0);
full : out std_logic;
empty : out std_logic
);
end async_fifo;Example
This example shows a complete asynchronous FIFO with separate write and read clocks, gray-coded pointers, and dual-port memory. It demonstrates safe data transfer between two clock domains.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity async_fifo is
generic(
DATA_WIDTH : integer := 8;
FIFO_DEPTH : integer := 16
);
port(
write_clock : in std_logic;
write_reset : in std_logic;
write_enable : in std_logic;
write_data : in std_logic_vector(DATA_WIDTH-1 downto 0);
read_clock : in std_logic;
read_reset : in std_logic;
read_enable : in std_logic;
read_data : out std_logic_vector(DATA_WIDTH-1 downto 0);
full : out std_logic;
empty : out std_logic
);
end async_fifo;
architecture rtl of async_fifo is
constant ADDR_WIDTH : integer := integer(ceil(log2(real(FIFO_DEPTH))));
type mem_type is array(0 to FIFO_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
signal memory : mem_type := (others => (others => '0'));
signal write_pointer_bin, write_pointer_gray : unsigned(ADDR_WIDTH downto 0) := (others => '0');
signal read_pointer_bin, read_pointer_gray : unsigned(ADDR_WIDTH downto 0) := (others => '0');
signal write_pointer_gray_sync1, write_pointer_gray_sync2 : unsigned(ADDR_WIDTH downto 0) := (others => '0');
signal read_pointer_gray_sync1, read_pointer_gray_sync2 : unsigned(ADDR_WIDTH downto 0) := (others => '0');
function to_gray(bin : unsigned) return unsigned is
begin
return bin xor (bin srl 1);
end function;
function to_bin(gray : unsigned) return unsigned is
variable bin : unsigned(gray'range) := (others => '0');
begin
bin(gray'high) := gray(gray'high);
for i in gray'high-1 downto 0 loop
bin(i) := bin(i+1) xor gray(i);
end loop;
return bin;
end function;
begin
-- Write process
process(write_clock)
begin
if rising_edge(write_clock) then
if write_reset = '1' then
write_pointer_bin <= (others => '0');
write_pointer_gray <= (others => '0');
else
if write_enable = '1' and full = '0' then
memory(to_integer(write_pointer_bin(ADDR_WIDTH-1 downto 0))) <= write_data;
write_pointer_bin <= write_pointer_bin + 1;
write_pointer_gray <= to_gray(write_pointer_bin + 1);
end if;
end if;
end if;
end process;
-- Read process
process(read_clock)
begin
if rising_edge(read_clock) then
if read_reset = '1' then
read_pointer_bin <= (others => '0');
read_pointer_gray <= (others => '0');
read_data <= (others => '0');
else
if read_enable = '1' and empty = '0' then
read_data <= memory(to_integer(read_pointer_bin(ADDR_WIDTH-1 downto 0)));
read_pointer_bin <= read_pointer_bin + 1;
read_pointer_gray <= to_gray(read_pointer_bin + 1);
end if;
end if;
end if;
end process;
-- Synchronize read pointer to write clock domain
process(write_clock)
begin
if rising_edge(write_clock) then
read_pointer_gray_sync1 <= read_pointer_gray;
read_pointer_gray_sync2 <= read_pointer_gray_sync1;
end if;
end process;
-- Synchronize write pointer to read clock domain
process(read_clock)
begin
if rising_edge(read_clock) then
write_pointer_gray_sync1 <= write_pointer_gray;
write_pointer_gray_sync2 <= write_pointer_gray_sync1;
end if;
end process;
-- Full flag
full <= '1' when (write_pointer_gray(ADDR_WIDTH-1 downto 0) = (not read_pointer_gray_sync2(ADDR_WIDTH-1 downto 0))) and
(write_pointer_gray(ADDR_WIDTH) /= read_pointer_gray_sync2(ADDR_WIDTH)) else '0';
-- Empty flag
empty <= '1' when read_pointer_gray = write_pointer_gray_sync2 else '0';
end rtl;Common Pitfalls
Common mistakes when designing asynchronous FIFOs include:
- Not using gray code for pointers, which can cause metastability and incorrect pointer synchronization.
- Failing to synchronize pointers across clock domains properly, leading to data corruption.
- Incorrectly calculating full and empty flags, causing premature full or empty signals.
- Not resetting pointers correctly on reset signals.
Always use double flip-flop synchronization for pointers crossing clock domains and gray code counters for safe pointer updates.
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- WRONG: Using binary counters directly for pointer synchronization signal write_pointer_bin : unsigned(ADDR_WIDTH downto 0); signal read_pointer_bin_sync : unsigned(ADDR_WIDTH downto 0); -- This can cause metastability and incorrect FIFO status. -- RIGHT: Use gray code counters and double synchronization signal write_pointer_gray : unsigned(ADDR_WIDTH downto 0); signal read_pointer_gray_sync1, read_pointer_gray_sync2 : unsigned(ADDR_WIDTH downto 0); -- Synchronize with two flip-flops in the other clock domain process(other_clock) begin if rising_edge(other_clock) then read_pointer_gray_sync1 <= read_pointer_gray; read_pointer_gray_sync2 <= read_pointer_gray_sync1; end if; end process;
Quick Reference
Tips for designing asynchronous FIFOs in VHDL:
- Use separate clocks for write and read processes.
- Implement gray code counters for write and read pointers.
- Synchronize pointers crossing clock domains with double flip-flops.
- Calculate full and empty flags using synchronized pointers.
- Reset pointers properly on reset signals.