---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2007
--
-- Filename: SysLBusInterface.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 System FPGA
--
-- Design Description:
-- Module to interface the local bus with the rest of the system
---------------------------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;

library UNISIM;
use UNISIM.vcomponents.all;

use work.SystemFpgaPkg.all;
use work.SystemMonitorPkg.all;

entity SysLBusInterface is
  port (-- Local bus
        nReset            : in  std_ulogic;
        LClk              : in  std_ulogic;
        LWnR              : in  std_ulogic;
        LDIn              : in  std_logic_vector(31 downto 0);
        LDOut             : out std_logic_vector(31 downto 0);
        nLDOe             : out std_ulogic;
        LAin              : in  std_logic_vector(31 downto 2);
        nAds              : in  std_ulogic;
        nBLast            : in  std_ulogic;
        nReady            : out std_ulogic;
        nBTerm            : out std_ulogic;
        LHold             : in  std_ulogic;
        LHoldA            : out std_ulogic;
        BReqi             : out std_ulogic;
        Test              : out std_ulogic; 
        -- DSP FPGA configuration
        DspConfigData     : out std_logic_vector(31 downto 0);
        DspConfigWr       : out std_ulogic;
        DspConfigStart    : out std_ulogic;
        DspConfigDone     : in  std_ulogic;
        DspConfigBusy     : in  std_ulogic;
        -- System Monitor
        SysMonitorEn      : out std_ulogic;
        SysMonitorSel     : in  std_logic_vector(4 downto 0);
        SysMonitorVal     : in  std_logic_vector(11 downto 0);
        SysMonitorValWr   : in  std_ulogic;
        SysMonitorMin     : out std_logic_vector(11 downto 0);
        SysMonitorMax     : out std_logic_vector(11 downto 0);
        SysMonitorTempVal : in  std_logic_vector(9 downto 0);
        SysMonitorTempMin : out std_logic_vector(9 downto 0);
        SysMonitorTempMax : out std_logic_vector(9 downto 0);
        UnderRangeFlags   : in  std_logic_vector(24 downto 0);
        OverRangeFlags    : in  std_logic_vector(24 downto 0);
        LedEnSel          : in  std_logic_vector(2 downto 0);
        UnderRangeEn      : out std_logic_vector(24 downto 0);
        OverRangeEn       : out std_logic_vector(24 downto 0));
end entity SysLBusInterface;


architecture Rtl of SysLBusInterface is

  signal nReadyPulse  : std_ulogic;
  signal nReadyOe     : std_ulogic;

  signal Addr     : std_logic_vector(31 downto 0);
  signal WrnRd    : std_ulogic;
  signal SysWr    : std_ulogic;
  signal SysRd    : std_ulogic;
  signal WrData   : std_logic_vector(31 downto 0);
  signal RdData   : std_logic_vector(31 downto 0);

  type tState is (Idle, Add, Wr, RdDel, Rd, BurstNext);
  signal State    : tState;

  -- Internal registers
  type tReg is array (0 to cLastReg) of std_logic_vector(31 downto 0);
  signal Reg: tReg;
  signal RegSel: integer := 0;

  -- SysMon register signals
  signal SysMonValIn      : std_logic_vector(15 downto 0);
  signal RegSysMonValOut  : std_logic_vector(15 downto 0);
  signal SysMonValRdAddr  : std_logic_vector(9 downto 0);

  signal SysMonAddr       : std_logic_vector(9 downto 0);
  signal SysMonMaxMin     : std_logic_vector(31 downto 0);
  signal RegSysMonMaxMin  : std_logic_vector(15 downto 0);
  signal SysMonMaxMinAddr : std_logic_vector(9 downto 0);
  signal RegSysMonMaxMinWr: std_ulogic;
  signal SysMonLedAddr    : std_logic_vector(8 downto 0);
  signal SysMonURng       : std_logic_vector(31 downto 0);
  signal RegSysMonURng    : std_logic_vector(31 downto 0);
  signal SysMonURngAddr   : std_logic_vector(8 downto 0);
  signal RegSysMonURngWr  : std_ulogic;
  signal SysMonORng       : std_logic_vector(31 downto 0);
  signal RegSysMonORng    : std_logic_vector(31 downto 0);
  signal SysMonORngAddr   : std_logic_vector(8 downto 0);
  signal RegSysMonORngWr  : std_ulogic;

  attribute iob : string;

  attribute iob of nReadyPulse: signal is "true";
  attribute iob of nLdOe      : signal is "true";

