library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_unsigned.all;
use ieee.numeric_extra.all;
library work_lib;
entity fifo_async is
  generic (
    WIDTH_g : integer := 8);            -- Width of fifo
  port(
    wrst_n : in  std_logic;             -- asynchronous active low write reset
    wclk   : in  std_logic;             -- write clock input
    wen    : in  std_logic;             -- fifo active low write enable
    wdata  : in  std_logic_vector(WIDTH_g-1 downto 0);  -- write data 
    wfull  : out std_logic;             -- Write full
                                        -- 
    rrst_n : in  std_logic;             -- read reset active low
    rclk   : in  std_logic;             -- fifo read clock
    ren    : in  std_logic;             -- fifo read enable (active low)
    rdata  : out std_logic_vector(WIDTH_g-1 downto 0);  -- read data
    rempty : out std_logic              -- fifo empty 
    );
end entity fifo_async;

architecture rtl of fifo_async is
  constant DEPTHEXP : integer := 2;     -- depth exponent of fifo  
  type     memory_typ is array(0 to 2**DEPTHEXP - 1) of
    std_logic_vector(WIDTH_g-1 downto 0);
  signal fifomem       : memory_typ;
  -- Counters are one bit larger to detect FULL/EMPTY states
  signal waddr_wrside  : std_logic_vector(DEPTHEXP downto 0);  -- write address
  signal raddr_rdside  : std_logic_vector(DEPTHEXP downto 0);  -- read address;
  signal waddr_rdside0  : std_logic_vector(DEPTHEXP downto 0);  -- 1st stage 
  signal waddr_rdside  : std_logic_vector(DEPTHEXP downto 0);  -- write address  
  signal raddr_wrside0  : std_logic_vector(DEPTHEXP downto 0);  -- read address;
  signal raddr_wrside  : std_logic_vector(DEPTHEXP downto 0);  -- read address;  
  signal rdbincount_wrside : std_logic_vector(DEPTHEXP downto 0);  -- write address
  signal wrbincount_wrside : std_logic_vector(DEPTHEXP downto 0);  -- write address  
  signal rdbincount_rdside  : std_logic_vector(DEPTHEXP downto 0);  -- read address;
  signal we : std_logic;                -- write enbla true
  signal re : std_logic;                -- read enable true

  function gray2binary (gray : std_logic_vector)  return std_logic_vector is
  variable bin : std_logic_vector(gray'length -1 downto 0);
  begin 
    for i in bin'range loop
      bin(i) := xor_reduce(gray(gray'length-1 downto i));
    end loop;  -- Gray_lp
    return bin;
  end function gray2binary;

  
begin  -- architecture rtl

  -- Latches the write address at the read side.
  Waddr_RDside_Proc: process (rclk, rrst_n) is
  begin  -- process Waddr_RDside_Proc
    if (rrst_n = '0') then
      waddr_rdside0 <= (others => '0');
      waddr_rdside <= (others => '0');      
    elsif (rclk'event and rclk = '1') then
      waddr_rdside0 <= waddr_wrside;     -- synchronize write address
      waddr_rdside <= waddr_rdside0;    -- 2nd metastable FF
    end if;
  end process Waddr_RDside_Proc;


  -- Latches the read  address at the write side.
  Rdaddr_WRside_Proc: process (wclk, wrst_n) is
  begin  -- process Waddr_RDside_Proc
    if (wrst_n = '0') then
      raddr_wrside0 <= (others => '0');
      raddr_wrside <= (others => '0');      
    elsif (wclk'event and wclk = '1') then
      raddr_wrside0 <= raddr_rdside;     -- synchronize read address
      raddr_wrside <= raddr_wrside0;    -- 2nd metastability FF 
    end if;
  end process Rdaddr_WRside_Proc;

  -- purpose: fifo memory
  process (wclk) is
  begin  -- process
    if (wclk'event and wclk = '1') then
      if (wen = '0') then
        fifomem(to_integer(wrbincount_wrside(DEPTHEXP-1 downto 0))) <= wdata;
      end if;
    end if;
  end process;

  -- fifo write pointer counter
  graycntr_wrptr : entity work_lib.graycntr
    generic map (
      SIZE => DEPTHEXP + 1)
    port map (
      gray  => waddr_wrside,                    -- [out]
      bincount => wrbincount_wrside,      -- [out]
      clk   => wclk,                     -- [in]  clock
      inc   => we,                     -- [in]  increment
      rst_n => wrst_n);                  -- [in] reset, active LO

  -- fifo read pointer counter 
  graycntr_rdptr : entity work_lib.graycntr
    generic map (
      SIZE => DEPTHEXP + 1)
    port map (
      gray  => raddr_rdside,                    -- [out]
      bincount => rdbincount_rdside,      --[out]
      clk   => rclk,                     -- [in]  clock
      inc   => re,                     -- [in]  increment
      rst_n => rrst_n);                  -- [in] reset, active LO


  rdata <= fifomem(to_integer(rdbincount_rdside(DEPTHEXP-1 downto 0)));

  re <= not ren;

  we <= not wen;

  rempty <= '1' when (raddr_rdside = waddr_rdside) else
            '0';

  rdbincount_wrside <= gray2binary(raddr_wrside);

  wfull <= '1' when (rdbincount_wrside(DEPTHEXP-1 downto 0) =
                     wrbincount_wrside(DEPTHEXP-1 downto 0)) and
                    (rdbincount_wrside(DEPTHEXP) /=
                     wrbincount_wrside(DEPTHEXP)) else
           '0';
end architecture rtl;
