---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2007
--
-- Filename: SystemMonitor.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 System FPGA
--
-- Design Description:
-- System Monitor to monitor voltage and temperature ADC inputs
---------------------------------------------------------------------------

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

entity SystemMonitor is
  port (-- Local bus
        nReset             : in  std_ulogic;
        LClk               : in  std_ulogic;
        -- Register interface signals
        SysMonitorEn       : in  std_ulogic;
        SysMonitorSel      : out std_logic_vector(4 downto 0) := (others => '0');
        SysMonitorVal      : out std_logic_vector(11 downto 0);
        SysMonitorValWr    : out std_ulogic;
        SysMonitorMin      : in  std_logic_vector(11 downto 0);
        SysMonitorMax      : in  std_logic_vector(11 downto 0);
        SysMonitorTempVal  : out std_logic_vector(9 downto 0) := (others => '0');
        SysMonitorTempMin  : in  std_logic_vector(9 downto 0);
        SysMonitorTempMax  : in  std_logic_vector(9 downto 0);
        UnderRangeFlags    : out std_logic_vector(24 downto 0);
        OverRangeFlags     : out std_logic_vector(24 downto 0);
        LedEnSel           : out std_logic_vector(2 downto 0);
        UnderRangeEn       : in  std_logic_vector(24 downto 0);
        OverRangeEn        : in  std_logic_vector(24 downto 0);
        -- System Monitor ADC signals
        nSysMonCs  : out   std_logic_vector(2 downto 0) := (others => '1');
        SysMonSClk : out   std_ulogic := '1';
        SysMonDIn  : out   std_ulogic := '0';
        SysMonDOut : in    std_ulogic;
        TempSnsCe  : out   std_ulogic := '0';
        TempSnsSClk: out   std_ulogic := '0';
        TempSnsSDo : in    std_ulogic;
        SysMonD    : out   std_logic_vector(4 downto 0);
        SysMonOK   : out   std_ulogic);

end entity SystemMonitor;


architecture Rtl of SystemMonitor is
  signal Channel: unsigned(4 downto 0) := (others => '0');
  signal FlagChannel: unsigned(4 downto 0) := (others => '0');
  type tState is (Disabled, Start, Convert, WriteVal, SetFlagsVal, WriteTemp, SetFlagsTemp, NextChan);
  signal State: tState := Disabled;

  signal ADCStart   : std_ulogic;
  signal ADCDone    : std_ulogic;
  signal ADCOut     : std_logic_vector(11 downto 0);
  signal ADCCount   : unsigned(8 downto 0) := (others => '0');
  signal ADCAdd     : std_logic_vector(2 downto 0) := (others => '0');
  signal AddSetup   : std_ulogic := '0';

  signal TempStart  : std_ulogic;
  signal TempDone   : std_ulogic;
  signal TempOut    : std_logic_vector(9 downto 0);
  signal TempCount  : unsigned(6 downto 0) := (others => '0');

  signal iUnderRangeFlags : std_logic_vector(24 downto 0);
  signal iOverRangeFlags  : std_logic_vector(24 downto 0);
  signal LedUpdate        : std_ulogic;
  signal LedEnCount       : unsigned(3 downto 0);