begin
  -- Bus arbitation
  BusArb : process (LClk) is
  begin
    if LClk'event and LClk = '1' then
      LHoldA <= LHold;
    end if;  
  end process BusArb;

  -- We don't need to request bus as System FPGA is not a Bus Master
  BReqi <= '0'; -- 'Z';

  nBTerm <= '1'; -- 'Z';

  nReady <= nReadyPulse when nReadyOe = '0' else 'Z';

  -- Bus access state machine
  LBusSM : process (LClk) is
  begin
    if LClk'event and LClk = '1' then
      nReadyPulse <= '1';
      nReadyOe <= '1';
      SysWr <= '0';
      SysRd <= '0';
      nLdOe <= '1';
      case State is
        when Idle =>
          if nAds = '0' then
            Addr <= LAin & "00";
            WrnRd <= LWnR;
            State <= Add;
          end if;
        when Add => 
          if Addr(31 downto cSysRegOffsetBits) = cRegSet(0).Address(31 downto cSysRegOffsetBits) then
            if WrnRd = '1' then
              WrData <= LdIn;
              SysWr <= '1';
              state <= Wr;
            else
              SysRd <= '1';
              State <= RdDel;
            end if;
          elsif nAds = '1' then
            State <= Idle;
          end if;
        when Wr => 
          nReadyPulse <= '0';
          nReadyOe <= '0';
          if nBLast = '0' then
            State <= Idle;
          else
            State <= BurstNext;
          end if;
        when RdDel =>
          State <= Rd;
        when Rd =>
          nReadyPulse <= '0';
          nReadyOe <= '0';
          LDOut <= RdData;
          nLdOe <= '0';
          if nBLast = '0' then
            State <= Idle;
          else
            State <= BurstNext;
          end if;
        when BurstNext => 
          Addr <= LAin & "00";
          WrnRd <= LWnR;
          State <= Add;
        when others =>
      end case;
      if nReadyPulse = '0' then
        nReadyOe <= '0'; -- active negatiation of nReady
      end if;
    end if;  
  end process LBusSM;

  Test <= '1' when State = Rd else '0';

  -- Decode Register addresses
  AddrDec : process (LClk) is
  begin
    if LClk'event and LClk = '1' then
      RegSel <= 0;

      for N in 1 to cLastReg loop
        if cRegSet(N).RegCount = 1 then
          if Addr(cSysRegOffsetBits downto 2) = cRegSet(N).Address(cSysRegOffsetBits downto 2) then
            RegSel <= N;
          end if;
        else
          if unsigned(Addr(cSysRegOffsetBits downto 2)) >= unsigned(cRegSet(N).Address(cSysRegOffsetBits downto 2)) and
              unsigned(Addr(cSysRegOffsetBits downto 2)) < unsigned(cRegSet(N).Address(cSysRegOffsetBits downto 2)) + (cRegSet(N).RegCount * 4) then
            RegSel <= N;
          end if;
        end if;
      end loop;
    end if;
  end process AddrDec;

  -- Generate signal containing all read register data
  ReadDataMux: process (RegSel, Reg, DspConfigDone, DspConfigBusy, UnderRangeFlags, OverRangeFlags, RegSysMonURng,
                          RegSysMonORng, RegSysMonValOut, SysMonitorTempVal, RegSysMonMaxMin)
    variable vRdData: std_logic_vector(31 downto 0);
  begin
    -- Default to simple register read
    vRdData := Reg(RegSel);

    -- special cases
    case RegSel is
      when cCheck =>
        vRdData := not Reg(cCheck);
      when cSysControl =>
        vRdData(30) := DspConfigDone;
        vRdData(31) := DsPConfigBusy;
      when cUnderRangeFlags =>
        vRdData(24 downto 0) := UnderRangeFlags;
      when cOverRangeFlags =>
        vRdData(24 downto 0) := OverRangeFlags;
      when cUnderRangeDLedEn | cUnderRangeOKLedEn =>
        vRdData := RegSysMonURng;
      when cOverRangeDLedEn  | cOverRangeOKLedEn =>
        vRdData := RegSysMonORng;
      when cSysMonitorADCVal =>
        vRdData(15 downto 0) := RegSysMonValOut;
      when cSysMonitorTempVal =>
        vRdData(9 downto 0) := SysMonitorTempVal;
      when cSysMonitorADCMin | cSysMonitorADCMax =>
        vRdData(15 downto 0) := RegSysMonMaxMin(15 downto 0);
      when cSysFpgaVer => 
        vRdData(11 downto 0) := cFpgaRevision;
      when others =>
        null;
    end case;

    -- Apply read mask
    RdData <= vRdData and cRegSet(RegSel).ReadMask;
  end process ReadDataMux;



  -- Process to generate registers and RAM write strobes
  GenRegAccess: process(nReset, LClk)
  begin
    if nReset = '0' then
      for i in 0 to cLastReg loop
        Reg(i) <= cRegSet(i).ResetVal;
      end loop;
      DspConfigStart <= '0';
      DspConfigWr <= '0';
      RegSysMonURngWr <= '0';
      RegSysMonORngWr <= '0';
      RegSysMonMaxMinWr <= '0';
    elsif LClk'event and LClk = '1' then
      DspConfigStart <= '0';
      DspConfigWr <= '0';
      RegSysMonURngWr <= '0';
      RegSysMonORngWr <= '0';
      RegSysMonMaxMinWr <= '0';

      if SysWr = '1' then
        -- write to writable bits
        Reg(RegSel) <= WrData and cRegSet(RegSel).WriteMask;

        -- special cases
        case RegSel is
          when cSysControl =>
            DspConfigStart <= WrData(0);
          when cFPGAConfigData => 
            DspConfigWr <= '1';
          when cUnderRangeDLedEn | cUnderRangeOKLedEn =>
            RegSysMonURngWr <= '1';
          when cOverRangeDLedEn  | cOverRangeOKLedEn =>
            RegSysMonORngWr <= '1';
          when cSysMonitorADCMin | cSysMonitorADCMax =>
            RegSysMonMaxMinWr <= '1';
          when others =>
            null;
        end case;
      end if;

    end if;
  end process GenRegAccess;

  DspConfigData <= Reg(cFPGAConfigData);
  SysMonitorEn <= Reg(cSystemMonitorCtrl)(0);
  SysMonitorTempMin <= Reg(cSysMonitorTempMin)(9 downto 0);
  SysMonitorTempMax <= Reg(cSysMonitorTempMax)(9 downto 0);

  -- RAM address decodes
  SysMonValRdAddr <= "00000" & (unsigned(Addr(6 downto 2)) - unsigned(cSysMonitorADCValAddr(6 downto 2)));
  SysMonMaxMinAddr <= "0000" & (unsigned(Addr(6 downto 2)) - unsigned(cSysMonitorADCMinAddr(6 downto 2))) & Addr(7);
  SysMonURngAddr <= "000000101" when RegSel = cUnderRangeOKLedEn else
                    "0000" & (unsigned(Addr(6 downto 2)) - unsigned(cUnderRangeDLedEnAddr(6 downto 2)));
  SysMonORngAddr <= "000000101" when RegSel = cOverRangeOKLedEn else
                    "0000" & (unsigned(Addr(6 downto 2)) - unsigned(cOverRangeDLedEnAddr(6 downto 2)));
                    
  SysMonValIn  <= "0000" & SysMonitorVal;
  SysMonAddr <= "00000" & SysMonitorSel;

  -- Block RAM for System Monitor Values
  SysMonValRam: RAMB16_S18_S18
    port map (DOA   => RegSysMonValOut,
  	          DOB   => open,
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => SysMonValRdAddr,
  	          ADDRB => SysMonAddr,
  	          CLKA  => LClk,
  	          CLKB  => LClk,
  	          DIA   => (Others => '0'),
  	          DIB   => SysMonValIn,
  	          DIPA  => (others => '0'),
  	          DIPB  => (others => '0'),
  	          ENA   => '1',
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => '0',
  	          WEB   => SysMonitorValWr);

  
  -- Block RAM for System Monitor Min/Max
  SysMonMinMaxRam: RAMB16_S18_S36
    generic map(INIT_00 => to_bitvector(cSysMonMaxMin(7).Max & cSysMonMaxMin(7).Min & cSysMonMaxMin(6).Max & cSysMonMaxMin(6).Min &
                                        cSysMonMaxMin(5).Max & cSysMonMaxMin(5).Min & cSysMonMaxMin(4).Max & cSysMonMaxMin(4).Min &
                                        cSysMonMaxMin(3).Max & cSysMonMaxMin(3).Min & cSysMonMaxMin(2).Max & cSysMonMaxMin(2).Min &
                                        cSysMonMaxMin(1).Max & cSysMonMaxMin(1).Min & cSysMonMaxMin(0).Max & cSysMonMaxMin(0).Min),
                INIT_01 => to_bitvector(cSysMonMaxMin(15).Max & cSysMonMaxMin(15).Min & cSysMonMaxMin(14).Max & cSysMonMaxMin(14).Min & 
                                        cSysMonMaxMin(13).Max & cSysMonMaxMin(13).Min & cSysMonMaxMin(12).Max & cSysMonMaxMin(12).Min &
                                        cSysMonMaxMin(11).Max & cSysMonMaxMin(11).Min & cSysMonMaxMin(10).Max & cSysMonMaxMin(10).Min &
                                        cSysMonMaxMin(9).Max & cSysMonMaxMin(9).Min & cSysMonMaxMin(8).Max & cSysMonMaxMin(8).Min),
                INIT_02 => to_bitvector(cSysMonMaxMin(23).Max & cSysMonMaxMin(23).Min & cSysMonMaxMin(22).Max & cSysMonMaxMin(22).Min &
                                        cSysMonMaxMin(21).Max & cSysMonMaxMin(21).Min & cSysMonMaxMin(20).Max & cSysMonMaxMin(20).Min &
                                        cSysMonMaxMin(19).Max & cSysMonMaxMin(19).Min & cSysMonMaxMin(18).Max & cSysMonMaxMin(18).Min &
                                        cSysMonMaxMin(17).Max & cSysMonMaxMin(17).Min & cSysMonMaxMin(16).Max & cSysMonMaxMin(16).Min))
    port map (DOA   => RegSysMonMaxMin,
  	          DOB   => SysMonMaxMin,
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => SysMonMaxMinAddr,
  	          ADDRB => SysMonAddr(8 downto 0),
  	          CLKA  => LClk,
  	          CLKB  => LClk,
  	          DIA   => WrData(15 downto 0),
  	          DIB   => (Others => '0'),
  	          DIPA  => (others => '0'),
  	          DIPB  => (others => '0'),
  	          ENA   => '1',
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => RegSysMonMaxMinWr,
  	          WEB   => '0');

  SysMonitorMin <= SysMonMaxMin(11 downto 0);
  SysMonitorMax <= SysMonMaxMin(27 downto 16);

  SysMonLedAddr <= "000000" & LedEnSel;

  -- Block RAM for Under-Range Enables
  SysMonURngRam: RAMB16_S36_S36
    port map (DOA   => RegSysMonURng,
  	          DOB   => SysMonURng,
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => SysMonURngAddr,
  	          ADDRB => SysMonLedAddr,
  	          CLKA  => LClk,
  	          CLKB  => LClk,
  	          DIA   => WrData,
  	          DIB   => (Others => '0'),
  	          DIPA  => (others => '0'),
  	          DIPB  => (others => '0'),
  	          ENA   => '1',
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => RegSysMonURngWr,
  	          WEB   => '0');

  UnderRangeEn <= SysMonURng(24 downto 0);

  -- Block RAM for Over-Range Enables
  SysMonORngRam: RAMB16_S36_S36
    port map (DOA   => RegSysMonORng,
  	          DOB   => SysMonORng,
  	          DOPA  => open,
  	          DOPB  => open,
  	          ADDRA => SysMonORngAddr,
  	          ADDRB => SysMonLedAddr,
  	          CLKA  => LClk,
  	          CLKB  => LClk,
  	          DIA   => WrData,
  	          DIB   => (Others => '0'),
  	          DIPA  => (others => '0'),
  	          DIPB  => (others => '0'),
  	          ENA   => '1',
  	          ENB   => '1',
  	          SSRA  => '0',
  	          SSRB  => '0',
  	          WEA   => RegSysMonORngWr,
  	          WEB   => '0');

  OverRangeEn <= SysMonORng(24 downto 0);

end Rtl;
