

--
-- Synchronous FIFO using up to 4 BRAMs providing up to 144 bits wide
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_misc.all;



-- synthesis translate_off
library UNISIM;
use UNISIM.all;
-- synthesis translate_on

entity sync_fifo_bram is
  generic (
    width : natural := 32);
  port (
    clk : in std_logic;
    rst : in std_logic;
    wadv : in std_logic;
    wdata : in std_logic_vector(width-1 downto 0);
    wnfull : out std_logic;
    wfull  : out std_logic;
    hfull  : out std_logic;
    radv  : in std_logic;
    rdata : out std_logic_vector(width-1 downto 0);
    rdw_out : out std_logic_vector(width-1 downto 0);
    rempty : out std_logic;
    rnempty : out std_logic
    );

end sync_fifo_bram;

architecture rtl of sync_fifo_bram is

  component RAMB16_S36_S36
       --synthesis translate_off
    generic(
      SIM_COLLISION_CHECK : STRING);
       --synthesis translate_on
    port(
      DOA   : out std_logic_vector(31 downto 0);
      DOPA  : out std_logic_vector(3 downto 0);
      DOB   : out std_logic_vector(31 downto 0);
      DOPB  : out std_logic_vector(3 downto 0);
      ADDRA : in  std_logic_vector(8 downto 0);
      DIA   : in  std_logic_vector(31 downto 0);
      DIPA  : in  std_logic_vector(3 downto 0);
      ENA   : in  std_logic;
      CLKA  : in  std_logic;
      WEA   : in  std_logic;
      SSRA  : in  std_logic;
      ADDRB : in  std_logic_vector(8 downto 0);
      DIB   : in  std_logic_vector(31 downto 0);
      DIPB  : in  std_logic_vector(3 downto 0);
      ENB   : in  std_logic;
      CLKB  : in  std_logic;
      WEB   : in  std_logic;
      SSRB  : in  std_logic);
  end component;

  signal rdw,wdw : std_logic_vector(width-1 downto 0);
  signal rdw1,wdw1 : std_logic_vector(143 downto 0);

  signal radv_q,wadv_q,empty,lempty : std_logic;
  signal wadv_q_reg : std_logic;
  signal full_r,empty_x,nempty : std_logic;

  signal waddr,raddr,raddr_p1,count,next_raddr : std_logic_vector(8 downto 0);

  signal zeros : std_logic_vector(31 downto 0) := (others => '0');
  
begin

  update_addr: process (clk, rst)
  begin
    if rst = '1' then
      waddr <= (others => '0');
      raddr <= (others => '0');
      raddr_p1 <= "000000001";
      count <= (others => '0');
      wadv_q_reg <= '0';
    elsif clk'event and clk = '1' then
      if radv_q = '1' then
        raddr <= raddr_p1;
        raddr_p1 <= raddr_p1+1;
      end if;
      if wadv_q ='1' then
        waddr <= waddr+1;
      end if;
      if radv_q = '1' and wadv_q_reg = '0' then
        count <= count -1;
      else
        if wadv_q_reg = '1' and radv_q = '0' then
          count <= count+1;
        end if;
      end if;
      wadv_q_reg <= wadv_q;
    end if;
  end process update_addr;

  
  empty <= '1' when count="000000000" else '0';
  nempty <= '1' when count="000000001" else '0';
  full_r <= '1' when count(8 downto 1)="11111111" else '0';

  wnfull <= '1' when count(8 downto 7) = "11" else '0'; 

  hfull <= count(8) or count(7);
  
  radv_q <= radv and not empty_x and not lempty;
  wadv_q <= wadv and not full_r;
  wfull <= full_r;

  rempty <= empty_x or lempty;
  rnempty <= nempty;

  next_raddr <= raddr_p1 when radv_q = '1' else raddr;
  
  r_op: process (clk, rst)
  begin
    if rst = '1' then
      rdata <= (others => '0');
      lempty <= '1';
      empty_x <= '1';
    elsif clk'event and clk = '1' then
      if radv = '1' then      
        rdata <= rdw;      
      end if;
      lempty <= empty;
      empty_x<= nempty and radv;        
    end if;
  end process r_op;

  wdw1(width-1 downto 0) <= wdata; 
  rdw <= rdw1(width-1 downto 0);
  
  ram0: RAMB16_S36_S36
    --synthesis translate_off
    generic map(
      SIM_COLLISION_CHECK => "none" )
    --synthesis translate_on
    port map (
      DOA   => open,
      DOPA  => open,
      DOB   => rdw1(31 downto 0),
      DOPB  => rdw1(35 downto 32),
      ADDRA => waddr,
      DIA   => wdw1(31 downto 0),
      DIPA  => wdw1(35 downto 32),
      ENA   => '1',
      CLKA  => clk,
      WEA   => wadv_q,
      SSRA  => '0',
      ADDRB => next_raddr,
      DIB   => zeros(31 downto 0),
      DIPB  => zeros(3 downto 0),
      ENB   => '1',
      CLKB  => clk,
      WEB   => '0',
      SSRB  => '0');

  ram1: RAMB16_S36_S36
    --synthesis translate_off
    generic map(
           SIM_COLLISION_CHECK => "none"  )
    --synthesis translate_on
    port map (
      DOA   => open,
      DOPA  => open,
      DOB   => rdw1(67 downto 36),
      DOPB  => rdw1(71 downto 68),
      ADDRA => waddr,
      DIA   => wdw1(67 downto 36),
      DIPA  => wdw1(71 downto 68),
      ENA   => '1',
      CLKA  => clk,
      WEA   => wadv_q,
      SSRA  => '0',
      ADDRB => next_raddr,
      DIB   => zeros(31 downto 0),
      DIPB  => zeros(3 downto 0),
      ENB   => '1',
      CLKB  => clk,
      WEB   => '0',
      SSRB  => '0');
  ram2: RAMB16_S36_S36
    --synthesis translate_off
    generic map(
           SIM_COLLISION_CHECK => "none"  )
    --synthesis translate_on    
    port map (
      DOA   => open,
      DOPA  => open,
      DOB   => rdw1(103 downto 72),
      DOPB  => rdw1(107 downto 104),
      ADDRA => waddr,
      DIA   => wdw1(103 downto 72),
      DIPA  => wdw1(107 downto 104),
      ENA   => '1',
      CLKA  => clk,
      WEA   => wadv_q,
      SSRA  => '0',
      ADDRB => next_raddr,
      DIB   => zeros(31 downto 0),
      DIPB  => zeros(3 downto 0),
      ENB   => '1',
      CLKB  => clk,
      WEB   => '0',
      SSRB  => '0');
  ram3: RAMB16_S36_S36
    --synthesis translate_off
    generic map(
       SIM_COLLISION_CHECK => "none" )
    --synthesis translate_on
    port map (
      DOA   => open,
      DOPA  => open,
      DOB   => rdw1(139 downto 108),
      DOPB  => rdw1(143 downto 140),
      ADDRA => waddr,
      DIA   => wdw1(139 downto 108),
      DIPA  => wdw1(143 downto 140),
      ENA   => '1',
      CLKA  => clk,
      WEA   => wadv_q,
      SSRA  => '0',
      ADDRB => next_raddr,
      DIB   => zeros(31 downto 0),
      DIPB  => zeros(3 downto 0),
      ENB   => '1',
      CLKB  => clk,
      WEB   => '0',
      SSRB  => '0');

  
  rdw_out <= rdw;

end rtl;

