Module fifo

This document contains technical documentation for the fifo module. To browse the source code, visit the repository on GitHub.

This module contains a very efficient and versatile FIFO implementation in VHDL. Supports both synchronous (one clock) and asynchronous (two clock) operations.

asynchronous_fifo.vhd

View source code on GitHub.

component asynchronous_fifo is
  generic (
    width : positive;
    depth : positive;
    almost_full_level : natural range 0 to depth;
    almost_empty_level : natural range 0 to depth;
    enable_last : boolean;
    enable_packet_mode : boolean;
    enable_drop_packet : boolean;
    enable_output_register : boolean;
    ram_type : ram_style_t
  );
  port (
    clk_write : in std_ulogic;
    write_ready : out std_ulogic;
    write_valid : in  std_ulogic;
    write_data  : in  std_ulogic_vector;
    write_last : in std_ulogic;
    write_level : out natural range 0 to depth;
    write_almost_full : out std_ulogic;
    drop_packet : in std_ulogic;
    --# {{}}
    clk_read : in std_ulogic;
    read_ready : in  std_ulogic;
    read_valid : out std_ulogic;
    read_data : out std_ulogic_vector;
    read_last : out std_ulogic;
    read_level : out natural range 0 to depth;
    read_almost_empty : out std_ulogic
  );
end component;

Asynchronous (two clocks) First In First Out (FIFO) data buffering stage with AXI-Stream-like handshaking interface. Supports last, packet mode and drop_packet just like the synchronous FIFO. The implementation is quite optimized with very low resource utilization when extra features are not enabled.

../../_images/asynchronous_fifo_circuit.png

For more CDC solutions, please see Module resync.

Note

This entity has a scoped constraint file asynchronous_fifo.tcl that must be used for proper operation. See here for instructions. This entity also instantiates resync_counter.vhd which has a further constraint file that must be used.

Warning

In case enable_output_register is set, the implementation does not keep track of the exact level on the write side. When there is no word in the output register, e.g when the FIFO is empty, the write_level reported will be one higher than the real level.

See the constraint file and this article for information about timing constraints and how this CDC topology is made reliable.

Resource utilization

This entity has netlist builds set up with automatic size checkers in module_fifo.py. The following table lists the resource utilization for the entity, depending on generic configuration.

Resource utilization for asynchronous_fifo netlist builds.

Generics

Total LUTs

FFs

Maximum logic level

RAMB36

RAMB18

use_asynchronous_fifo = True

width = 8

depth = 8

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

31

42

4

0

0

use_asynchronous_fifo = True

width = 16

depth = 8

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

35

50

4

0

0

use_asynchronous_fifo = True

width = 24

depth = 8

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

39

58

4

0

0

use_asynchronous_fifo = True

width = 32

depth = 8

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

47

66

4

0

0

use_asynchronous_fifo = True

width = 64

depth = 8

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

67

98

4

0

0

use_asynchronous_fifo = True

width = 32

depth = 1024

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

44

90

6

1

0

use_asynchronous_fifo = True

width = 32

depth = 1025

enable_output_register = True

(Using wrapper

fifo_netlist_build_wrapper.vhd)

45

91

6

1

0

width = 32

depth = 1024

almost_full_level = 800

68

112

6

1

0

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

68

112

6

1

0

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

enable_packet_mode = True

60

123

6

1

0

width = 32

depth = 1025

almost_full_level = 800

enable_last = True

enable_packet_mode = True

enable_output_register = True

76

135

8

1

0

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

enable_packet_mode = True

enable_output_register = False

enable_drop_packet = True

66

134

6

1

0

fifo.vhd

View source code on GitHub.

component fifo is
  generic (
    width : positive;
    depth : positive;
    almost_full_level : natural range 0 to depth;
    almost_empty_level : natural range 0 to depth;
    enable_last : boolean;
    enable_packet_mode : boolean;
    enable_drop_packet : boolean;
    enable_peek_mode : boolean;
    enable_output_register : boolean;
    ram_type : ram_style_t
  );
  port (
    clk : in std_ulogic;
    --# {{}}
    level : out natural range 0 to depth;
    --# {{}}
    write_ready : out std_ulogic;
    write_valid : in std_ulogic;
    write_data : in std_ulogic_vector;
    write_last : in std_ulogic;
    almost_full : out std_ulogic;
    drop_packet : in std_ulogic;
    --# {{}}
    read_ready : in std_ulogic;
    read_valid : out std_ulogic;
    read_data : out std_ulogic_vector;
    read_last : out std_ulogic;
    read_peek_mode : in std_ulogic;
    almost_empty : out std_ulogic
  );
