2.2.10 Synchronous FIFO

May 27, 2025 ยท View on GitHub

Synchronous FIFO

A synchronous FIFO (First-In First-Out) buffer provides temporary storage between circuits that process data at different rates but share the same clock domain. It allows a producer to write data when ready and a consumer to read it later, while maintaining the original order of the data.

Verilog

module sync_fifo (
  input clock, clear, write, read,
  input [15:0] data_in,
  output reg [15:0] data_out,
  output full, empty
);

  reg [2:0] wptr, rptr;
  reg [15:0] mem[0:7];

  assign full = (wptr + 1) == rptr;
  assign empty = wptr == rptr;

  wire write_incr = write & !full;
  wire read_incr = read & !empty;

  always @(posedge clock) begin
    if (clear) begin
      rptr <= 0;
      wptr <= 0;
    end else begin
      if (write_incr) wptr <= wptr + 1;
      if (read_incr) rptr <= rptr + 1;
    end
    if (write_incr) mem[wptr] <= data_in;
    if (read_incr) data_out <= mem[rptr];
  end

endmodule

VHDL

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

entity sync_fifo is
  port (
    clock, clear, write, read : in std_logic;
    data_in : in std_logic_vector(15 downto 0);
    data_out : out std_logic_vector(15 downto 0);
    full, empty : out std_logic
  );
end entity;

architecture rtl of sync_fifo is
  type mem_t is array (0 to 7) of std_logic_vector(15 downto 0);
  signal mem : mem_t;

  signal wptr, rptr : unsigned(2 downto 0);
  signal write_incr : std_logic;
  signal read_incr : std_logic;
  signal full_int : std_logic;
  signal empty_int : std_logic;
begin

  full_int <= '1' when (wptr + 1) = rptr else '0';
  empty_int <= '1' when wptr = rptr else '0';
  full <= full_int;
  empty <= empty_int;

  write_incr <= write and (not full_int);
  read_incr <= read and (not full_int);

  process (clock) begin
    if rising_edge(clock) then
      if clear = '1' then
        rptr <= (others => '0');
        wptr <= (others => '0');
      else
        if write_incr = '1' then 
          wptr <= wptr + 1;
        end if;
        if read_incr = '1' then 
          rptr <= rptr + 1;
        end if;
      end if;

      if write_incr = '1' then
        mem(to_integer(wptr)) <= data_in;
      end if;
      if read_incr = '1' then
        data_out <= mem(to_integer(rptr));
      end if;
    end if;
  end process;
end architecture;

Hardcaml

  type t =
    { data_out : Signal.t
    ; full : Signal.t
    ; empty : Signal.t
    }

  let sync_fifo ~clock ~clear ~write ~read ~data_in =
    let spec = Reg_spec.create ~clock ~clear () in
    let wptr = wire log_depth in
    let wptr_next = wptr +:. 1 in
    let rptr = wire log_depth in
    let full = wptr_next ==: rptr in
    let empty = wptr ==: rptr in
    let write_enable = write &: ~:full in
    let read_enable = read &: ~:empty in
    let mem =
      Ram.create
        ~collision_mode:Write_before_read
        ~size:depth
        ~write_ports:
          [| { write_clock = clock
             ; write_data = data_in
             ; write_enable
             ; write_address = wptr
             }
          |]
        ~read_ports:[| { read_clock = clock; read_enable; read_address = rptr } |]
        ()
    in
    wptr <-- reg spec ~enable:write_enable wptr_next;
    rptr <-- reg spec ~enable:read_enable (rptr +:. 1);
    { data_out = mem.(0); full; empty }
  ;;