begin

  -- System Monitor State machine
  SysMtrState: process(LClk)
  begin
    if LClk'event and LClk = '1' then
      SysMonitorValWr <= '0';
      ADCStart <= '0';
      TempStart <= '0';
      LedUpdate <= '0';
      if SysMonitorEn = '0' then
        State <= Disabled;
      else
        case State is
          when Disabled =>
            Channel <= (others => '0');
            AddSetup <= '0';
            State <= Start;
          when Start =>
            if Channel = 24 then
              TempStart <= '1';
            else
              ADCStart <= '1';
            end if;
            State <= Convert;
          when Convert =>
            if ADCDone = '1' then
              State <= WriteVal;
            elsif TempDone = '1' then
              State <= WriteTemp;
            end if;
          when WriteVal =>
            SysMonitorVal <= ADCOut;
            if Channel(2 downto 0) /= 0 then
              SysMonitorValWr <= '1';
            end if;
            State <= SetFlagsVal;
          when SetFlagsVal =>
            if Channel(2 downto 0) /= 0 then
              if unsigned(ADCOut) > unsigned(SysMonitorMax) then
                iOverRangeFlags(conv_integer(FlagChannel)) <= '1';
              else
                iOverRangeFlags(conv_integer(FlagChannel)) <= '0';
              end if;
              if unsigned(ADCOut) < unsigned(SysMonitorMin) then
                iUnderRangeFlags(conv_integer(FlagChannel)) <= '1';
              else
                iUnderRangeFlags(conv_integer(FlagChannel)) <= '0';
              end if;
              LedUpdate <= '1';
            end if;
            State <= NextChan;
          when WriteTemp =>
            SysMonitorTempVal <= TempOut;
            State <= SetFlagsTemp;
          when SetFlagsTemp =>
            if unsigned(TempOut) > unsigned(SysMonitorTempMax) then
              iOverRangeFlags(24) <= '1';
            else
              iOverRangeFlags(24) <= '0';
            end if;
            if unsigned(TempOut) < unsigned(SysMonitorTempMin) then
              iUnderRangeFlags(24) <= '1';
            else
              iUnderRangeFlags(24) <= '0';
            end if;
            LedUpdate <= '1';
            State <= NextChan;
          when NextChan => 
            SysMonitorSel <= std_logic_vector(Channel(4 downto 0));
            FlagChannel <= Channel;
            if Channel = 24 then
              Channel <= (others => '0');
            else
              if Channel(2 downto 0) = 7 and AddSetup = '0' then
                AddSetup <= '1';
              else
                AddSetup <= '0';
                Channel <= Channel + 1;
              end if;
            end if;
            State <= Start;
          when others =>
            State <= Disabled;
        end case;
      end if;
    end if;
  end process SysMtrState;

  UnderRangeFlags <= iUnderRangeFlags;
  OverRangeFlags <= iOverRangeFlags;

  -- LED enable decode
  LedState: process(LClk)
  begin
    if LClk'event and LClk = '1' then
      if ADCStart = '1' then
        LedEnCount <= "0000";
      elsif LedEnCount /= 12 then
        if (LedEnCount(0) = '1') then
          if LedEnCount(3 downto 1) = 5 then
            SysMonOK <= '0';
          else
            SysMonD(conv_integer(LedEnCount(3 downto 1))) <= '0';
          end if;

          for i in 0 to 24 loop
            if (iUnderRangeFlags(i) = '1' and UnderRangeEn(i) = '1') or 
                (iOverRangeFlags(i) = '1' and OverRangeEn(i) = '1') then
              if LedEnCount(3 downto 1) = 5 then
                SysMonOK <= '1';
              else
                SysMonD(conv_integer(LedEnCount(3 downto 1))) <= '1';
              end if;
            end if;
          end loop;  
        end if;
        LedEnCount <= LedEnCount + 1;
      end if;
    end if;
  end process LedState;

  LedEnSel <= std_logic_vector(LedEnCount(3 downto 1));

  -- System monitor ADC interface
  ADCState: process(LClk)
  begin
    if LClk'event and LClk = '1' then
      ADCDone <= '0';
      if ADCStart = '1' then
        nSysMonCs(conv_integer(Channel(4 downto 3))) <= '0';
        ADCAdd <= std_logic_vector(Channel(2 downto 0));
        ADCCount <= ADCCount + 1;
        SysMonSClk <= '0';
      elsif ADCCount /= 0 then
        ADCCount <= ADCCount + 1;
        if ADCCount(4 downto 0) = 0 then
          SysMonSClk <= '0';
          if ADCCount(8 downto 5) >= 2 and ADCCount(8 downto 5) <= 4 then
            SysMonDIn <= ADCAdd(2);
            ADCAdd <= ADCAdd(1 downto 0) & '0';
          end if;
        end if;
        if ADCCount(4 downto 0) = 16 then
          SysMonSClk <= '1';
          ADCOut <= ADCOut(10 downto 0) & SysMonDOut;
        end if;
        if ADCCount = 511 then
          nSysMonCs(conv_integer(Channel(4 downto 3))) <= '1';
          ADCDone <= '1';
        end if;
      end if;
    end if;
  end process ADCState;

  -- Temp sensor interface
  TempState: process(LClk)
  begin
    if LClk'event and LClk = '1' then
      TempDone <= '0';
      if TempStart = '1' then
        TempSnsCe <= '1';
        TempCount <= TempCount + 1;
        TempSnsSClk <= '1';
      elsif TempCount /= 0 then
        TempCount <= TempCount + 1;
        if TempCount(2 downto 0) = 0 then
          TempSnsSClk <= '1';
        end if;
        if TempCount(2 downto 0) = 4 then
          TempSnsSClk <= '0';
          if TempCount(6 downto 3) <= 10 then
            TempOut <= TempOut(8 downto 0) & TempSnsSDo;
          end if;
        end if;
        if TempCount = 127 then
          TempSnsCe <= '0';
          TempDone <= '1';
        end if;
      end if;
    end if;
  end process TempState;

end Rtl;
