---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2008
--
-- Filename: Ddr2State.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- DDR2 SDRAM State Machine
---------------------------------------------------------------------------

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

entity Ddr2State is
  generic(gDevices  : integer := 1;
          gClkFreq  : integer := 100); -- in MHz
  port(nReset       : in  std_ulogic;
       SyncRst      : in  std_ulogic;
       Clk          : in  std_ulogic;
       InitWait     : in  std_ulogic;
       RfshReq      : in  std_ulogic;
       WriteReq     : in  std_ulogic;
       ReadReq      : in  std_ulogic;
       DeviceSelect : in  std_logic_vector(SizeToBits(gDevices)-1 downto 0);
       WrPop        : out std_ulogic;
       DInEnable    : out std_ulogic;
       DOutTri      : out std_ulogic;
       nCeEn        : out std_logic_vector(gDevices-1 downto 0);
       CeDisable    : out std_ulogic;
       nRas         : out std_ulogic;
       nCas         : out std_ulogic;
       nWe          : out std_ulogic;
       RstDqsDivP   : out std_ulogic;
       RstDqsDivN   : out std_ulogic;
       FifoWe0      : out std_ulogic;
       FifoWe1      : out std_ulogic;
       Row          : out std_ulogic;
       WriteAck     : out std_ulogic;
       ReadAck      : in  std_ulogic;
       AddrLatEn    : out std_ulogic;
       ModeSetFlag  : out std_ulogic;
       ModeReg      : out std_logic_vector(15 downto 0);
       RfshDone     : out std_ulogic);
end Ddr2State;


architecture Rtl of Ddr2State is

  signal InitCount: integer range 0 to gClkFreq/2;
  signal AutoRefCount: integer range 0 to gClkFreq/8;
  signal  SeqDone  : std_ulogic;
  type tMainState is (Init, PreCharge1, PreChargeWait1, EModeSet2, EModeSet3, EModeSet, ModeSetDllRst,
                      Precharge2, PrechargeWait2, AutoRef1, AutoRefWait1, AutoRef2, AutoRefWait2,
                      ModeSet, EModeSetOCDDef, EModeSetOcdExit,
                      Idle, Write, Write_Delay, Read, Read_Delay, AutoRef_Delay, AutoRef);
  signal  CtrlState: tMainState;
  type tOpState is (RWOp0, RWOp1, RWOp2, RWOp3, DelayOp);
  signal Output    : tOpState;
  signal CeNext    : std_ulogic;
  signal ReadNext  : std_ulogic;
  signal OpEnable  : std_ulogic;
  signal RstMain   : std_ulogic;
  signal RstOp     : std_ulogic;
  signal iModeSetFlag: std_ulogic;
  signal DOutTriNext : std_ulogic;
  signal DOutTriD1   : std_ulogic;
  signal DOutTriD2   : std_ulogic;
  signal inCe        : std_ulogic;
  signal inRas       : std_ulogic;
  signal inCas       : std_ulogic;
  signal RstDqsDivNext: std_ulogic;
  signal iRstDqsDivP  : std_ulogic;
  signal iFifoWe0     : std_ulogic;
  signal iFifoWe1     : std_ulogic;
  Signal ReadAckD     : std_ulogic;
