---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2008
--
-- Filename: RamArbitrate.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- Sample RAM arbitration between write data path and processor access
---------------------------------------------------------------------------

library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_Arith.all;

library UNISIM;
use UNISIM.vcomponents.all;

entity RamArbitrate is
  port (nReset          : in  std_ulogic;
        RamClk          : in  std_ulogic;
        -- SDRAM interface i/o
        RamDIn          : out std_logic_vector(127 downto 0);
        RamDMask        : out std_logic_vector(15 downto 0);
        RamDOut         : in  std_logic_vector(127 downto 0);
        RamAddr         : out std_logic_vector(30 downto 5);
        RamWr           : out std_ulogic;
        RamWrPop        : in  std_ulogic;
        RamAck          : in  std_ulogic;
        RamRd           : out std_ulogic;
        RamRdPush       : in  std_ulogic;
        -- Local Processor bus
        LClk            : in  std_ulogic;
        LWrData         : in  std_logic_vector(31 downto 0);
        LRdData         : out std_logic_vector(31 downto 0);
        LAddr           : in  std_logic_vector(30 downto 0);
        LWr             : in  std_ulogic;
        LRd             : in  std_ulogic;
        LAck            : out std_ulogic;
        -- Data input
        SysClk          : in  std_ulogic;
        DataIn          : in  std_logic_vector(31 downto 0);
        DataWrA         : in  std_ulogic;
        DataWrC         : in  std_ulogic;
        ResetAScanAddr  : in  std_ulogic;
        ResetCScanAddr  : in  std_ulogic;
        BankSel         : in  std_ulogic;
        -- Register signals
        CScanOffsetAddr : in  std_logic_vector(30 downto 0);
        BankSize        : in  std_logic_vector(30 downto 0);
        ResetSampling   : in  std_ulogic);
end entity RamArbitrate;


architecture Rtl of RamArbitrate is
  component SyncPulseUp
    port (InClock  : in  std_ulogic;
          OutClock : in  std_ulogic;
          nReset   : in  std_ulogic;
          DIn      : in  std_ulogic;
          DOut     : out std_ulogic);
  end component;

  signal ADataEn        : std_logic_vector(3 downto 0);
  signal AFifoWrAddr    : std_logic_vector(7 downto 0);
  signal IncAFifoCount  : std_logic_vector(2 downto 0);
  signal AFinishBlock   : std_ulogic;
  signal CDataEn        : std_logic_vector(3 downto 0);
  signal CFifoWrAddr    : std_logic_vector(7 downto 0);
  signal IncCFifoCount  : std_logic_vector(2 downto 0);
  signal CFinishBlock   : std_ulogic;

  signal DataInD        : std_logic_vector(31 downto 0);
  signal DataWrD        : std_ulogic;
  signal DataEnD        : std_logic_vector(3 downto 0);
  signal FifoWrAddrD    : std_logic_vector(8 downto 0);

  signal FifoRdData     : std_logic_vector(127 downto 0);
  signal AScanAddr      : unsigned(31 downto 0);
  signal CScanAddr      : unsigned(31 downto 0);
  signal RstAAddr       : std_logic_vector(1 downto 0);
  signal RstCAddr       : std_logic_vector(1 downto 0);

  signal ResetSamplingSys : std_ulogic;
  signal nResetSampling   : std_ulogic;

  signal ResetAScanAddrPulse: std_ulogic;
  signal ResetAScanAddrRam  : std_ulogic;
  signal ResetCScanAddrPulse: std_ulogic;
  signal ResetCScanAddrRam  : std_ulogic;

  signal IncAFifoCountSync: std_logic_vector(2 downto 0);
  signal AFifoCount       : unsigned(7 downto 0);
  signal AFifoRdAddr      : std_logic_vector(7 downto 0);
  signal IncCFifoCountSync: std_logic_vector(2 downto 0);
  signal CFifoCount       : unsigned(7 downto 0);
  signal CFifoRdAddr      : std_logic_vector(7 downto 0);
  signal FifoRdAddr       : std_logic_vector(8 downto 0);
  signal FifoRd           : std_ulogic;
  signal FifoRdnAC        : std_ulogic;

  signal RamWrPopD1 : std_ulogic;
  signal RamWrPopD2 : std_ulogic;
  signal RamRdPushD : std_ulogic;
  signal RamLAck    : std_ulogic;
  signal iLAck      : std_ulogic;
  signal iRamWr     : std_ulogic;
  signal iRamRd     : std_ulogic;
  signal LDelay     : std_ulogic;
  signal DWordSel   : integer range 0 to 3;

