---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2008
--
-- Filename: SPD_EEPROM.vhd
-- Language: VHDL '93
-- Project:  NDTS2100
--
-- Design Description:
-- SPD EEPROM on 1G/2GByte SODIMM for read test
---------------------------------------------------------------------------

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

entity SPD_EEPROM is
  generic(gModuleSize : integer := 2); -- size in GBytes
	port (SCL      : in    std_logic;
		    SDA      : inout std_logic := 'Z';
        SA       : in    std_logic_vector(2 downto 0);

    	  TestFail : out boolean := false);
end entity SPD_EEPROM;

architecture Beh of SPD_EEPROM is
	constant SLAVE_ADDR : std_logic_vector(7 downto 4) := "1010";

	signal Start: boolean := false;
	signal Stop: boolean := false;
	signal Done: boolean := false;
	signal ShiftReg : std_logic_vector(7 downto 0);

  type tState is (Idle, SlaveAddr, Addr, Data, WaitStop);
  signal State: tState := Idle;
  signal Read: boolean := false;
  signal Write: boolean := false;
  signal RdData: std_logic_vector(7 downto 0);

  -- Standard mode timings
  constant tlow:    time := 4.7 us;
  constant thigh:   time := 4 us;
  constant tbuf:    time := 4.7 us;
  constant tsu_dat: time := 250 ns;
  constant tsu_sto: time := 250 ns;

begin
  -- Capture bytes
  process
    variable vBitCount: integer := 0;
  begin
    wait on SCL, SDA;

    if SDA'event and SDA = '0' and SCL /= '0' then -- start condition
      Start <= true;

      if not SCL'stable(tbuf) and now > 1 ns then
        BusFreeErr: assert false report "Bus free time violated before start condition" severity warning;
        TestFail <= true;
      end if;
    end if;

    if SDA'event and SDA /= '0' and SCL /= '0' then -- stop condition
      Stop <= true, false after 1 ns;
      vBitCount := 0;

      if not SCL'stable(tsu_sto) and now > 1 ns then
        SuStoErr: assert false report "SCL Set-up time violated before stop condition" severity warning;
        TestFail <= true;
      end if;
    end if;

    if SCL'event and SCL = '0' then -- falling edge of clock
      -- Timing checks
      if not SCL'delayed'stable(thigh) then
        SclHighErr: assert false report "SCL high time violated" severity warning;
        TestFail <= true;
      end if;

      if not SDA'stable(tsu_dat) then
        SuDatErr: assert false report "SDA Set-up time violated" severity warning;
        TestFail <= true;
      end if;


      if Start then
        vBitCount := 1;
        Start <= false;
      elsif vBitCount > 0 and vBitCount < 9 then
        if State = Data and Read then
          if ShiftReg(7) = '0' then
            SDA <= '0';
          else
            SDA <= 'Z';
          end if;
          ShiftReg <= ShiftReg(6 downto 0) & '0';
        elsif SDA = '0' then
          ShiftReg <= ShiftReg(6 downto 0) & '0';
        else  
          ShiftReg <= ShiftReg(6 downto 0) & '1';
        end if;
        if vBitCount = 8 then
          Done <= true;
          wait for 1 ns;
          Done <= false;
          if Read or Write then
            SDA <= '0';
          end if;
          if State = Data and Read then
            ShiftReg <= RdData;
          end if;
        end if;
        vBitCount := vBitCount + 1;
      elsif vBitCount = 9 then
        vBitCount := 1;
        if State = Data and Read then
          if ShiftReg(7) = '0' then
            SDA <= '0';
          else
            SDA <= 'Z';
          end if;
          ShiftReg <= ShiftReg(6 downto 0) & '0';
        else
          SDA <= 'Z';
        end if;
      end if;
    end if;

    if SCL'event and SCL /= '0' then -- rising edge of clock
      if not SCL'delayed'stable(tlow) then
        SclLowErr: assert false report "SCL low time violated" severity warning;
        TestFail <= true;
      end if;
    end if;

  end process;


  -- Read/write state machine
  process
    variable vAddr: std_logic_vector(7 downto 0);
  begin
    wait on Start, Stop, Done;
    case State is
      when Idle =>
        if Start'event and Start then
          State <= SlaveAddr;
        end if;
      when SlaveAddr =>
        if Done'event and Done then
          State <= Addr;
          if ShiftReg(7 downto 1) = SLAVE_ADDR & SA then
            if ShiftReg(0) = '1' then
              Read <= true;
            else
              Write <= true;
            end if;
          end if;
        end if;
      when Addr =>
        if Done'event and Done then
          State <= Data;
          vAddr := ShiftReg;
          if Read then
            case vAddr is
              when x"00" => -- Number of SPD bytes
                RdData <= x"80";
              when x"01" => -- Total Number of bytes in SPD device
                RdData <= x"08"; -- X256
              when x"02" => -- Fundamental memory type
                RdData <= x"08"; -- DDR2
              when x"03" => -- Row addresses
                RdData <= x"0E";
              when x"04" => -- Column addresses
                RdData <= x"0A";
              when x"05" => -- DIMM height and module ranks
                RdData <= x"61";
              when x"06" => -- Module data width
                RdData <= x"40";
              when x"0D" => -- SDRAM Device width
                RdData <= x"08";
              when x"11" => -- Number of banks on SDRAM device
                if gModuleSize = 1 then
                  RdData <= x"04";
                else
                  RdData <= x"08";
                end if;
              when x"1F" => -- Module rank density
                if gModuleSize = 1 then
                  RdData <= x"80";
                else
                  RdData <= x"01";
                end if;
              when x"3E" => -- SPD revision
                RdData <= x"12";
              when others =>
                RdData <= (others => '0');
            end case;    
          end if;
        end if;
      when Data =>
        if Done'event and Done then
          State <= WaitStop;

          Read <= false;
        end if;
      when WaitStop =>
        if Stop'event and Stop then
          State <= Idle;
          Read <= false;
          Write <= false;
        end if;
      when others =>
        null;
    end case;    
  end process;

end architecture Beh;


