Bird
Raised Fist0
VHDLprogramming~10 mins

Synchronous FIFO design in VHDL

Choose your learning style10 modes available
Introduction

A FIFO (First In First Out) is like a line where the first person to get in is the first to get out. A synchronous FIFO uses one clock to control both adding and removing data, making it simple and reliable.

When you need to temporarily store data between two parts of a circuit working at the same speed.
When you want to buffer data to handle small delays without losing information.
When designing communication between modules that share the same clock.
When you want to manage data flow smoothly in digital signal processing.
When you need a simple queue structure inside an FPGA or ASIC.
Syntax
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;
        write_en  : in  std_logic;
        read_en   : in  std_logic;
        data_in   : in  std_logic_vector(DATA_WIDTH-1 downto 0);
        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
    type memory_array is array (0 to FIFO_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
    signal mem : memory_array := (others => (others => '0'));
    signal write_ptr : integer range 0 to FIFO_DEPTH-1 := 0;
    signal read_ptr  : integer range 0 to FIFO_DEPTH-1 := 0;
    signal count     : integer range 0 to FIFO_DEPTH := 0;
begin
    process(clk, rst)
    begin
        if rst = '1' then
            write_ptr <= 0;
            read_ptr <= 0;
            count <= 0;
            data_out <= (others => '0');
        elsif rising_edge(clk) then
            if write_en = '1' and full = '0' then
                mem(write_ptr) <= data_in;
                write_ptr <= (write_ptr + 1) mod FIFO_DEPTH;
                count <= count + 1;
            end if;
            if read_en = '1' and empty = '0' then
                data_out <= mem(read_ptr);
                read_ptr <= (read_ptr + 1) mod FIFO_DEPTH;
                count <= count - 1;
            end if;
        end if;
    end process;

    full <= '1' when count = FIFO_DEPTH else '0';
    empty <= '1' when count = 0 else '0';
end Behavioral;

The FIFO uses a circular buffer with read and write pointers.

Count keeps track of how many items are inside to detect full and empty states.

Examples
When the FIFO is empty, you cannot read data. The empty signal is high.
VHDL
-- Case: FIFO is empty
-- count = 0, empty = '1', full = '0'
-- read_ptr = write_ptr = 0
-- No data to read
When there is one element, you can read it or add more data until full.
VHDL
-- Case: FIFO has one element
-- count = 1, empty = '0', full = '0'
-- read_ptr and write_ptr differ by 1
-- You can read or write data
When full, the FIFO stops accepting new data until some is read out.
VHDL
-- Case: FIFO is full
-- count = FIFO_DEPTH, full = '1', empty = '0'
-- write_ptr catches up to read_ptr
-- No more writes allowed
Sample Program

This testbench creates a 4-bit wide, 4-depth synchronous FIFO. It writes 3 values, reads 2, writes 2 more, then reads all remaining values. The monitor prints FIFO status and output data on each clock.

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

entity testbench is
end testbench;

architecture Behavioral of testbench is
    constant DATA_WIDTH : integer := 4;
    constant FIFO_DEPTH : integer := 4;

    signal clk       : std_logic := '0';
    signal rst       : std_logic := '1';
    signal write_en  : std_logic := '0';
    signal read_en   : std_logic := '0';
    signal data_in   : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
    signal data_out  : std_logic_vector(DATA_WIDTH-1 downto 0);
    signal full      : std_logic;
    signal empty     : std_logic;

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

begin
    uut: sync_fifo
        generic map(DATA_WIDTH => DATA_WIDTH, FIFO_DEPTH => FIFO_DEPTH)
        port map(clk => clk, rst => rst, write_en => write_en, read_en => read_en,
                 data_in => data_in, data_out => data_out, full => full, empty => empty);

    clk_process : process
    begin
        while true loop
            clk <= '0';
            wait for 5 ns;
            clk <= '1';
            wait for 5 ns;
        end loop;
    end process;

    stim_proc: process
    begin
        rst <= '1';
        wait for 20 ns;
        rst <= '0';

        -- Write 3 values
        for i in 0 to 2 loop
            wait until rising_edge(clk);
            data_in <= std_logic_vector(to_unsigned(i+1, DATA_WIDTH));
            write_en <= '1';
            read_en <= '0';
            wait until rising_edge(clk);
            write_en <= '0';
        end loop;

        -- Read 2 values
        for i in 0 to 1 loop
            wait until rising_edge(clk);
            write_en <= '0';
            read_en <= '1';
            wait until rising_edge(clk);
            read_en <= '0';
        end loop;

        -- Write 2 more values
        for i in 3 to 4 loop
            wait until rising_edge(clk);
            data_in <= std_logic_vector(to_unsigned(i+1, DATA_WIDTH));
            write_en <= '1';
            read_en <= '0';
            wait until rising_edge(clk);
            write_en <= '0';
        end loop;

        -- Read all remaining values
        while empty = '0' loop
            wait until rising_edge(clk);
            write_en <= '0';
            read_en <= '1';
            wait until rising_edge(clk);
            read_en <= '0';
        end loop;

        wait;
    end process;

    monitor_proc: process(clk)
    begin
        if rising_edge(clk) then
            report "Full=" & std_logic'image(full) & " Empty=" & std_logic'image(empty) &
                   " Data_out=" & integer'image(to_integer(unsigned(data_out)));
        end if;
    end process;

end Behavioral;
OutputSuccess
Important Notes

Time complexity: O(1) for both write and read operations because pointers just move.

Space complexity: O(N) where N is FIFO_DEPTH, as memory stores all data.

Common mistake: Forgetting to check full before writing or empty before reading causes data loss or invalid reads.

Use synchronous FIFO when both sides share the same clock. For different clocks, use asynchronous FIFO.

Summary

A synchronous FIFO stores data in order using one clock.

It uses read and write pointers and a count to track fullness and emptiness.

Always check full before writing and empty before reading to avoid errors.

Practice

(1/5)
1. What is the main purpose of a synchronous FIFO in VHDL?
easy
A. To perform asynchronous data transfers between different clock domains
B. To store data in order using a single clock signal
C. To generate clock signals for other components
D. To convert analog signals to digital

Solution

  1. Step 1: Understand FIFO functionality

    A FIFO (First-In-First-Out) stores data in the order it arrives.
  2. Step 2: Identify synchronous aspect

    A synchronous FIFO uses one clock to control both read and write operations.
  3. Final Answer:

    To store data in order using a single clock signal -> Option B
  4. Quick Check:

    Synchronous FIFO = single clock storage [OK]
Hint: Remember FIFO means first-in, first-out with one clock [OK]
Common Mistakes:
  • Confusing synchronous with asynchronous FIFO
  • Thinking FIFO generates clocks
  • Mixing FIFO with analog-to-digital conversion
2. Which VHDL signal type is typically used for the FIFO write pointer in a synchronous FIFO?
easy
A. std_logic_vector
B. integer
C. boolean
D. real

Solution

  1. Step 1: Identify pointer representation

    Write pointers are usually binary counters represented as vectors.
  2. Step 2: Choose appropriate type

    std_logic_vector is used to represent binary numbers in VHDL.
  3. Final Answer:

    std_logic_vector -> Option A
  4. Quick Check:

    Write pointer = std_logic_vector [OK]
Hint: Pointers are binary, so use std_logic_vector [OK]
Common Mistakes:
  • Using integer which is less hardware-friendly
  • Using boolean which can't count multiple bits
  • Using real which is for floating-point numbers
3. Given a synchronous FIFO with depth 4, write pointer at "10" (binary 2), read pointer at "01" (binary 1), and count = 2, what happens if write enable is high and FIFO is not full?
medium
A. Write pointer resets to 0 without writing data
B. Data is written at position 1, write pointer increments to 2, count becomes 3
C. Data is not written because FIFO is full
D. Data is written at position 2, write pointer increments to 3, count becomes 3

Solution

  1. Step 1: Check FIFO status

    Count is 2, depth is 4, so FIFO is not full.
  2. Step 2: Write operation effect

    Data writes at write pointer position 2, pointer increments to 3, count increments to 3.
  3. Final Answer:

    Data is written at position 2, write pointer increments to 3, count becomes 3 -> Option D
  4. Quick Check:

    Write enabled + not full = write and increment [OK]
Hint: Write pointer points where data writes; count increments if not full [OK]
Common Mistakes:
  • Writing at read pointer instead of write pointer
  • Not incrementing count after write
  • Assuming FIFO is full incorrectly
4. Identify the error in this VHDL snippet for a synchronous FIFO write process:
process(clk)
begin
if rising_edge(clk) then
if (write_enable = '1') and (not full) then
fifo_mem(write_ptr) <= data_in;
write_ptr <= write_ptr + 1;
count <= count + 1;
end if;
end if;
end process;
medium
A. write_ptr and count signals are not wrapped around FIFO depth
B. Missing reset condition for write_ptr
C. Data is written even when FIFO is full
D. Process is not sensitive to write_enable

Solution

  1. Step 1: Analyze pointer increment logic

    write_ptr increments without wrapping back to zero at FIFO depth limit.
  2. Step 2: Understand FIFO pointer behavior

    Write pointer must wrap around to zero after reaching max depth to avoid overflow.
  3. Final Answer:

    write_ptr and count signals are not wrapped around FIFO depth -> Option A
  4. Quick Check:

    Pointer wrap-around needed to avoid overflow [OK]
Hint: Always wrap pointers modulo FIFO depth [OK]
Common Mistakes:
  • Ignoring pointer wrap-around causing overflow
  • Assuming reset is mandatory in this snippet
  • Confusing sensitivity list with process behavior
5. You want to design a synchronous FIFO with depth 8 and 4-bit data width. Which of the following VHDL design choices correctly ensures no data overwrite and proper empty/full detection?
hard
A. Use 3-bit write and read pointers with a count signal; check full when count = 7 and empty when count = 0
B. Use 4-bit write and read pointers without count; check full when write_ptr = read_ptr and empty when write_ptr = read_ptr
C. Use 3-bit write and read pointers without count; check full when write_ptr + 1 = read_ptr and empty when write_ptr = read_ptr
D. Use 4-bit write and read pointers with count; check full when count = 15 and empty when count = 0

Solution

  1. Step 1: Determine pointer size

    Depth 8 requires 3-bit pointers (since 2^3=8).
  2. Step 2: Understand full/empty detection without count

    Full when write pointer is one less than read pointer (circular), empty when equal.
  3. Step 3: Evaluate options

    Use 3-bit write and read pointers without count; check full when write_ptr + 1 = read_ptr and empty when write_ptr = read_ptr correctly uses 3-bit pointers without count and proper full/empty logic.
  4. Final Answer:

    Use 3-bit write and read pointers without count; check full when write_ptr + 1 = read_ptr and empty when write_ptr = read_ptr -> Option C
  5. Quick Check:

    Pointer wrap + compare = full/empty detection [OK]
Hint: Use pointer difference for full/empty without count [OK]
Common Mistakes:
  • Using wrong pointer size for FIFO depth
  • Confusing full and empty conditions
  • Using count with incorrect max value
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard