---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2007
--
-- Filename: Ddr2DataPath.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- DDR2 SDRAM Data path including input FIFOs for Spartan 3 FPGA
---------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
library UNISIM;
use UNISIM.Vcomponents.all;

entity Ddr2DataPath is
  generic(gDataWidth: integer := 64);
  port (Clk         : in    std_ulogic;
        Clk90       : in    std_ulogic;
        SyncRst     : in    std_ulogic;
        SyncRst90   : in    std_ulogic;
        UserWrData  : in    std_logic_vector((gDataWidth*2)-1 downto 0);
        UserWrMask  : in    std_logic_vector((gDataWidth/8*2)-1 downto 0);
        UserWrEn    : in    std_ulogic;
        UserRdData  : out   std_logic_vector((gDataWidth*2)-1 downto 0);
        UserRdVal   : out   std_ulogic;
        UserRdAck   : out   std_ulogic;
        --
        MemClk      : in    std_ulogic;
        nMemClk     : in    std_ulogic;
        DInR        : in    std_logic_vector(gDataWidth-1 downto 0);
        DInF        : in    std_logic_vector(gDataWidth-1 downto 0);
        DQSIn       : in    std_logic_vector((gDataWidth/8-1) downto 0);
        DOutR       : out   std_logic_vector(gDataWidth-1 downto 0);
        DOutF       : out   std_logic_vector(gDataWidth-1 downto 0);
        DmR         : out   std_logic_vector((gDataWidth/8-1) downto 0);
        DmF         : out   std_logic_vector((gDataWidth/8-1) downto 0);
        WriteEn     : out   std_ulogic;
        FifoWe0     : in    std_ulogic;
        FifoWe1     : in    std_ulogic;
        RstDqsDivIn : in    std_ulogic;
        DelaySel    : in    std_logic_vector(4 downto 0);
        Test        : out   std_logic_vector(7 downto 0));
end entity Ddr2DataPath;

architecture Str of Ddr2DataPath is

  component GrayCounter4BitRd
    port (Clk     : in  std_ulogic;
          SyncRst : in  std_ulogic;
          CountEn : in  std_ulogic;
          GrCount : out std_logic_vector(3 downto 0));
  end component;

  component GrayCounter4BitWr
    port (Clk     : in  std_ulogic;
          Reset   : in  std_ulogic;
          CountEn : in  std_ulogic;
          GrCount : out std_logic_vector(3 downto 0));
  end component;

  component Ddr2FifoWrEn0
    port (Clk           : in  std_ulogic;
          Reset         : in  std_ulogic;
          DIn           : in  std_ulogic;
          nRstDqsDelay  : out std_ulogic;
          DOut          : out std_ulogic);
  end component;

  component Ddr2FifoWrEn1
    port (Clk           : in  std_ulogic;
          nRstDqsDelay  : in  std_ulogic;
          Reset         : in  std_ulogic;
          DIn           : in  std_ulogic;
          DOut          : out std_ulogic);
  end component;

  component DqsDelay
    port (ClkIn  : in  std_ulogic;
          SelIn  : in  std_logic_vector(4 downto 0);
          ClkOut : out std_ulogic);
  end component;

  signal SyncRstD         : std_ulogic;

  signal ReadValidData1   : std_ulogic;
  signal ReadValidData1D  : std_ulogic;
  signal iUserRdVal       : std_ulogic;
  signal UserRdValD       : std_ulogic;
  signal Fifo0RdAddr      : std_logic_vector(3 downto 0);
  signal Fifo1RdAddr      : std_logic_vector(3 downto 0);
  signal Fifo0RdAddrD     : std_logic_vector((4*(gDataWidth/8)-1) downto 0);
  signal Fifo1RdAddrD     : std_logic_vector((4*(gDataWidth/8)-1) downto 0);
  signal Fifo0DOut        : std_logic_vector(gDataWidth-1 downto 0);
  signal Fifo1DOut        : std_logic_vector(gDataWidth-1 downto 0);

  signal Fifo0WrAddr      : std_logic_vector((4*(gDataWidth/8)-1) downto 0);
  signal Fifo1WrAddr      : std_logic_vector((4*(gDataWidth/8)-1) downto 0);
  signal Fifo0WrEn        : std_logic_vector(((gDataWidth/8)-1) downto 0);
  signal Fifo1WrEn        : std_logic_vector(((gDataWidth/8)-1) downto 0);
  signal Fifo0WrAddrD     : std_logic_vector(3 downto 0);
  signal Fifo0WrAddrD2    : std_logic_vector(3 downto 0);
  signal Fifo0WrAddrD3    : std_logic_vector(3 downto 0);
  signal Fifo1WrAddrD     : std_logic_vector(3 downto 0);
  signal Fifo1WrAddrD2    : std_logic_vector(3 downto 0);
  signal Fifo1WrAddrD3    : std_logic_vector(3 downto 0);

  signal Fifo0Empty       : std_ulogic;
  signal Fifo1Empty       : std_ulogic;

  signal DqsDelayedCol0   : std_logic_vector(((gDataWidth/8)-1) downto 0);
  signal DqsDelayedCol1   : std_logic_vector(((gDataWidth/8)-1) downto 0);
  signal nDqsDelayedCol0  : std_logic_vector(((gDataWidth/8)-1) downto 0);
  signal nDqsDelayedCol1  : std_logic_vector(((gDataWidth/8)-1) downto 0);

  signal RstDqsDiv        : std_ulogic;
  signal nRstDqsDelay0    : std_logic_vector(((gDataWidth/8)-1) downto 0);

  signal WriteEnD1        : std_logic;
  signal WriteData        : std_logic_vector((2*gDataWidth-1) downto 0);
  signal WriteData1       : std_logic_vector((2*gDataWidth-1) downto 0);
  signal WriteData2       : std_logic_vector((2*gDataWidth-1) downto 0);
  signal WriteData3       : std_logic_vector((2*gDataWidth-1) downto 0);
  signal WriteDataM       : std_logic_vector ((2*(gDataWidth/8)-1) downto 0);
  signal WriteDataM1      : std_logic_vector ((2*(gDataWidth/8)-1) downto 0);
  signal WriteDataM2      : std_logic_vector ((2*(gDataWidth/8)-1) downto 0);
  signal WriteDataM3      : std_logic_vector ((2*(gDataWidth/8)-1) downto 0);

  signal WriteData180     : std_logic_vector((gDataWidth-1) downto 0);
  signal WriteDataM180    : std_logic_vector (((gDataWidth/8)-1) downto 0);

  signal WriteData270     : std_logic_vector((gDataWidth-1) downto 0);
  signal WriteDataM270    : std_logic_vector (((gDataWidth/8)-1) downto 0);

  attribute syn_noprune   : boolean;
  attribute syn_preserve  : boolean;

  attribute syn_noprune of Fifo0RdAddrD   : signal is true;
  attribute syn_noprune of Fifo1RdAddrD   : signal is true;
  attribute syn_preserve of Fifo0RdAddrD  : signal is true;
  attribute syn_preserve of Fifo1RdAddrD  : signal is true;

  attribute syn_preserve of WriteData1    : signal is true;
  attribute syn_preserve of WriteData2    : signal is true;
  attribute syn_preserve of WriteData3    : signal is true;

  attribute syn_preserve of WriteDataM1   : signal is true;
  attribute syn_preserve of WriteDataM2   : signal is true;
  attribute syn_preserve of WriteDataM3   : signal is true;

  attribute syn_preserve of WriteData180  : signal is true;

  attribute syn_preserve of WriteData270  : signal is true;

begin

  RstFF: process(clk)
  begin
    if clk'event and clk = '1' then
      SyncRstD <= SyncRst;
    end if;
  end process RstFF;

  -- Read
  Fifo0RdCount: GrayCounter4BitRd
    port map (Clk     => Clk,
              SyncRst => SyncRst,
              CountEn => ReadValidData1,
              GrCount => Fifo0RdAddr);
  
  Fifo1RdCount: GrayCounter4BitRd
    port map (Clk     => Clk,
              SyncRst => SyncRst,
              CountEn => ReadValidData1,
              GrCount => Fifo1RdAddr);

  GenRdAddr: for i in 0 to (4*(gDataWidth/8)-1) generate
    RdAddr: process(Clk)
    begin
      if Clk'event and Clk = '1' then
        Fifo0RdAddrD(i) <= Fifo0RdAddr(i mod 4);
        Fifo1RdAddrD(i) <= Fifo1RdAddr(i mod 4);
      end if;
    end process RdAddr;
  end generate;

  -- Input FIFOs
  GenFifo: for i in 0 to gDataWidth-1 generate
    GenCol0: if i mod 2 = 0 generate
      FifoBit0: RAM16X1D
        port map (DPO    => Fifo0DOut(i),
                  A0     => Fifo0WrAddr(i/8*4),
                  A1     => Fifo0WrAddr(i/8*4+1),
                  A2     => Fifo0WrAddr(i/8*4+2),
                  A3     => Fifo0WrAddr(i/8*4+3),
                  D      => DInR(i),
                  DPRA0  => Fifo0RdAddrD(i/8*4),
                  DPRA1  => Fifo0RdAddrD(i/8*4+1),
                  DPRA2  => Fifo0RdAddrD(i/8*4+2),
                  DPRA3  => Fifo0RdAddrD(i/8*4+3),
                  WCLK   => DqsDelayedCol1(i/8),
                  SPO    => OPEN,
                  WE     => Fifo0WrEn(i/8));

      FifoBit1: RAM16X1D
        port map (DPO    => Fifo1DOut(i),
                  A0     => Fifo1WrAddr(i/8*4),
                  A1     => Fifo1WrAddr(i/8*4+1),
                  A2     => Fifo1WrAddr(i/8*4+2),
                  A3     => Fifo1WrAddr(i/8*4+3),
                  D      => DInF(i),
                  DPRA0  => Fifo1RdAddrD(i/8*4),
                  DPRA1  => Fifo1RdAddrD(i/8*4+1),
                  DPRA2  => Fifo1RdAddrD(i/8*4+2),
                  DPRA3  => Fifo1RdAddrD(i/8*4+3),
                  WCLK   => nDqsDelayedCol1(i/8),
                  SPO    => OPEN,
                  WE     => Fifo1WrEn(i/8));
    end generate;

    GenCol1: if i mod 2 = 1 generate
      FifoBit0: RAM16X1D
        port map (DPO    => Fifo0DOut(i),
                  A0     => Fifo0WrAddr(i/8*4),
                  A1     => Fifo0WrAddr(i/8*4+1),
                  A2     => Fifo0WrAddr(i/8*4+2),
                  A3     => Fifo0WrAddr(i/8*4+3),
                  D      => DInR(i),
                  DPRA0  => Fifo0RdAddrD(i/8*4),
                  DPRA1  => Fifo0RdAddrD(i/8*4+1),
                  DPRA2  => Fifo0RdAddrD(i/8*4+2),
                  DPRA3  => Fifo0RdAddrD(i/8*4+3),
                  WCLK   => DqsDelayedCol0(i/8),
                  SPO    => OPEN,
                  WE     => Fifo0WrEn(i/8));

      FifoBit1: RAM16X1D
        port map (DPO    => Fifo1DOut(i),
                  A0     => Fifo1WrAddr(i/8*4),
                  A1     => Fifo1WrAddr(i/8*4+1),
                  A2     => Fifo1WrAddr(i/8*4+2),
                  A3     => Fifo1WrAddr(i/8*4+3),
                  D      => DInF(i),
                  DPRA0  => Fifo1RdAddrD(i/8*4),
                  DPRA1  => Fifo1RdAddrD(i/8*4+1),
                  DPRA2  => Fifo1RdAddrD(i/8*4+2),
                  DPRA3  => Fifo1RdAddrD(i/8*4+3),
                  WCLK   => nDqsDelayedCol0(i/8),
                  SPO    => OPEN,
                  WE     => Fifo1WrEn(i/8));
    end generate;
  end generate;

  RdData: process(Clk)
  begin
    if Clk'event and Clk='1' then
      if SyncRstD = '1' then
        UserRdData <= (others => '0');
        iUserRdVal <= '0';
        UserRdValD <= '0';
      else
        if ReadValidData1D = '1' then
          UserRdData <= Fifo1DOut & Fifo0DOut;
        end if;
        ReadValidData1D <= ReadValidData1;
        iUserRdVal <= ReadValidData1D;
        UserRdValD <= iUserRdVal;
      end if;
    end if;
  end process RdData;

  UserRdVal <= iUserRdVal;
  UserRdAck <= UserRdValD and not iUserRdVal;

  --******************************************************************************
  -- DQS Internal Delay Circuit implemented in LUTs
  --******************************************************************************
  
--  GenDelay: for i in 0 to (gDataWidth/8)-1 generate
--    attribute syn_preserve of  DqsDelayCol0: label is true;
--    attribute syn_preserve of  DqsDelayCol1: label is true;
--  begin
--    -- Internal Clock Delay circuit placed in the first
--    -- column (for falling edge data) adjacent to IOBs
--    DqsDelayCol0 : DqsDelay
--      port map (ClkIn => DQSIn(i),
--                SelIn => DelaySel,
--                ClkOut => DqsDelayedCol0(i));
--    -- Internal Clock Delay circuit placed in the second
--    -- column (for rising edge data) adjacent to IOBs
--    DqsDelayCol1 : DqsDelay
--      port map (ClkIn => DQSIn(i),
--                SelIn  => DelaySel,
--                ClkOut => DqsDelayedCol1(i));
--  end generate;
  DqsDelayedCol0 <= (others => MemClk);
  DqsDelayedCol1 <= (others => MemClk);

  nDqsDelayedCol0 <= (others => nMemClk);
  nDqsDelayedCol1 <= (others => nMemClk);

--  nDqsDelayedCol0 <= not DqsDelayedCol0;
--  nDqsDelayedCol1 <= not DqsDelayedCol1;
  -------------------------------------------------------------------------------
  -- write enables
  -------------------------------------------------------------------------------

  RstDqsDelay : DqsDelay
    port map (ClkIn => RstDqsDivIn,
              SelIn  => DelaySel,
              ClkOut => RstDqsDiv);

--  GenWrEn: for i in 0 to (gDataWidth/8)-1 generate
--    Fifo0WrEnInst: Ddr2FifoWrEn0
--       port map (Clk          => nDqsDelayedCol1(i),
--                 Reset        => SyncRstD,
--                 DIn          => RstDqsDiv,
--                 nRstDqsDelay => nRstDqsDelay0(i),
--                 DOut         => Fifo0WrEn(i));
--
--    Fifo1WrEnInst: Ddr2FifoWrEn1
--      port map (Clk          => DqsDelayedCol0(i),
--                nRstDqsDelay => nRstDqsDelay0(i),
--                Reset        => SyncRstD,
--                DIn          => RstDqsDiv,
--                DOut         => Fifo1WrEn(i));
--  end generate;

  FWe0 : process (DqsDelayedCol0) is 
  begin
    if DqsDelayedCol0(0)'event and DqsDelayedCol0(0) = '1' then
      if FifoWe0 = '1' then
        Fifo0WrEn <= (others => '1');
      else
        Fifo0WrEn <= (others => '0');
      end if;
    end if; 
  end process FWe0;  

  FWe1 : process (nDqsDelayedCol0) is 
  begin
    if nDqsDelayedCol0(0)'event and nDqsDelayedCol0(0) = '1' then
      if FifoWe1 = '1' then
        Fifo1WrEn <= (others => '1');
      else
        Fifo1WrEn <= (others => '0');
      end if;
    end if; 
  end process FWe1;  

  -------------------------------------------------------------------------------
  -- write pointer gray counter instances
  -------------------------------------------------------------------------------

  GenWrAddr: for i in 0 to (gDataWidth/8)-1 generate
    Fifo0WrAddrInst: GrayCounter4BitWr
      port map (Clk     => DqsDelayedCol1(i),
                Reset   => SyncRstD,
                CountEn => Fifo0WrEn(i),
                GrCount => Fifo0WrAddr((i*4-1)+4 downto i*4));
    Fifo1WrAddrInst : GrayCounter4BitWr
      port map (Clk     => nDqsDelayedCol0(i),
                Reset   => SyncRstD,
                CountEn => Fifo1WrEn(i),
                GrCount => Fifo1WrAddr((i*4-1)+4 downto i*4));
  end generate;


  -- data_valid signal is derived from fifo_0 and fifo_1 empty signals only
  -- FIFO WRITE POINTER DELAYED SIGNALS
  -- To avoid meta-stability due to the domain crossing from ddr_dqs to clk
  FifoWrSync1: process (clk)
  begin
    if Clk'event and Clk = '1' then
      if SyncRstD = '1' then
        Fifo0WrAddrD <= "0000";
        Fifo1WrAddrD <= "0000";
      else
        Fifo0WrAddrD <= Fifo0WrAddr(23 downto 20);
        Fifo1WrAddrD <= Fifo1WrAddr(23 downto 20);
      end if;
    end if;
  end process;

  -- FIFO WRITE POINTER DOUBLE DELAYED SIGNALS
  FifoWrSync2: process (clk)
  begin
    if Clk'event and Clk = '1' then
      if SyncRstD = '1' then
        Fifo0WrAddrD2 <= "0000";
        Fifo1WrAddrD2 <= "0000";
      else
        Fifo0WrAddrD2 <= Fifo0WrAddrD;
        Fifo1WrAddrD2 <= Fifo1WrAddrD;
      end if;
    end if;
  end process;

  FifoWrSync3: process (clk)
  begin
    if Clk'event and Clk = '1' then
      if SyncRstD = '1' then
        Fifo0WrAddrD3 <= "0000";
        Fifo1WrAddrD3 <= "0000";
      else
        Fifo0WrAddrD3 <= Fifo0WrAddrD2;
        Fifo1WrAddrD3 <= Fifo1WrAddrD2;
      end if;
    end if;
  end process;

  -- user data valid output signal from data path

  Fifo0Empty <= '1' when (Fifo0RdAddr(3 downto 0) = Fifo0WrAddrD3(3 downto 0)) else '0';
  Fifo1Empty <= '1' when (Fifo1RdAddr(3 downto 0) = Fifo1WrAddrD3(3 downto 0)) else '0';
  ReadValidData1 <= ((not Fifo0Empty) and (not Fifo1Empty));

--  Test(4) <= Fifo0WrEn(0);
--  Test(5) <= Fifo1WrEn(0);
--  Test(6) <= RstDqsDivIn;
--  Test(7) <= nRstDqsDelay0(0);
  Test(3 downto 0) <= Fifo0WrAddrD;
  Test(7 downto 4) <= Fifo1WrAddrD;

  -- write data
  WriteData <= UserWrData;
  WriteDataM <= not UserWrMask;

  WrDPipe: process(Clk)
  begin
    if Clk'event and Clk = '1' then
      WriteData1  <= WriteData;
      WriteDataM1 <= WriteDataM;
      WriteData2  <= WriteData1;
      WriteDataM2 <= WriteDataM1;
      WriteData3  <= WriteData2;
      WriteDataM3 <= WriteDataM2;
    end if;
  end process;

  WrD270Pipe: process(Clk90)
  begin
    if Clk90'event and Clk90 = '0' then
      WriteData270  <= WriteData1((gDataWidth-1) downto 0); 
      WriteDataM270 <= WriteDataM1(((gDataWidth/8)-1) downto 0);
    end if;
  end process;

  WrD180Pipe: process(Clk)
  begin
    if Clk'event and Clk = '0' then
      WriteData180  <= WriteData270;
      WriteDataM180 <= WriteDataM270;
    end if;
  end process;

  DOutR <= WriteData3((gDataWidth*2-1) downto gDataWidth);
  DmR   <= WriteDataM3(((gDataWidth/8)*2-1) downto (gDataWidth/8));
  DOutF <= WriteData180;
  DmF   <= WriteDataM180;

  -- write enable for data path
  process(Clk)
  begin
    if Clk'event and Clk = '1' then
      WriteEnD1 <= UserWrEn;
      WriteEn <= WriteEnD1;
    end if;
  end process;

end architecture Str;
