----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    19:52:23 05/18/2016 
-- Design Name: 
-- Module Name:    DDR2_FSM - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity DDR2_FSM is
    Port ( FSM_in_clk           : IN    STD_LOGIC;
           FSM_in_rst           : IN    STD_LOGIC;
           FSM_in_data          : IN    STD_LOGIC_VECTOR (7 downto 0);
           FSM_in_sof           : IN    STD_LOGIC;
           FSM_in_eof           : IN    STD_LOGIC;
           FSM_in_src_rdy       : IN    STD_LOGIC;
           FSM_in_dst_rdy       : OUT   STD_LOGIC;
           FSM_out_clk          : IN    STD_LOGIC;
           FSM_out_rst          : IN    STD_LOGIC;
           FSM_out_data         : OUT   STD_LOGIC_VECTOR (7 downto 0);
           FSM_out_sof          : OUT   STD_LOGIC;
           FSM_out_eof          : OUT   STD_LOGIC;
           FSM_out_src_rdy      : OUT   STD_LOGIC;
           FSM_out_dst_rdy      : IN    STD_LOGIC;
           DDR2_a               : OUT   STD_LOGIC_VECTOR(12 DOWNTO 0);
           DDR2_ba              : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);
           DDR2_cas_n           : OUT   STD_LOGIC;
           DDR2_cke             : OUT   STD_LOGIC_VECTOR(0 DOWNTO 0);
           DDR2_ck_n            : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);
           DDR2_ck              : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);
           DDR2_cs_n            : OUT   STD_LOGIC_VECTOR(0 DOWNTO 0);
           DDR2_dq              : INOUT STD_LOGIC_VECTOR(63 DOWNTO 0);
           DDR2_dqs_n           : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);
           DDR2_dqs             : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);
           DDR2_odt             : OUT   STD_LOGIC_VECTOR(0 DOWNTO 0);
           DDR2_ras_n           : OUT   STD_LOGIC;
           DDR2_we_n            : OUT   STD_LOGIC;
           DDR2_sys_rst_n       : IN    STD_LOGIC;
           DDR2_clk_sys         : IN    STD_LOGIC;
           DDR2_clk_200         : IN    STD_LOGIC;
           DDR2_interface_rst   : OUT   STD_LOGIC
         );  
end DDR2_FSM;

architecture Behavioral of DDR2_FSM is

    COMPONENT DDR_input
    PORT (
        rst             : IN    STD_LOGIC;
        wr_clk          : IN    STD_LOGIC;
        rd_clk          : IN    STD_LOGIC;
        din             : IN    STD_LOGIC_VECTOR(9 DOWNTO 0);
        wr_en           : IN    STD_LOGIC;
        rd_en           : IN    STD_LOGIC;
        dout            : OUT   STD_LOGIC_VECTOR(79 DOWNTO 0);
        full            : OUT   STD_LOGIC;
        almost_full     : OUT   STD_LOGIC;
        empty           : OUT   STD_LOGIC;
        almost_empty    : OUT   STD_LOGIC;
        valid           : OUT   STD_LOGIC
    );
    END COMPONENT;
    
    COMPONENT DDR_output
    PORT (
        rst             : IN    STD_LOGIC;
        wr_clk          : IN    STD_LOGIC;
        rd_clk          : IN    STD_LOGIC;
        din             : IN    STD_LOGIC_VECTOR(79 DOWNTO 0);
        wr_en           : IN    STD_LOGIC;
        rd_en           : IN    STD_LOGIC;
        dout            : OUT   STD_LOGIC_VECTOR(9 DOWNTO 0);
        full            : OUT   STD_LOGIC;
        almost_full     : OUT   STD_LOGIC;
        empty           : OUT   STD_LOGIC;
        almost_empty    : OUT   STD_LOGIC
    );
    END COMPONENT;
    
    component ddr2_MEM
    generic(
        BANK_WIDTH               : integer := 2;    
                                -- # of memory bank addr bits.
        CKE_WIDTH                : integer := 1;    
                                -- # of memory clock enable outputs.
        CLK_WIDTH                : integer := 2;    
                                -- # of clock outputs.
        COL_WIDTH                : integer := 10;    
                                -- # of memory column bits.
        CS_NUM                   : integer := 1;    
                                -- # of separate memory chip selects.
        CS_WIDTH                 : integer := 1;    
                                -- # of total memory chip selects.
        CS_BITS                  : integer := 0;    
                                -- set to log2(CS_NUM) (rounded up).
        DQ_WIDTH                 : integer := 64;    
                                -- # of data width.
        DQ_PER_DQS               : integer := 8;    
                                -- # of DQ data bits per strobe.
        DQS_WIDTH                : integer := 8;    
                                -- # of DQS strobes.
        DQ_BITS                  : integer := 6;    
                                -- set to log2(DQS_WIDTH*DQ_PER_DQS).
        DQS_BITS                 : integer := 3;    
                                -- set to log2(DQS_WIDTH).
        ODT_WIDTH                : integer := 1;    
                                -- # of memory on-die term enables.
        ROW_WIDTH                : integer := 13;    
                                -- # of memory row and # of addr bits.
        ADDITIVE_LAT             : integer := 0;    
                                -- additive write latency.
        BURST_LEN                : integer := 4;    
                                -- burst length (in double words).
        BURST_TYPE               : integer := 0;    
                                -- burst type (=0 seq; =1 interleaved).
        CAS_LAT                  : integer := 4;    
                                -- CAS latency.
        ECC_ENABLE               : integer := 0;    
                                -- enable ECC (=1 enable).
        APPDATA_WIDTH            : integer := 128;    
                                -- # of usr read/write data bus bits.
        MULTI_BANK_EN            : integer := 1;    
                                -- Keeps multiple banks open. (= 1 enable).
        TWO_T_TIME_EN            : integer := 1;    
                                -- 2t timing for unbuffered dimms.
        ODT_TYPE                 : integer := 0;    
                                -- ODT (=0(none),=1(75),=2(150),=3(50)).
        REDUCE_DRV               : integer := 0;    
                                -- reduced strength mem I/O (=1 yes).
        REG_ENABLE               : integer := 0;    
                                -- registered addr/ctrl (=1 yes).
        TREFI_NS                 : integer := 7800;    
                                -- auto refresh interval (ns).
        TRAS                     : integer := 40000;    
                                -- active->precharge delay.
        TRCD                     : integer := 15000;    
                                -- active->read/write delay.
        TRFC                     : integer := 105000;    
                                -- refresh->refresh, refresh->active delay.
        TRP                      : integer := 15000;    
                                -- precharge->command delay.
        TRTP                     : integer := 7500;    
                                -- read->precharge delay.
        TWR                      : integer := 15000;    
                                -- used to determine write->precharge.
        TWTR                     : integer := 7500;    
                                -- write->read delay.
        HIGH_PERFORMANCE_MODE    : boolean := TRUE;    
                                -- # = TRUE, the IODELAY performance mode is set
                                -- to high.
                                -- # = FALSE, the IODELAY performance mode is set
                                -- to low.
        SIM_ONLY                 : integer := 0;    
                                -- = 1 to skip SDRAM power up delay.
        DEBUG_EN                 : integer := 0;    
                                -- Enable debug signals/controls.
                                -- When this parameter is changed from 0 to 1,
                                -- make sure to uncomment the coregen commands
                                -- in ise_flow.bat or create_ise.bat files in
                                -- par folder.
        CLK_PERIOD               : integer := 4000;    
                                -- Core/Memory clock period (in ps).
        DLL_FREQ_MODE            : string := "HIGH";    
                                -- DCM Frequency range.
        CLK_TYPE                 : string := "SINGLE_ENDED";    
                                -- # = "DIFFERENTIAL " ->; Differential input clocks ,
                                -- # = "SINGLE_ENDED" -> Single ended input clocks.
        NOCLK200                 : boolean := FALSE;    
                                -- clk200 enable and disable
        RST_ACT_LOW              : integer := 1     
                                -- =1 for active low reset, =0 for active high.
    );
    port (
        ddr2_dq             : INOUT     STD_LOGIC_VECTOR((DQ_WIDTH-1) downto 0);
        ddr2_a              : OUT       STD_LOGIC_VECTOR((ROW_WIDTH-1) downto 0);
        ddr2_ba             : OUT       STD_LOGIC_VECTOR((BANK_WIDTH-1) downto 0);
        ddr2_ras_n          : OUT       STD_LOGIC;
        ddr2_cas_n          : OUT       STD_LOGIC;
        ddr2_we_n           : OUT       STD_LOGIC;
        ddr2_cs_n           : OUT       STD_LOGIC_VECTOR((CS_WIDTH-1) downto 0);
        ddr2_odt            : OUT       STD_LOGIC_VECTOR((ODT_WIDTH-1) downto 0);
        ddr2_cke            : OUT       STD_LOGIC_VECTOR((CKE_WIDTH-1) downto 0);
        sys_clk             : IN        STD_LOGIC;
        idly_clk_200        : IN        STD_LOGIC;
        sys_rst_n           : IN        STD_LOGIC;
        phy_init_done       : OUT       STD_LOGIC;
        rst0_tb             : OUT       STD_LOGIC;
        clk0_tb             : OUT       STD_LOGIC;
        app_wdf_afull       : OUT       STD_LOGIC;
        app_af_afull        : OUT       STD_LOGIC;
        rd_data_valid       : OUT       STD_LOGIC;
        app_wdf_wren        : IN        STD_LOGIC;
        app_af_wren         : IN        STD_LOGIC;
        app_af_addr         : IN        STD_LOGIC_VECTOR(30 downto 0);
        app_af_cmd          : IN        STD_LOGIC_VECTOR(2 downto 0);
        rd_data_fifo_out    : OUT       STD_LOGIC_VECTOR((APPDATA_WIDTH-1) downto 0);
        app_wdf_data        : IN        STD_LOGIC_VECTOR((APPDATA_WIDTH-1) downto 0);
        ddr2_dqs            : INOUT     STD_LOGIC_VECTOR((DQS_WIDTH-1) downto 0);
        ddr2_dqs_n          : INOUT     STD_LOGIC_VECTOR((DQS_WIDTH-1) downto 0);
        ddr2_ck             : OUT       STD_LOGIC_VECTOR((CLK_WIDTH-1) downto 0);
        ddr2_ck_n           : OUT       STD_LOGIC_VECTOR((CLK_WIDTH-1) downto 0)
    );
    end component;
    
    TYPE t_state_DDR2 is (check_rd, check_wr, do_rd, do_wr);
    
    SIGNAL next_state, pres_state : t_state_DDR2 := check_wr;
    
    SIGNAL input_empty      : STD_LOGIC;
    SIGNAL ddr2_full        : STD_LOGIC := '0';
    SIGNAL output_afull     : STD_LOGIC;
    SIGNAL ddr2_empty       : STD_LOGIC := '1';
    SIGNAL clk_main         : STD_LOGIC;
    SIGNAL command          : STD_LOGIC_VECTOR(2 downto 0);
    SIGNAL address          : STD_LOGIC_VECTOR(30 downto 0) := (OTHERS => '0');
    SIGNAL rd_addr          : UNSIGNED(30 downto 0) := (OTHERS => '0');
    SIGNAL wr_addr          : UNSIGNED(30 downto 0) := (OTHERS => '0');
    SIGNAL data_en          : STD_LOGIC := '0';
    SIGNAL addr_en          : STD_LOGIC := '0';
    SIGNAL addr_jump        : STD_LOGIC := '0';
    SIGNAL after_wr         : STD_LOGIC;
    SIGNAL after_rd         : STD_LOGIC;
    SIGNAL data_ddr2_in     : STD_LOGIC_VECTOR((APPDATA_WIDTH-1) downto 0);
    SIGNAL data_ddr2_out    : STD_LOGIC_VECTOR((APPDATA_WIDTH-1) downto 0);
    SIGNAL data_FSM         : STD_LOGIC_VECTOR(79 downto 0);
    SIGNAL input_en         : STD_LOGIC := '0';
    SIGNAL output_en        : STD_LOGIC := '0';

BEGIN

    u_ddr2_MEM : ddr2_MEM
    generic map (
        BANK_WIDTH => BANK_WIDTH,
        CKE_WIDTH => CKE_WIDTH,
        CLK_WIDTH => CLK_WIDTH,
        COL_WIDTH => COL_WIDTH,
        CS_NUM => CS_NUM,
        CS_WIDTH => CS_WIDTH,
        CS_BITS => CS_BITS,
        DQ_WIDTH => DQ_WIDTH,
        DQ_PER_DQS => DQ_PER_DQS,
        DQS_WIDTH => DQS_WIDTH,
        DQ_BITS => DQ_BITS,
        DQS_BITS => DQS_BITS,
        ODT_WIDTH => ODT_WIDTH,
        ROW_WIDTH => ROW_WIDTH,
        ADDITIVE_LAT => ADDITIVE_LAT,
        BURST_LEN => BURST_LEN,
        BURST_TYPE => BURST_TYPE,
        CAS_LAT => CAS_LAT,
        ECC_ENABLE => ECC_ENABLE,
        APPDATA_WIDTH => APPDATA_WIDTH,
        MULTI_BANK_EN => MULTI_BANK_EN,
        TWO_T_TIME_EN => TWO_T_TIME_EN,
        ODT_TYPE => ODT_TYPE,
        REDUCE_DRV => REDUCE_DRV,
        REG_ENABLE => REG_ENABLE,
        TREFI_NS => TREFI_NS,
        TRAS => TRAS,
        TRCD => TRCD,
        TRFC => TRFC,
        TRP => TRP,
        TRTP => TRTP,
        TWR => TWR,
        TWTR => TWTR,
        HIGH_PERFORMANCE_MODE => HIGH_PERFORMANCE_MODE,
        SIM_ONLY => SIM_ONLY,
        DEBUG_EN => DEBUG_EN,
        CLK_PERIOD => CLK_PERIOD,
        DLL_FREQ_MODE => DLL_FREQ_MODE,
        CLK_TYPE => CLK_TYPE,
        NOCLK200 => NOCLK200,
        RST_ACT_LOW => RST_ACT_LOW
    )
    port map (
        ddr2_dq             => DDR2_dq,
        ddr2_a              => DDR2_a,
        ddr2_ba             => DDR2_ba,
        ddr2_ras_n          => DDR2_ras_n,
        ddr2_cas_n          => DDR2_cas_n,
        ddr2_we_n           => DDR2_we_n,
        ddr2_cs_n           => DDR2_cs_n,
        ddr2_odt            => DDR2_odt,
        ddr2_cke            => DDR2_cke,
        sys_clk             => DDR2_clk_sys,
        idly_clk_200        => DDR2_clk_200,
        sys_rst_n           => DDR2_sys_rst_n,
        phy_init_done       => OPEN,
        rst0_tb             => DDR2_interface_rst,
        clk0_tb             => clk_main,
        app_wdf_afull       => OPEN,
        app_af_afull        => OPEN,
        rd_data_valid       => OPEN,
        app_wdf_wren        => data_en,
        app_af_wren         => addr_en,
        app_af_addr         => address,
        app_af_cmd          => command,
        rd_data_fifo_out    => data_ddr2_out,
        app_wdf_data        => data_ddr2_in,
        ddr2_dqs            => DDR2_dqs,
        ddr2_dqs_n          => DDR2_dqs_n,
        ddr2_ck             => DDR2_ck,
        ddr2_ck_n           => DDR2_ck_n
    );
    
    FSM_input : DDR_input
    PORT MAP (
        rst             => FSM_in_rst,
        wr_clk          => FSM_in_clk,
        rd_clk          => clk_main,
        din             => FSM_in_data & FSM_in_sof & FSM_in_eof,
        wr_en           => FSM_in_src_rdy,
        rd_en           => input_en,
        dout            => data_FSM,
        full            => OPEN,
        almost_full     => NOT FSM_in_dst_rdy,
        empty           => input_empty,
        almost_empty    => OPEN,
        valid           => OPEN
    );
    
    FSM_output : DDR_output
    PORT MAP (
        rst             => FSM_out_rst,
        wr_clk          => clk_main,
        rd_clk          => FSM_out_clk,
        din             => data_FSM,
        wr_en           => output_en,
        rd_en           => FSM_out_dst_rdy,
        dout            => FSM_out_data & FSM_out_sof & FSM_out_eof,
        full            => OPEN,
        almost_full     => output_afull,
        empty           => NOT FSM_out_src_rdy,
        almost_empty    => OPEN
    );
    
    -- STATE MACHINE --
    -- Next state determination --
    PROCESS (pres_state, input_empty, ddr2_full, output_afull, ddr2_empty) BEGIN
        CASE pres_state IS
            WHEN check_wr =>    IF input_empty = '0' AND ddr2_full = '0' THEN
                                    next_state <= do_wr;
                                ELSE
                                    next_state <= check_rd;
                                END IF;
            WHEN check_rd =>    IF output_afull = '0' AND ddr2_empty = '0' THEN
                                    next_state <= do_rd;
                                ELSE
                                    next_state <= check_wr;
                                END IF;
            WHEN do_wr    =>    next_state <= check_rd;
            WHEN do_rd    =>    next_state <= check_wr;
        END CASE;
    END PROCESS;
    
    -- Next state aplication --
    PROCESS (clk_main) BEGIN
        IF rising_edge(clk_main) THEN
            pres_state <= next_state;
        END IF;
    END PROCESS;
    
    -- Present state functionality --
    PROCESS (pres_state, after_rd, after_wr, rd_addr, wr_addr) BEGIN
        CASE pres_state IS
            WHEN check_wr =>    command <= X"0";
                                address <= STD_LOGIC_VECTOR(wr_addr);
                                data_en <= '0';
                                addr_en <= '0';
                                input_en <= '0';
                                output_en <= '0';
                                IF after_rd = '1' THEN
                                    IF STD_LOGIC_VECTOR(rd_addr) = X"7FFFFFFF" THEN
                                        IF STD_LOGIC_VECTOR(wr_addr) = X"00000000" THEN
                                            ddr2_empty <= '1';
                                        END IF;
                                        rd_addr <= X"00000000";
                                        addr_jump <= '0';
                                    ELSE
                                        IF STD_LOGIC_VECTOR(wr_addr) = STD_LOGIC_VECTOR(rd_addr + 1) THEN
                                            ddr2_empty <= '1';
                                        END IF;
                                        rd_addr <= rd_addr + 1;
                                    END IF;
                                END IF;
            WHEN check_rd =>    command <= X"1";
                                address <= STD_LOGIC_VECTOR(rd_addr);
                                data_en <= '0';
                                addr_en <= '0';
                                input_en <= '0';
                                output_en <= '0';
                                IF after_wr = '1' THEN
                                    IF STD_LOGIC_VECTOR(wr_addr) = X"7FFFFFFF" THEN
                                        IF STD_LOGIC_VECTOR(rd_addr) = X"00000000" THEN
                                            ddr2_full <= '1';
                                            addr_jump <= '0';
                                        ELSE
                                            addr_jump <= '1';
                                        END IF;
                                        wr_addr <= X"00000000";
                                    ELSE
                                        IF STD_LOGIC_VECTOR(rd_addr) = STD_LOGIC_VECTOR(wr_addr + 1) THEN
                                            ddr2_full <= '1';
                                        END IF;
                                        wr_addr <= wr_addr + 1;
                                    END IF;
                                END IF;
            WHEN do_wr    =>    addr_en <= '1';
                                data_en <= '1';
                                input_en <= '1';
                                data_ddr2_in <= data_FSM & (OTHERS => '0');
                                after_wr <= '1';
            WHEN do_rd    =>    addr_en <= '1';
                                output_en <= '1';
                                data_FSM <= data_ddr2_out(79 downto 0);
                                after_rd <= '1';
        END CASE;
    END PROCESS;

END Behavioral;