end component;

Synchronous (one clock) First In First Out (FIFO) data buffering stage with AXI-Stream-like handshaking interface. This implementation is very versatile in terms of features that can be enabled. Despite this it is very optimized when used in its barebone configuration, and will result in a very small logic footprint.

Features that can be enabled:

  • If enable_last is set to true, the write_last signal will be concatenated with write_data and stored in RAM, and then passed on to read_last. Without this, read_last will have an undefined value and write_last will not be used.

  • FIFO packet mode is enabled by setting the generic enable_packet_mode to true. When this mode is enabled, read_valid will not be asserted until the whole “packet” has been written to FIFO, as indicated by write_valid and write_last.

  • The FIFO supports dropping packets that are in the progress of being written. If the enable_drop_packet generic is set to true, the drop_packet port can be used to drop the current packet, i.e. all words written since the last write_valid and write_last happened.

    The port can be asserted at any time, regardless of e.g. write_ready or write_valid.

  • Additionally there is a “peek read” mode available that is enabled by setting the enable_peek_mode generic to true. It makes it possible to read a packet multiple times. If the read_peek_mode signal is asserted when read_ready is asserted, the current packet will not be popped from the FIFO, but can instead be read again. Once the readout encounters read_last, the readout will return again to the first word of the packet. Note that the read_peek_mode value may not be changed during the readout of a packet. It must be static for all words in a packet, but may be updated after read_last.

  • There is an option to enable an output register using the enable_output_register generic. This can be used to improve timing since the routing delay on the data output of a block RAM is usually quite high. Most block RAM primitives have a “hard” output register that can be used. It has been verified (with Netlist builds in CI) that the implementation in this file will map to the hard output register in Xilinx 7-series devices, and hence not consume extra flip-flops.

    Note

    The “peek read” mode can not be used when output register is enabled.

Resource utilization

This entity has netlist builds set up with automatic size checkers in module_fifo.py. The following table lists the resource utilization for the entity, depending on generic configuration.

Resource utilization for fifo netlist builds.

Generics

Total LUTs

FFs

RAMB36

RAMB18

Maximum logic level

use_asynchronous_fifo = False

width = 32

depth = 1024

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

14

24

1

0

6

use_asynchronous_fifo = False

width = 32

depth = 1025

enable_output_register = True

(Using wrapper

fifo_netlist_build_wrapper.vhd)

15

25

1

0

6

width = 32

depth = 1024

almost_full_level = 800

27

35

1

0

6

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

27

35

1

0

6

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

enable_packet_mode = True

40

47

1

0

6

width = 32

depth = 1025

almost_full_level = 800

enable_last = True

enable_packet_mode = True

enable_output_register = True

45

50

1

0

9

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

enable_packet_mode = True

enable_output_register = False

enable_drop_packet = True

45

58

1

0

6

width = 32

depth = 1024

almost_full_level = 800

enable_last = True

enable_packet_mode = True

enable_output_register = False

enable_drop_packet = False

enable_peek_mode = True

58

58

1

0

6

use_asynchronous_fifo = False

width = 8

depth = 32

enable_output_register = False

(Using wrapper

fifo_netlist_build_wrapper.vhd)

32

22

0

0

3

fifo_wrapper.vhd

View source code on GitHub.

component fifo_wrapper is
  generic (
    use_asynchronous_fifo : boolean;
    width : positive;
    depth : natural;
    almost_full_level : natural range 0 to depth;
    almost_empty_level : natural range 0 to depth;
    enable_last : boolean;
    enable_packet_mode : boolean;
    enable_drop_packet : boolean;
    enable_peek_mode : boolean;
    enable_output_register : boolean;
    ram_type : ram_style_t
  );
  port (
    clk : in std_ulogic;
    clk_write : in std_ulogic;
    clk_read : in std_ulogic;
    --# {{}}
    write_ready : out std_ulogic;
    write_valid : in  std_ulogic;
    write_data : in  std_ulogic_vector;
    write_last : in std_ulogic;
    write_level : out natural range 0 to depth;
    almost_full : out std_ulogic;
    drop_packet : in std_ulogic;
    --# {{}}
    read_ready : in  std_ulogic;
    read_valid : out std_ulogic;
    read_data : out std_ulogic_vector;
    read_last : out std_ulogic;
    read_peek_mode : in std_ulogic;
    read_level : out natural range 0 to depth;
    almost_empty : out std_ulogic
  );
end component;

Wrapper that selects synchronous/asynchronous FIFO or passthrough depending on generic values.