---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2008
--
-- Filename: SPDRead.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- This module reads the SPD EEPROM on the DDR2 SODIMM
-- and returns the module size (in units of 256MBytes)
---------------------------------------------------------------------------

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

entity SPDRead is
  port (Clk   : in    std_ulogic;
        Reset : in    std_ulogic;
        --
        SCL   : out   std_ulogic;
        SDA   : inout std_ulogic;
        SA    : out   std_logic_vector(1 downto 0);
        --
        Delay : in    std_ulogic;
        Size  : out   std_logic_vector(3 downto 0);
        Valid : out   std_ulogic);
end SPDRead;

architecture Rtl of SPDRead is

  component I2C_MASTER_BYTE_CTRL
	  generic (
	    ClockDiv: integer);
	  port (
		  Clk    : in std_logic;
		  Reset  : in std_logic;	-- async reset (FPGA compatible)

		  -- input signals
		  En      : in std_logic; -- core enable signal
		  Start   : in std_logic;
		  Stop    : in std_logic;
		  Read    : in std_logic;
		  Write   : in std_logic;
		  AckIn   : in std_logic;
		  DIn     : in std_logic_vector(7 downto 0);

		  -- output signals
		  CmdAck  : out std_logic;
		  AckOut  : out std_logic;
		  i2cBusy : out std_logic;
		  i2cAl   : out std_logic;
		  DOut    : out std_logic_vector(7 downto 0);

		  -- i2c lines
		  SCLi    : in  std_logic; -- i2c clock line input
		  SCLo    : out std_logic; -- i2c clock line output
		  nSCLoe  : out std_logic; -- i2c clock line output enable, active low
		  SDAi    : in  std_logic; -- i2c data line input
		  SDAo    : out std_logic; -- i2c data line output
		  nSDAoe  : out std_logic); -- i2c data line output enable, active low
  end component;

  signal DelayD1: std_ulogic;
  signal DelayD2: std_ulogic;
  signal ReadStart: std_ulogic;
  signal ReadDone : std_ulogic;
  signal ReadError: std_ulogic;
  signal ReadData: std_logic_vector(7 downto 0);

  type tSPDState is (idle, ReadBanks, OutputSize, Done);
  signal SPDState: tSPDState;
  type tReadState is (RdIdle, RdSlaveAddress, RdStart, RdAddress, RdAddressWait, RdRead, RdStop, RdError);
  signal ReadState: tReadState;

  signal En     : std_logic;
  signal Start  : std_logic;
  signal Stop   : std_logic;
  signal Read   : std_logic;
  signal Write  : std_logic;
  signal AckIn  : std_logic;
  signal DIn    : std_logic_vector(7 downto 0);
  --
  signal CmdAck : std_logic;
  signal AckOut : std_logic;
  signal i2cBusy: std_logic;
  signal i2cAl  : std_logic;
  signal DOut   : std_logic_vector(7 downto 0);
  --
  signal SCLi   : std_ulogic;
  signal SCLo   : std_ulogic;
  signal nSCLoe : std_ulogic;
  signal SDAi   : std_ulogic;
  signal SDAo   : std_ulogic;
  signal nSDAoe : std_ulogic;

  constant ciicPage: std_logic_vector(2 downto 0) := "000";
  constant cSlaveAddress: std_logic_vector(7 downto 4) := "1010";
  constant cReadMode: std_logic := '1';
  constant cWriteMode: std_logic := '0';
  constant cSPDBanks: integer := 17;