begin

  SampReset: SyncPulseUp
    port map (InClock  => LClk,
              OutClock => SysClk,
              nReset   => nReset,
              DIn      => ResetSampling,
              DOut     => ResetSamplingSys);

  nResetSampling <= '0' when nReset = '0' or ResetSamplingSys = '1' else '1';

  AFifoCtrl: process(nResetSampling, SysClk)
  begin
    if nResetSampling = '0' then
      ADataEn <= "0001";
      AFifoWrAddr <= (others => '0');
      IncAFifoCount <= "000";
      AFinishBlock <= '0';
      ResetAScanAddrPulse <= '0';
    elsif SysClk'event and SysClk = '1' then
      IncAFifoCount <= IncAFifoCount(1 downto 0) & '0';
      AFinishBlock <= '0';
      ResetAScanAddrPulse <= '0';

      if DataWrA = '1' then
        ADataEn <= ADataEn(2 downto 0) & ADataEn(3);
        if ADataEn(3) = '1' then
          AFifoWrAddr <= unsigned(AFifoWrAddr) + 1;
          IncAFifoCount <= "111";
        end if;
      end if;
      if (ResetAScanAddr = '1' or AFinishBlock = '1') and AFifoWrAddr(1 downto 0) /= "00" then
        if IncAFifoCount(2) = '0' then
          AFifoWrAddr <= unsigned(AFifoWrAddr) + 1;
          IncAFifoCount <= "111";
        end if;

        AFinishBlock <= '1';
        else
          if ResetAScanAddr = '1' or AFinishBlock = '1' then
            ResetAScanAddrPulse <= '1';
          end if;
      end if;

      if ResetAScanAddr = '1' then
        ADataEn <= "0001";
      end if;
    end if;
  end process AFifoCtrl;

  CFifoCtrl: process(nResetSampling, SysClk)
  begin
    if nResetSampling = '0' then
      CDataEn <= "0001";
      CFifoWrAddr <= (others => '0');
      IncCFifoCount <= "000";
      CFinishBlock <= '0';
      ResetCScanAddrPulse <= '0';
    elsif SysClk'event and SysClk = '1' then
      IncCFifoCount <= IncCFifoCount(1 downto 0) & '0';
      CFinishBlock <= '0';
      ResetCScanAddrPulse <= '0';

      if DataWrC = '1' then
        CDataEn <= CDataEn(2 downto 0) & CDataEn(3);
        if CDataEn(3) = '1' then
          CFifoWrAddr <= unsigned(CFifoWrAddr) + 1;
          IncCFifoCount <= "111";
        end if;
      end if;
      if (ResetCScanAddr = '1' or CFinishBlock = '1') and CFifoWrAddr(1 downto 0) /= "00" then
        if IncCFifoCount(2) = '0' then
          CFifoWrAddr <= unsigned(CFifoWrAddr) + 1;
          IncCFifoCount <= "111";
        end if;

        CFinishBlock <= '1';
      else
        if ResetCScanAddr = '1' or CFinishBlock = '1' then
          ResetCScanAddrPulse <= '1';
        end if;
      end if;

      if ResetCScanAddr = '1' then
        CDataEn <= "0001";
      end if;
    end if;
  end process CFifoCtrl;

  -- Data FIFO
  FifoDFF: process(nResetSampling, SysClk)
  begin
    if nResetSampling = '0' then
      DataInD <= (others => '0');
      DataWrD <= '0';
      DataEnD <= (others => '0');
      FifoWrAddrD <= (others => '0');
    elsif SysClk'event and SysClk = '1' then
      if DataWrC = '0' then
        DataEnD <= ADataEn;
        FifoWrAddrD <= '0' & AFifoWrAddr;
      else
        DataEnD <= CDataEn;
        FifoWrAddrD <= '1' & CFifoWrAddr;
      end if;
      DataInD <= DataIn;
      DataWrD <= DataWrA or DataWrC;
    end if;
  end process FifoDFF;


  -- Sample FIFO
  Fifo0: RAMB16_S36_S36
  	port map (DOA   => open,
  	          DOB   => FifoRdData(31 downto 0),
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => FifoWrAddrD,
  	          ADDRB => FifoRdAddr,
  	          CLKA  => SysClk,
  	          CLKB  => RamClk,
  	          DIA   => DataInD,
  	          DIB   => x"0000_0000",
  	          DIPA  => "0000",
  	          DIPB  => "0000",
  	          ENA   => DataEnD(0),
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => DataWrD,
  	          WEB   => '0');

  Fifo1: RAMB16_S36_S36
    port map (DOA   => open,
  	          DOB   => FifoRdData(63 downto 32),
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => FifoWrAddrD,
  	          ADDRB => FifoRdAddr,
  	          CLKA  => SysClk,
  	          CLKB  => RamClk,
  	          DIA   => DataInD,
  	          DIB   => x"0000_0000",
  	          DIPA  => "0000",
  	          DIPB  => "0000",
  	          ENA   => DataEnD(1),
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => DataWrD,
  	          WEB   => '0');

  Fifo2: RAMB16_S36_S36
  	port map (DOA   => open,
  	          DOB   => FifoRdData(95 downto 64),
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => FifoWrAddrD,
  	          ADDRB => FifoRdAddr,
  	          CLKA  => SysClk,
  	          CLKB  => RamClk,
  	          DIA   => DataInD,
  	          DIB   => x"0000_0000",
  	          DIPA  => "0000",
  	          DIPB  => "0000",
  	          ENA   => DataEnD(2),
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => DataWrD,
  	          WEB   => '0');

  Fifo3: RAMB16_S36_S36
  	port map (DOA   => open,
  	          DOB   => FifoRdData(127 downto 96),
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => FifoWrAddrD,
  	          ADDRB => FifoRdAddr,
  	          CLKA  => SysClk,
  	          CLKB  => RamClk,
  	          DIA   => DataInD,
  	          DIB   => x"0000_0000",
  	          DIPA  => "0000",
  	          DIPB  => "0000",
  	          ENA   => DataEnD(3),
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => DataWrD,
  	          WEB   => '0');

  -- Fifo Read and Level count
  AFifoCounters: process(nResetSampling, RamClk)
  begin
  	if nResetSampling = '0' then
  	  IncAFifoCountSync <= (others => '0');
  	  AFifoCount <= (others => '0');
  	  AFifoRdAddr <= (others => '0');
  	elsif RamClk'event and RamClk = '1' then
      IncAFifoCountSync <= IncAFifoCountSync(1 downto 0) & IncAFifoCount(1);

      if (IncAFifoCountSync(1) = '1' and IncAFifoCountSync(2) = '0') and not (RamWrPop = '1' and FiFoRd = '1' and FifoRdnAC = '0') then
        AFifoCount <= AFifoCount + 1;
      elsif not (IncAFifoCountSync(1) = '1' and IncAFifoCountSync(2) = '0') and (RamWrPop = '1' and FifoRd = '1' and FifoRdnAC = '0') then
        AFifoCount <= AFifoCount - 1;
      end if;

 	    if RamWrPop = '1' and FifoRdnAC = '0' then
        AFifoRdAddr <= unsigned(AFifoRdAddr) + 1;
  	  end if;
  	end if;
  end process AFifoCounters;

  CFifoCounters: process(nResetSampling, RamClk)
  begin
  	if nResetSampling = '0' then
  	  IncCFifoCountSync <= (others => '0');
  	  CFifoCount <= (others => '0');
  	  CFifoRdAddr <= (others => '0');
  	elsif RamClk'event and RamClk = '1' then
      IncCFifoCountSync <= IncCFifoCountSync(1 downto 0) & IncCFifoCount(1);

      if (IncCFifoCountSync(1) = '1' and IncCFifoCountSync(2) = '0') and not (RamWrPop = '1' and FiFoRd = '1' and FifoRdnAC = '1') then
        CFifoCount <= CFifoCount + 1;
      elsif not (IncCFifoCountSync(1) = '1' and IncCFifoCountSync(2) = '0') and (RamWrPop = '1' and FifoRd = '1' and FifoRdnAC = '1') then
        CFifoCount <= CFifoCount - 1;
      end if;

      if RamWrPop = '1'  and FifoRdnAC = '1' then
        CFifoRdAddr <= unsigned(CFifoRdAddr) + 1;
  	  end if;
  	end if;
  end process CFifoCounters;

  FifoRdAddr <= '0' & AFifoRdAddr when FifoRdnAC = '0' else '1' & CFifoRdAddr;

  ResetA: SyncPulseUp
    port map (InClock  => SysClk,
              OutClock => RamClk,
              nReset   => nReset,
              DIn      => ResetAScanAddrPulse,
              DOut     => ResetAScanAddrRam);

  ResetC: SyncPulseUp
    port map (InClock  => SysClk,
              OutClock => RamClk,
              nReset   => nReset,
              DIn      => ResetCScanAddrPulse,
              DOut     => ResetCScanAddrRam);

  RstDFF: process(nResetSampling, RamClk)
  begin
    if nResetSampling = '0' then
      RstAAddr <= "00";
      RstCAddr <= "00";
    elsif RamClk'event and RamClk = '1' then
      
      if ResetAScanAddrRam = '1' then
        RstAAddr <= "01";
      elsif RstAAddr(0) = '1' and AFifoCount /= 0 then
        RstAAddr <= "01";
      else
        RstAAddr <= RstAAddr(0) & '0';
      end if;

      if ResetCScanAddrRam = '1' then
        RstCAddr <= "01";
      elsif RstCAddr(0) = '1' and CFifoCount /= 0 then
        RstAAddr <= "01";
      else
        RstCAddr <= RstCAddr(0) & '0';
      end if;
    end if;
  end process RstDFF;

  -- A-Scan address generation
  AScanACounter: process(nReset, RamClk)
  begin
    if nReset = '0' then
      AScanAddr <= (others => '0');
    elsif RamClk'event and RamClk = '1' then
      if RstAAddr(1) = '1' then
        if BankSel = '0' then
          AScanAddr <= (others => '0');
        else
          AScanAddr <= '0' & unsigned(BankSize);
        end if;
      elsif RamAck = '1' and FifoRd = '1' and FifoRdnAC = '0' then
          AScanAddr <= AScanAddr + 32;
      end if;

    end if;
  end process AScanACounter;

  -- C-Scan address generation
  CScanACounter: process(nReset, RamClk)
  begin
    if nReset = '0' then
      CScanAddr <= (others => '0');
    elsif RamClk'event and RamClk = '1' then
      if RstCAddr(1) = '1' then
        if BankSel = '0' then
          CScanAddr <= '0' & unsigned(CScanOffsetAddr);
        else
          CScanAddr <= '0' & (unsigned(BankSize) + unsigned(CScanOffsetAddr));
        end if;
      elsif RamAck = '1' and FifoRd = '1' and FifoRdnAC = '1' then
          CScanAddr <= CScanAddr + 32;
      end if;

    end if;
  end process CScanACounter;

  -- Ram Access State machine
  RamSM: process(nReset, RamClk)
  begin
  	if nReset = '0' then
      FifoRd <= '0';
      FifoRdnAC <= '0';
      iRamWr <= '0';
      iRamRd <= '0';
    	RamLAck <= '0';
      LDelay <= '0';
  	elsif RamClk'event and RamClk = '1' then
      if RamAck = '1' then
        FifoRd <= '0';
        iRamWr <= '0';
        iRamRd <= '0';
      elsif iRamWr = '0' and iRamRd = '0' then
        if AFifoCount >= 2 then
          FifoRd <= '1';
          FifoRdnAC <= '0';
          iRamWr <= '1';
        elsif CFifoCount >= 2 then
          FifoRd <= '1';
          FifoRdnAC <= '1';
          iRamWr <= '1';
        elsif LWr = '1' and LDelay = '0' then
          iRamWr <= '1';
        elsif LRd = '1' and LDelay = '0' then
          iRamRd <= '1';
        end if;
      end if;
      if RamAck = '1' and FifoRd = '0' then
        RamLAck <= '1';
      elsif iLack = '1' then
        RamLAck <= '0';
      end if;

      if RamAck = '1' and FifoRd = '0' then
      	LDelay <= '1';
      elsif LWr = '0' and LRd = '0' then
        LDelay <= '0';
      end if;
  	end if;
  end process RamSM;

  RamWr <= iRamWr;
  RamRd <= iRamRd;

  LBusSM: process(nReset, LClk)
  begin
  	if nReset = '0' then
      iLAck <= '0';
  	elsif LClk'event and LClk = '1' then
      if iLAck = '1' then
        iLAck <= '0';
      elsif RamLAck = '1' then
        iLAck <= '1';
      end if;
  	end if;
  end process LBusSM;

  LAck <= iLAck;

  DWordSel <= conv_integer(unsigned(LAddr(3 downto 2)));

  -- Mux RAM data, mask and address
  RamOpFF: process(nReset, RamClk)
  begin
  	if nReset = '0' then
      RamWrPopD1 <= '0';
      RamWrPopD2 <= '0';
    	RamDIn <= (others => '0');
    	RamDMask <= (others => '0');
  	  RamAddr <= (others => '0');
  	elsif RamClk'event and RamClk = '1' then
        RamWrPopD1 <= RamWrPop;
        RamWrPopD2 <= RamWrPopD1;
      if FifoRd = '1' then
        RamDIn <= FifoRdData;
        RamDMask <= (others => '1');
        if FifoRdnAC = '0' then
          RamAddr <= std_logic_vector(AScanAddr(30 downto 5));
        else
          RamAddr <= std_logic_vector(CScanAddr(30 downto 5));
        end if;
      else
        RamDIn <= LWrData & LWrData & LWrData & LWrData;
        RamDMask <= (others => '0');
        if iRamWr = '1' and RamWrPopD2 = '0' and Laddr(4) = '0' then
          RamDMask(((DWordSel*4)+3) downto (DWordSel*4)) <= (others => '1');
        end if;
        if iRamWr = '1' and RamWrPopD2 = '1' and Laddr(4) = '1' then
          RamDMask(((DWordSel*4)+3) downto (DWordSel*4)) <= (others => '1');
        end if;
        RamAddr <= LAddr(30 downto 5);
      end if;
  	end if;
  end process RamOpFF;

  -- RAM data in
  RamIpFF: process(nReset, RamClk)
  begin
  	if nReset = '0' then
      RamRdPushD <= '0';
      LRdData <= (others => '0');
  	elsif RamClk'event and RamClk = '1' then
      if RamRdPush = '1' then
        RamRdPushD <= not RamRdPushD;
        if RamRdPushD = '0' and Laddr(4) = '0' then
          LRdData <= RamDOut(((DWordSel*32)+31) downto (DWordSel*32));
        end if;
        if RamRdPushD = '1' and Laddr(4) = '1' then
          LRdData <= RamDOut(((DWordSel*32)+31) downto (DWordSel*32));
        end if;
      end if;
      if iRamRd = '0' then
        RamRdPushD <= '0';
      end if;
    end if;
  end process RamIpFF;

end architecture Rtl;