0
0
VhdlHow-ToBeginner · 4 min read

VHDL Code for Synchronous FIFO: Syntax, Example, and Tips

A synchronous FIFO in VHDL uses a single clock for both read and write operations. It typically includes signals for data input/output, write/read enable, full/empty flags, and internal pointers. The FIFO stores data in a memory array and updates pointers synchronously on clock edges.
📐

Syntax

A synchronous FIFO in VHDL usually has these main parts:

  • clk: The clock signal for synchronization.
  • rst: Reset signal to initialize the FIFO.
  • data_in: Input data bus.
  • data_out: Output data bus.
  • write_en: Write enable signal.
  • read_en: Read enable signal.
  • full: Flag indicating FIFO is full.
  • empty: Flag indicating FIFO is empty.
  • memory: Internal array to store data.
  • write_ptr and read_ptr: Pointers to track write and read positions.

All read and write operations happen on the rising edge of clk.

vhdl
entity sync_fifo is
    generic (
        DATA_WIDTH : integer := 8;
        FIFO_DEPTH : integer := 16
    );
    port (
        clk       : in  std_logic;
        rst       : in  std_logic;
        data_in   : in  std_logic_vector(DATA_WIDTH-1 downto 0);
        write_en  : in  std_logic;
        read_en   : in  std_logic;
        data_out  : out std_logic_vector(DATA_WIDTH-1 downto 0);
        full      : out std_logic;
        empty     : out std_logic
    );
end sync_fifo;
💻

Example

This example shows a complete synchronous FIFO with 8-bit data and 16-depth memory. It handles write and read operations on the clock's rising edge, updates full and empty flags, and outputs data accordingly.

vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity sync_fifo is
    generic (
        DATA_WIDTH : integer := 8;
        FIFO_DEPTH : integer := 16
    );
    port (
        clk       : in  std_logic;
        rst       : in  std_logic;
        data_in   : in  std_logic_vector(DATA_WIDTH-1 downto 0);
        write_en  : in  std_logic;
        read_en   : in  std_logic;
        data_out  : out std_logic_vector(DATA_WIDTH-1 downto 0);
        full      : out std_logic;
        empty     : out std_logic
    );
end sync_fifo;

architecture Behavioral of sync_fifo is
    function log2ceil(n : integer) return integer is
        variable i : integer := 0;
        variable v : integer := 1;
    begin
        while v < n loop
            v := v * 2;
            i := i + 1;
        end loop;
        return i;
    end function;

    type memory_array is array (0 to FIFO_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
    signal memory : memory_array := (others => (others => '0'));
    signal write_ptr : unsigned(log2ceil(FIFO_DEPTH)-1 downto 0) := (others => '0');
    signal read_ptr  : unsigned(log2ceil(FIFO_DEPTH)-1 downto 0) := (others => '0');
    signal count     : unsigned(log2ceil(FIFO_DEPTH) downto 0) := (others => '0');

begin
    process(clk, rst)
    begin
        if rst = '1' then
            write_ptr <= (others => '0');
            read_ptr  <= (others => '0');
            count     <= (others => '0');
            data_out  <= (others => '0');
        elsif rising_edge(clk) then
            -- Write operation
            if write_en = '1' and full = '0' then
                memory(to_integer(write_ptr)) <= data_in;
                write_ptr <= write_ptr + 1;
                count <= count + 1;
            end if;

            -- Read operation
            if read_en = '1' and empty = '0' then
                data_out <= memory(to_integer(read_ptr));
                read_ptr <= read_ptr + 1;
                count <= count - 1;
            elsif empty = '1' then
                data_out <= (others => '0');
            end if;
        end if;
    end process;

    full  <= '1' when count = to_unsigned(FIFO_DEPTH, count'length) else '0';
    empty <= '1' when count = 0 else '0';

end Behavioral;
Output
No console output; hardware signals update as expected: data_out outputs read data, full and empty flags indicate FIFO status.
⚠️

Common Pitfalls

Common mistakes when writing synchronous FIFO code include:

  • Not resetting pointers and counters properly on rst.
  • Allowing write when FIFO is full or read when FIFO is empty, causing data corruption.
  • Incorrect pointer increment or overflow handling.
  • Not updating full and empty flags correctly.

Always check conditions before read/write and reset all internal signals.

vhdl
wrong: process(clk)
begin
    if rising_edge(clk) then
        if write_en = '1' then  -- No full check
            memory(to_integer(write_ptr)) <= data_in;
            write_ptr <= write_ptr + 1;
        end if;
        if read_en = '1' then  -- No empty check
            data_out <= memory(to_integer(read_ptr));
            read_ptr <= read_ptr + 1;
        end if;
    end if;
end process;

right: process(clk)
begin
    if rising_edge(clk) then
        if write_en = '1' and full = '0' then
            memory(to_integer(write_ptr)) <= data_in;
            write_ptr <= write_ptr + 1;
        end if;
        if read_en = '1' and empty = '0' then
            data_out <= memory(to_integer(read_ptr));
            read_ptr <= read_ptr + 1;
        end if;
    end if;
end process;
📊

Quick Reference

Tips for writing synchronous FIFO in VHDL:

  • Use a single clock for all operations.
  • Reset pointers and counters on reset.
  • Check full before writing and empty before reading.
  • Use unsigned arithmetic for pointers and counters.
  • Update full and empty flags based on count.

Key Takeaways

A synchronous FIFO uses one clock for both read and write operations to keep data in sync.
Always check full and empty flags before writing or reading to avoid data loss or corruption.
Reset all internal pointers and counters to known states on reset.
Use unsigned counters and pointers for easy arithmetic and boundary checks.
Update full and empty signals based on the count of stored elements.