begin

  -- Read SPD and calculate size
  SpdStateM: process(Reset, Clk)
  begin
    if Reset = '1' then
      DelayD1 <= '1';
      DelayD2 <= '1';
      SPDState <= idle;
      ReadStart <= '0';
      Size  <= (others => '0');
      Valid <= '0';
    elsif Clk'event and Clk = '1' then
      DelayD1 <= Delay;
      DelayD2 <= DelayD1;
      ReadStart <= '0';
      Valid <= '0';
      case SPDState is
        when idle =>
          if DelayD2 = '0' then
            SPDState <= ReadBanks;
          end if;
        when ReadBanks => 
          ReadStart <= '1';
          if ReadDone = '1' then
            SPDState <= OutputSize;
          end if;
          if ReadError = '1' then
            SPDState <= Done;
          end if;
        when OutputSize =>
          SPDState <= Done;
          Size <= ReadData(3 downto 0);
          Valid <= '1';
        when Done =>
          If DelayD2 = '1' then
            SPDState <= Idle;
          end if;
        when others =>
          null;
      end case;    
    end if;
  end process SpdStateM;

  -- Read iic byte SM
  ReadStateM: process(Reset, Clk)
  begin
    if Reset = '1' then
      ReadState <= Rdidle;
      ReadDone <= '0';
      ReadError <= '0';
      ReadData <= (others => '0');
      En    <= '0';
      Start <= '0';
      Stop  <= '0';
      Read  <= '0';
      Write <= '0';
      AckIn <= '0';
      DIn   <= (others => '0');
    elsif Clk'event and Clk = '1' then
      ReadDone <= '0';
      ReadError <= '0';
      En <= '1';
      Start <= '0';
      Stop  <= '0';
      Read  <= '0';
      Write <= '0';
      AckIn <= '0';
      case ReadState is
        when RdIdle =>
          if ReadStart = '1' and i2cBusy = '0' then
            ReadState <= RdSlaveAddress;
          end if;
        when RdSlaveAddress =>
          Din <= cSlaveAddress & ciicPage & cReadMode;
          ReadState <= RdStart;
        when RdStart =>
          if CmdAck = '1' then
            ReadState <= RdAddress;
          elsif i2cAl = '1' then
            ReadState <= RdError;
          else
            Start <= '1';
            Write <= '1';
          end if;
        when RdAddress =>
          DIn <= conv_std_logic_vector(cSPDBanks, 8);
          ReadState <= RdAddressWait;
        when RdAddressWait =>
          if CmdAck = '1' then
            ReadState <= RdRead;
          elsif i2cAl = '1' then
            ReadState <= RdError;
          else
            Write <= '1';
          end if;
        when RdRead =>
          if CmdAck = '1' then
            ReadState <= RdStop;
            ReadData <= DOut;
          elsif i2cAl = '1' then
            ReadState <= RdError;
          else
            Read <= '1';
          end if;
        when RdStop =>
          Stop <= '1';
          ReadDone <= '1';
          if ReadStart = '0' then
            ReadState <= RdIdle;
          end if;
        when RdError =>
          ReadError <= '1';
          if ReadStart = '0' then
            ReadState <= RdIdle;
          end if;
        when others =>
          null;
      end case;    
    end if;
  end process ReadStateM;

  iic: I2C_MASTER_BYTE_CTRL
	  generic map(ClockDiv => 66000000 / 100000)
	  port map (Clk     => Clk,
        		  Reset   => Reset,

		          -- input signals
		          En      => En,
		          Start   => Start,
		          Stop    => Stop,
		          Read    => Read,
		          Write   => Write,
		          AckIn   => AckIn,
		          DIn     => DIn,

		          -- output signals
		          CmdAck  => CmdAck,
		          AckOut  => AckOut,
		          i2cBusy => i2cBusy,
		          i2cAl   => i2cAl,
		          DOut    => DOut,

		          -- i2c lines
		          SCLi    => SCLi,
		          SCLo    => SCLo,
		          nSCLoe  => nSCLoe,
		          SDAi    => SDAi,
		          SDAo    => SDAo,
		          nSDAoe  => nSDAoe);

  SCLi <= SCLo when nSCLoe = '0' else '1';
  SCL <= SCLo when nSCLoe = '0' else '1';

  SDA <= SDAo when nSDAoe = '0' else 'Z';
  SDAi <= '0' when SDA = '0' else '1';

  SA <= ciicPage(1 downto 0);

end Rtl;