BEGIN

  MainState : process (nReset, Clk)  -- Main state machine
  begin
    if nReset = '0' then
      CtrlState <= Init;
      InitCount <= 0;
      AutoRefCount <= 0;
      ReadAckD <= '0';
    elsif Clk'event and Clk = '1' then
      ReadAckD <= ReadAck;
      if RstMain = '1' then
        CtrlState <= Init;
        InitCount <= 0;
      elsif CtrlState = Init then
        if InitWait = '0' then
          if InitCount = gClkFreq/2 then
            CtrlState <= PreCharge1;
          else
            InitCount <= InitCount + 1;
          end if;
        else
          InitCount <= 0;
        end if;
      elsif SeqDone = '1' and OpEnable = '0' then
        case CtrlState is
          when PreCharge1 =>
            CtrlState <= PreChargeWait1;
          when PreChargeWait1 =>
            CtrlState <= EModeSet2;
          when EModeSet2 =>
            CtrlState <= EModeSet3;
          when EModeSet3 =>
            CtrlState <= EModeSet;
          when EModeSet =>
            CtrlState <= ModeSetDllRst;
          when ModeSetDllRst =>
            CtrlState <= PreCharge2;
          when PreCharge2 =>
            CtrlState <= PreChargeWait2;
          when PreChargeWait2 =>
            CtrlState <= AutoRef1;
          when AutoRef1 =>
            CtrlState <= AutoRefWait1;
            AutoRefCount <= 0;
          When AutoRefWait1 =>
            if AutoRefCount = gClkFreq/8 then
              CtrlState <= AutoRef2;
            else
              AutoRefCount <= AutoRefCount + 1;
            end if;
          when AutoRef2 =>
            CtrlState <= AutoRefWait2;
            AutoRefCount <= 0;
          When AutoRefWait2 =>
            if AutoRefCount = gClkFreq/8 then
              CtrlState <= ModeSet;
            else
              AutoRefCount <= AutoRefCount + 1;
            end if;
          when ModeSet =>
            CtrlState <= EModeSetOCDDef;
          when EModeSetOCDDef =>
            CtrlState <= EModeSetOcdExit;
          When EModeSetOcdExit =>
            CtrlState <= Idle;
          when Idle =>
            if (WriteReq = '1') then
              CtrlState <= Write;
            elsif (ReadReq = '1') then
              CtrlState <= Read;
            elsif (RfshReq = '1') then
              CtrlState <= AutoRef;
            end if;
          when Write =>
              CtrlState <= Write_Delay;
          when Write_Delay =>
              CtrlState <= Idle;
          when Read =>
              CtrlState <= Read_Delay;
          when Read_Delay =>
            if ReadAck = '1' or ReadAckD = '1' then
              CtrlState <= Idle;
            end if;
          when AutoRef =>
            CtrlState <= AutoRef_Delay;
          when AutoRef_Delay =>
            CtrlState <= Write_Delay;
          when others =>
            null;
        end case;
      end if;
    end if;
  end process MainState;

  -- Mode Register value a and flag to indicate modeset in progress
  ModeGen : process (CtrlState) is
  begin
    ModeReg <= (others => '0');
    iModeSetFlag <= '1';
    case CtrlState is
      when ModeSet | ModeSetDllRst =>
        ModeReg(15 downto 14) <= "00";
        ModeReg(11 downto 9) <= "010"; -- Write Recovery = 3
        if CtrlState = ModeSetDllRst then
          ModeReg(8) <= '1';  -- DLL reset
        else
          ModeReg(8) <= '0';
        end if;
        ModeReg(6 downto 4) <= "011"; -- CAS Latency = 3
        ModeReg(2 downto 0) <= "010"; -- Burst Length = 4
      when EModeSet | EModeSetOCDDef | EModeSetOcdExit =>
        ModeReg(15 downto 14) <= "01";
        ModeReg(12) <= '0'; -- Outputs enabled
        ModeReg(11) <= '0'; -- RDQS disabled
        ModeReg(10) <= '1'; -- DQS# disabled
        if CtrlState = EModeSetOCDDef then
          ModeReg(9 downto 7) <= "111";
        else
          ModeReg(9 downto 7) <= "000";
        end if;
        ModeReg(6) <= '0';  -- Rtt disabled
        ModeReg(2) <= '0';  --
        ModeReg(5 downto 3) <= "000"; -- Posted CAS = 0;
      when EModeSet2 =>
        ModeReg(15 downto 14) <= "10";
      when EModeSet3 =>
        ModeReg(15 downto 14) <= "11";
      when others =>
        iModeSetFlag <= '0';
    end case;    
  end process ModeGen;    

  ModeSetFlag <= iModeSetFlag;

  -- Flag to indicate rfsh in progress
  RfshDone <= '1' when (CtrlState = AutoRef and Output = RWOp1 and OpEnable = '1') else '0';

  pOpEnable: process (nReset, Clk)
  begin
    if (nReset = '0') then
      OpEnable <= '0';
      RstMain <= '0';
      RstOp <= '0';
    elsif (Clk'event and Clk = '1') then
      OpEnable <= not OpEnable;
      if (OpEnable = '1') then
        RstMain <= SyncRst;
      end if;
      RstOp <= RstMain;
    end if;
  end process pOpEnable;

  OpState : process (nReset, clk)    -- State machine to generate output signals
  begin
    if nReset = '0' then
      Output <= RWOp2;
      CeNext <= '0';
      inRas <= '1';
      inCas <= '1';
      nWe <= '1';
      DOutTriNext <= '1';
      SeqDone <= '0';
      WrPop <= '0';
      RstDqsDivNext <= '0';
    elsif Clk'event and Clk = '1' then
      if (OpEnable = '1') then
        CeNext <= '0';
        inRas <= '1';
        inCas <= '1';
        nWe <= '1';
        DOutTriNext <= '1';
        SeqDone <= '0';
        RstDqsDivNext <= '0';
        if (RstOp = '1') then
          Output <= RWOp3;
          WrPop <= '0';
        else
          case (Output) is
            when RWOp0 =>
              Output <= RwOp1;
              CeNext <= '1';
              inCas <= '0';
              if CtrlState = Write then
                nWe <= '0';
                DOutTriNext <= '0';
              end if;
              WrPop <= '0';
            when RWOp1 =>
              Output <= RWOp2;
              if CtrlState = Read then
                RstDqsDivNext <= '1';
              end if;
            when RWOp2 =>
              Output <= RWOp3;
              SeqDone <= '1';
            when RWOp3 =>
              case (CtrlState) is
                when Read =>
                  Output <= RWOp0;
                  CeNext <= '1';
                  inRas <= '0';
                when Write =>
                  Output <= RWOp0;
                  CeNext <= '1';
                  inRas <= '0';
                  WrPop <= '1';
                when Precharge1 | Precharge2 =>
                  Output <= DelayOp;
                  CeNext <= '1';
                  inRas <= '0';
                  nWe <= '0';
                when AutoRef | AutoRef1 | AutoRef2 =>
                  Output <= DelayOp;
                  CeNext <= '1';
                  inRas <= '0';
                  inCas <= '0';
                when EModeSet2 | EModeSet3 | EModeSet | ModeSetDllRst | ModeSet | EModeSetOCDDef | EModeSetOcdExit =>
                  Output <= DelayOp;
                  CeNext <= '1';
                  inRas <= '0';
                  inCas <= '0';
                  nWe <= '0';
                when Idle | Read_Delay | Write_Delay | AutoRef_Delay | PreChargeWait1 | PreChargeWait2 | AutoRefWait1 | AutoRefWait2 =>
                  Output <= RWOp3;
                  SeqDone <= '1';
                when others =>
                  null;
              end case;
            when DelayOp =>
              Output <= RWOp1;
            when others =>
              null;
          end case;
        end if;
      end if;
    end if;
  end process OpState;

  CeEn : process (nReset, Clk) is 
  begin
    if nReset = '0' then
      nCeEn <= (others => '1');
    elsif Clk'event and Clk = '1' then
      if CtrlState = PreCharge1 or CtrlState = PreCharge2 or CtrlState = AutoRef1 or CtrlState = AutoRef2 or
        iModeSetFlag = '1' or CtrlState = AutoRef then
        nCeEn <= (others => '0');
      else
        nCeEn <= (Others => '1');
        nCeEn(conv_integer(unsigned(DeviceSelect))) <= '0';
      end if;
    end if; 
  end process CeEn;

  OpCe : process (nReset, Clk)   -- State machine to generate nce
  begin
    if nReset = '0' then
      inCe <= '1';
    elsif Clk'event and Clk = '1' then
      if (inRas = '0' or inCas = '0') and inCe = '1' then
        inCe <= '0';
      else
        inCe <= '1';
      end if;
    end if;
  end process OpCe;

  CeDisable <= '0' when (inRas = '0' or inCas = '0') and inCe = '1' else '1';

  TriGen : process (nReset, Clk)
  begin
    if nReset = '0' then
      DOutTriD1 <= '1';
      DOutTriD2 <= '1';
      DOutTri <= '1';
    elsif Clk'event and Clk = '1' then
      DoutTriD1 <= DOutTriNext;
      DoutTriD2 <= DOutTriD1;
      if DOutTriD1 = '0' or DoutTriD2 = '0' then
        DOutTri <= '0';
      else
        DOutTri <= '1';
      end if;
    end if;
  end process TriGen;

  EnWord : process (nReset, Clk)
  begin
    if nReset = '0' then
      ReadNext <= '0';
      DInEnable <= '0';
    elsif Clk'event and Clk = '1' then
      if OpEnable = '1' then
        if (Output = RWOp1 or Output = RWOp2) and CtrlState = Read then
          ReadNext <= '1';
        else
          ReadNext <= '0';
        end if;
      end if;
      if OpEnable = '0' then
        if ReadNext = '1' then
          DInEnable <= '1';
        else
          DInEnable <= '0';
        end if;
      end if;
    end if;
  end process EnWord;

  -- make Write Ack pulse
  AckPulse : process (nReset, Clk) is 
  begin
    if nReset = '0' then
      WriteAck <= '0';
    elsif Clk'event and Clk = '1' then
      if CtrlState = Write and Output = RWOp1 and OpEnable = '0' then
        WriteAck <= '1';
      else
        WriteAck <= '0';
      end if;
    end if; 
  end process AckPulse;  

  -- make RstDqsDiv pulse
--  DqsPulseP : process (nReset, Clk) is 
--  begin
--    if nReset = '0' then
--      iRstDqsDivP <= '0';
--    elsif Clk'event and Clk = '1' then
--      if OpEnable = '1' or iRstDqsDivP = '1' then
--        iRstDqsDivP <= RstDqsDivNext;
--      end if;
--    end if; 
--  end process DqsPulseP;  
--
--  RstDqsDivP <= iRstDqsDivP;
--
--  DqsPulseN : process (nReset, Clk) is 
--  begin
--    if nReset = '0' then
--      RstDqsDivN <= '0';
--    elsif Clk'event and Clk = '0' then
--      if OpEnable = '1' then
--        RstDqsDivN <= RstDqsDivNext;
--      end if;
--    end if; 
--  end process DqsPulseN;  

  FWe0 : process (nReset, Clk) is 
  begin
    if nReset = '0' then
      iFifoWe0 <= '0';
    elsif Clk'event and Clk = '1' then
      if OpEnable = '1' then
        iFifoWe0 <= RstDqsDivNext;
      end if;
        FifoWe0 <= iFifoWe0;
    end if; 
  end process FWe0;  


  FWe1 : process (nReset, Clk) is 
  begin
    if nReset = '0' then
      iFifoWe1 <= '0';
      FifoWe1 <= '0';
    elsif Clk'event and Clk = '0' then
      iFifoWe1 <= iFifoWe0;
      FifoWe1 <= iFifoWe1;
    end if; 
  end process FWe1;  

  -- assign output signals
  nRas <= inRas;
  nCas <= inCas;
  Row <= '1' when Output = RWOp0 or CtrlState = Precharge1 or CtrlState = Precharge2 else '0';
  AddrLatEn <= OpEnable;

end Rtl;
