-------------------------------------------------------------
-- MSS copyright 2012-2017
--	Filename:  DDR3_DELAY.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 0
--	Date last modified: 1/7/18
-- Inheritance: 	SDRAM_DELAY.VHD 8/19/13, DDR3_WRAPPER.VHD 3/5/17
-------------------------------------------------------------
-- description: Programmable delay between input and output data streams through a DDR3 SDRAM
-- Features: Independent clocks on user and DDR3 sides.
-- VHDL driver to interface the FPGA with a DDR3 DIMM/SODIMM module.
-- Pre-synthesis configuration settings are selected by parameters in the Generic.
-- Fixed DDR3 clocks: 330-400 MHz / 660-800 MSamples/s on Artix-7 -1. Use MIG 7 series to change as per target FPGA.
-- MIG 7 series IP automatically manages SDRAM: memory initialization, calibration, periodic refresh, etc.
-- See Xilinx ug586_7series_MIS p81,p162,etc
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
Library UNISIM;
use UNISIM.vcomponents.all;

entity DDR3_DELAY is
	Generic (
	RAMB_ADDR_WIDTH: in integer := 7;
		-- block RAM address width(equivalent to selecting the block RAM size)
		-- data width is always 512
		-- For CLK at 125 MHz and MIG_UI_CLKg = 83.3 MHz, select 6(or 7???) (because of the latency in reporting 
		-- the input flow control information DATA1_IN_CTS
	DDR3_NRANKS: integer := 2;	-- number of ranks , 1GB = 1, 8GB = 2
	DDR3_NADDRBITS: integer := 16;	-- number of address bits, 1GB = 14, 8GB = 16
		-- MIG must be configured accordingly
	APP_NADDRBITS: integer := 30;		
		-- number of application address bits (address of 64-bit words), 
		-- in accordance with memory size 28(1GB),30(8GB)
	DATA1_STARTADDR: std_logic_vector(31 downto 0);
		-- DDR3 SDRAM start address (inclusive). Expressed in SDRAM words (not bytes).
		-- Must be a multiple of 8 SDRAM words (= multiple of 16 complex input samples)
	DATA1_CEILINGADDR: std_logic_vector(31 downto 0);
		-- optional ceiling address (exclusive). Expressed in SDRAM words (not bytes).
		-- Must be a multiple of 8 SDRAM words (= multiple of 16 complex input samples)
	SIMULATION: std_logic := '0'
		-- 1 during simulation, 0 for release	
	);
    Port ( 
		--// CLK, RESET
		CLK: in std_logic;
			-- user-interface GLOBAL CLOCK. 
			-- Designer is responsible for instantiating an external BUFG.
		SYNC_RESET: in std_logic;
			-- CLK-synchronous reset
		CLK200: in std_logic; 
			-- 200 MHz processing clock from 25 MHz reference. not global
			-- for use by MIG for generating I/O delays
		CLK333p3: in std_logic; 
			-- 333.33 MHz from 25 Mhz reference. not global 
		PLL_LOCKED_IN: in std_logic;
			-- external PLL generating the CLK200 and CLK333p3 report lock status
			
		--// USER interface
		-- Input stream
		DATA1_IN: in std_logic_vector(31 downto 0);
			-- input samples (word width large enought for 2*16-bit complex samples)
		DATA1_IN_VALID: in std_logic;
			-- data valid qualifier
		DATA1_IN_CTS: out std_logic;
			-- Clear to send: flow control signal indicating that the input elastic buffer has room for more 
			-- user data. Always check CTS before sending additional data words.

		-- Output stream
		DATA1_OUT: out std_logic_vector(31 downto 0);
			-- output samples (word width large enought for 2*16-bit complex samples)
		DATA1_OUT_VALID: out std_logic;
			-- data valid qualifier
		DATA1_OUT_CTS: in std_logic;
			-- Clear to send: flow control signal. User requests data from the output elastic buffer.
		DATA1_OUT_EMPTY: out std_logic;
			-- output elastic buffer is empty.
			
		-- CONTROLS
		DATA1_DELAY_CHANGE_TOGGLE: in std_logic;
			-- toggles when DATA1_DELAY changed
		DATA1_DELAY: in std_logic_vector(31 downto 0);
			-- delay between SDRAM input stream and output stream. Expressed in SDRAM words (not bytes). 
			-- Must be a multiple of 8 SDRAM words (= multiple of 16 complex input samples)
			-- 0 delay is valid.
			-- DATA1_DELAY MUST be less than the memory window size(DATA1_CEILINGADDR - DATA1_STARTADDR)
		
		
		--// DDR3 SDRAM DIMM/SODIMM. 1.5 V
		-- Inouts
		ddr3_dq                        : inout std_logic_vector(63 downto 0);
		ddr3_dqs_p                     : inout std_logic_vector(7 downto 0);
		ddr3_dqs_n                     : inout std_logic_vector(7 downto 0);

		-- Outputs
		ddr3_addr                      : out   std_logic_vector((DDR3_NADDRBITS-1) downto 0);
		ddr3_ba                        : out   std_logic_vector(2 downto 0);
		ddr3_ras_n                     : out   std_logic;
		ddr3_cas_n                     : out   std_logic;
		ddr3_we_n                      : out   std_logic;
		ddr3_reset_n                   : out   std_logic;
		ddr3_ck_p                      : out   std_logic_vector((DDR3_NRANKS-1) downto 0);
		ddr3_ck_n                      : out   std_logic_vector((DDR3_NRANKS-1) downto 0);
		ddr3_cke                       : out   std_logic_vector((DDR3_NRANKS-1) downto 0);
		ddr3_cs_n                      : out   std_logic_vector((DDR3_NRANKS-1) downto 0);
		ddr3_odt                       : out   std_logic_vector((DDR3_NRANKS-1) downto 0);
		ddr3_dm                        : out   std_logic_vector(7 downto 0);
		

		--// Monitoring + Test points 
		DATA1_IN_ADDR: out std_logic_vector(31 downto 0);
		DATA1_OUT_ADDR: out std_logic_vector(31 downto 0);
			-- Monitoring the address pointers at the DDR3 SDRAM interface (both synchronous with CLK)
			-- Usage: cannot be read immediately after sending a read or write command, as it takes time for 
			-- the current addresses to propagate through the clock domains.
		MEMORY_RDY_OUT: out std_logic;
			-- '1' when the DDR3 SDRAM is ready to operate (all clocks stable, memory is reset/initialized/calibrated)
			-- commands received before the memory is ready may not be acted upon properly.
		TP: out std_logic_vector(20 downto 1)
	
 );
end entity;

architecture Behavioral of DDR3_DELAY is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT BRAM_DP2
	 GENERIC(
		DATA_WIDTHA: integer;
		ADDR_WIDTHA: integer;
		DATA_WIDTHB: integer;
		ADDR_WIDTHB: integer
	 );
	 PORT(
		CSA : IN std_logic;
		CLKA   : in  std_logic;
		WEA    : in  std_logic;
		OEA : IN std_logic;
		ADDRA  : in  std_logic_vector(ADDR_WIDTHA-1 downto 0);
		DIA   : in  std_logic_vector(DATA_WIDTHA-1 downto 0);
		DOA  : out std_logic_vector(DATA_WIDTHA-1 downto 0);
		CSB : IN std_logic;
		CLKB   : in  std_logic;
		WEB    : in  std_logic;
		OEB : IN std_logic;
		ADDRB  : in  std_logic_vector(ADDR_WIDTHB-1 downto 0);
		DIB   : in  std_logic_vector(DATA_WIDTHB-1 downto 0);
		DOB  : out std_logic_vector(DATA_WIDTHB-1 downto 0)
		  );
	 END COMPONENT;

   component mig_7series_0
	port (
		 ddr3_dq       : inout std_logic_vector(63 downto 0);
		 ddr3_dqs_p    : inout std_logic_vector(7 downto 0);
		 ddr3_dqs_n    : inout std_logic_vector(7 downto 0);

		 ddr3_addr     : out   std_logic_vector((DDR3_NADDRBITS-1) downto 0);
		 ddr3_ba       : out   std_logic_vector(2 downto 0);
		 ddr3_ras_n    : out   std_logic;
		 ddr3_cas_n    : out   std_logic;
		 ddr3_we_n     : out   std_logic;
		 ddr3_reset_n  : out   std_logic;
		 ddr3_ck_p     : out   std_logic_vector((DDR3_NRANKS - 1) downto 0);
		 ddr3_ck_n     : out   std_logic_vector((DDR3_NRANKS - 1) downto 0);
		 ddr3_cke      : out   std_logic_vector((DDR3_NRANKS - 1) downto 0);
		 ddr3_cs_n     : out   std_logic_vector((DDR3_NRANKS - 1) downto 0);
		 ddr3_dm       : out   std_logic_vector(7 downto 0);
		 ddr3_odt      : out   std_logic_vector((DDR3_NRANKS - 1) downto 0);
		 app_addr                  : in    std_logic_vector((APP_NADDRBITS - 1) downto 0);
		 app_cmd                   : in    std_logic_vector(2 downto 0);
		 app_en                    : in    std_logic;
		 app_wdf_data              : in    std_logic_vector(511 downto 0);
		 app_wdf_end               : in    std_logic;
		 app_wdf_mask         : in    std_logic_vector(63 downto 0);
		 app_wdf_wren              : in    std_logic;
		 app_rd_data               : out   std_logic_vector(511 downto 0);
		 app_rd_data_end           : out   std_logic;
		 app_rd_data_valid         : out   std_logic;
		 app_rdy                   : out   std_logic;
		 app_wdf_rdy               : out   std_logic;
		 app_sr_req                : in    std_logic;
		 app_ref_req               : in    std_logic;
		 app_zq_req                : in    std_logic;
		 app_sr_active             : out   std_logic;
		 app_ref_ack               : out   std_logic;
		 app_zq_ack                : out   std_logic;
		 ui_clk                    : out   std_logic;
		 ui_clk_sync_rst           : out   std_logic;
		 init_calib_complete       : out   std_logic;
		 -- System Clock Ports
		 sys_clk_i                      : in    std_logic;
		 -- Reference Clock Ports
		 clk_ref_i                                : in    std_logic;
		sys_rst                     : in    std_logic
		);
    end component;
	 
	 
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- NOTATIONS: 
-- _E as one-CLK early sample
-- _D as one-CLK delayed sample
-- _D2 as two-CLKs delayed sample
-- _X as (sign-)extended precision

--// CLK & RESETS ---------
signal CLK200g: std_logic := '0';
signal CLK333p3g: std_logic := '0';
signal ASYNC_RESETn: std_logic := '0';
signal SYNC_RESET100n: std_logic := '0';
signal MIG_UI_CLKg: std_logic := '0';
signal MIG_UI_CLK_SYNC_RST: std_logic := '0';
signal MEMORY_RDY_local: std_logic := '0';

--// DATA1_IN INPUT ELASTIC BUFFER ---------------------------------------------------
signal RAMB1_DIA: std_logic_vector(31 downto 0) := (others => '0');
signal RAMB1_DOB: std_logic_vector(511 downto 0) := (others => '0');
signal RAMB1_ADDRA: unsigned((RAMB_ADDR_WIDTH+3) downto 0) := (others => '0');
signal RAMB1_ADDRA_D: unsigned((RAMB_ADDR_WIDTH+3) downto 0) := (others => '0');
signal RAMB1_ADDRA_D2: unsigned((RAMB_ADDR_WIDTH+3) downto 0) := (others => '0');
signal RAMB1_ADDRA_D3: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal COUNTER1_MOD8: unsigned(2 downto 0) := "000";
signal RAMB1_ADDRA_IS_STABLE: std_logic := '0';
signal RAMB1_ADDRA_IS_STABLE_D: std_logic := '0';
signal RAMB1_ADDRB: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal RAMB1_ADDRB_INC: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal RAMB1_ADDRB0: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal RAMB1_WEA: std_logic := '0';
signal RAMB1_SIZE: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal DATA1_IN_CTS_local: std_logic := '0';

--// DATA1_OUT OUTPUT ELASTIC BUFFER ---------------------------------------------------
signal RAMB2_WEB: std_logic := '0'; 
signal RAMB2_DIB: std_logic_vector(511 downto 0) := (others => '0');
signal RAMB2_ADDRB: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal RAMB2_ADDRB_D: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal RAMB2_ADDRB_D2: unsigned((RAMB_ADDR_WIDTH-1) downto 0) := (others => '0');
signal RAMB2_ADDRB_D3: unsigned((RAMB_ADDR_WIDTH+3) downto 0) := (others => '0');
signal COUNTER2_MOD8: unsigned(2 downto 0) := (others => '0');
signal RAMB2_ADDRB_IS_STABLE: std_logic := '0';
signal RAMB2_ADDRB_IS_STABLE_D: std_logic := '0';
signal RAMB2_ADDRA: unsigned((RAMB_ADDR_WIDTH+3) downto 0) := (others => '1');
signal RAMB2_DOA: std_logic_vector(31 downto 0) := (others => '0');
signal RAMB2_SIZE: unsigned((RAMB_ADDR_WIDTH+3) downto 0) := (others => '0');
signal DATA2_VALID_E: std_logic := '0';
signal DATA1_OUT_EMPTY_local: std_logic := '0';

--// CONTROLS -----------------------------------------
signal DATA1_DELAY_CHANGE_TOGGLE_D: std_logic := '0';
signal DATA1_DELAY_CHANGE_TOGGLE_D2: std_logic := '0';
signal DATA1_DELAY_D: unsigned(31 downto 0) := (others => '0');
signal WINDOW_SIZE: unsigned(31 downto 0) := (others => '0');

--// TRANSACTION SCHEDULER ---------------------------------------------------
signal EVENT1: std_logic := '0';
signal EVENT2: std_logic := '0';
signal EVENT3: std_logic := '0';
signal EVENT4: std_logic := '0';
signal EVENT1234: std_logic := '0';
signal EVENT1234_D: std_logic := '0';
signal EVENT5: std_logic := '0';
signal EVENT6: std_logic := '0';
signal EVENT7: std_logic := '0';
signal EVENT8: std_logic := '0';
signal EVENT5678: std_logic := '0';
signal EVENT5678_D: std_logic := '0';
signal EVENT5678_D2: std_logic := '0';
signal EVENT5678_D3: std_logic := '0';
signal EVENT9: std_logic := '0';
signal STATE: integer range 0 to 10 := 0;
signal SDRAM_READY: std_logic := '0';
signal STATE_TIMER: integer range 0 to 100 := 0;
signal WR_ADDR: unsigned(31 downto 0) := (others => '0');
signal WR_ADDR_PLUS8: unsigned(31 downto 0) := (others => '0');
signal WR_ADDR_STABLE: std_logic := '0';
signal WR_ADDR_STABLE_D: std_logic := '0';
signal RD_ADDR0: unsigned(31 downto 0) := (others => '0');
signal RD_ADDR: unsigned(31 downto 0) := (others => '0');
signal RD_ADDR_PLUS8: unsigned(31 downto 0) := (others => '0');

--//------ SDRAM CONTROLLER ------------------------------------
signal app_addr                    : std_logic_vector((APP_NADDRBITS - 1) downto 0) := (others => '0');
signal app_cmd                     : std_logic_vector(2 downto 0) := (others => '0');
signal app_en                      : std_logic := '0';
signal app_wdf_data                : std_logic_vector(511 downto 0) := (others => '0');
signal app_wdf_end                 : std_logic := '0';
signal app_wdf_wren                : std_logic := '0';
signal app_rd_data                 : std_logic_vector(511 downto 0) := (others => '0');
signal app_rd_data_end             : std_logic := '0';
signal app_rd_data_valid           : std_logic := '0';
signal app_rd_data_valid0           : std_logic := '0';
signal app_rdy                     : std_logic := '0';
signal app_rdy0                     : std_logic := '0';
signal app_rdy_df                     : std_logic := '0';
signal app_rdy_d                     : std_logic := '0';
signal app_wdf_rdy                 : std_logic := '0';
signal app_wdf_rdy0                 : std_logic := '0';
signal app_ref_ack                 : std_logic := '0';
signal app_zq_ack                  : std_logic := '0';
signal app_wdf_mask                : std_logic_vector(63 downto 0) := (others => '0');
signal init_calib_complete_i       : std_logic := '0';
signal init_calib_complete_i0       : std_logic := '0';

--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

--//-- CLOCKS --------------------------------
-- monitoring
MONITORING_001: process(CLK)
begin
	if rising_edge(CLK) then
		-- memory ready
		MEMORY_RDY_local <= PLL_LOCKED_IN and (not(MIG_UI_CLK_SYNC_RST)) and init_calib_complete_i;
	end if;
end process;
MEMORY_RDY_OUT <= MEMORY_RDY_local;

BUFG_001: BUFG port map(I => CLK200, O=> CLK200g);
BUFG_002: BUFG port map(I => CLK333p3, O=> CLK333p3g);

-- convert reset pulse to step 
RESET_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			ASYNC_RESETn <= '1';
		end if;
	end if;
end process;

-- make a reset synchronous with CLK333p3g
RESET_GEN_002: process(CLK333p3g)
begin
	if rising_edge(CLK333p3g) then
		SYNC_RESET100n <= ASYNC_RESETn and PLL_LOCKED_IN;
	end if;
end process;

--// DATA1_IN INPUT ELASTIC BUFFERS ---------------------------------------------------
-- A-side = user side
-- B-side = DDR3 memory side

-- reclock CTS
RECLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		DATA1_IN_CTS <= DATA1_IN_CTS_local;
	end if;
end process;

-- write pointer management. Move pointer immediately after writing a cell.
RAMB1_WPTRA_001: process(CLK)
begin
	if rising_edge(CLK) then
		COUNTER1_MOD8 <= COUNTER1_MOD8 + 1;

		-- Move write pointer immediately after writing to the input elastic buffer.
		if(SYNC_RESET = '1') then
			RAMB1_ADDRA <= (others => '0');
		elsif(RAMB1_WEA = '1') then
			RAMB1_ADDRA <= RAMB1_ADDRA + 1;
		end if;
		
		-- update copy of write pointer once every 8 clocks
		if(COUNTER1_MOD8 = 7) then
			RAMB1_ADDRA_D <= RAMB1_ADDRA;
		end if;
		
		if(COUNTER1_MOD8 < 6) then
			-- safe to reclock RAMB1_ADDRA_D with another unrelated clock
			RAMB1_ADDRA_IS_STABLE <= '1';
		else
			RAMB1_ADDRA_IS_STABLE <= '0';
		end if;
		
	end if;
end process;

RAMB1_DIA <= DATA1_IN;
RAMB1_WEA <= DATA1_IN_VALID and SDRAM_READY;

-- cross clock domain from CLK to MIG_UI_CLKg
RAMB_001: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => 32,		
	ADDR_WIDTHA => RAMB_ADDR_WIDTH + 4,	-- 32b words
	DATA_WIDTHB => 512,		 
	ADDR_WIDTHB => RAMB_ADDR_WIDTH
)
PORT MAP(
    CSA => '1',
    CLKA => CLK,
    WEA => RAMB1_WEA,
	 OEA => '0',
    ADDRA => std_logic_vector(RAMB1_ADDRA),  
    DIA => RAMB1_DIA,
    DOA => open,
    CSB => '1',
    CLKB => MIG_UI_CLKg,
    WEB => '0',
	 OEB => '1',
    ADDRB => std_logic_vector(RAMB1_ADDRB0),
    DIB => (others => '0'),
    DOB => RAMB1_DOB
);

-- reclock RAMB1_ADDRA_D with DDR3 SDRAM clock when safe
RAMB1_WPTRA_002: process(MIG_UI_CLKg, RAMB1_ADDRA_IS_STABLE, RAMB1_ADDRA_D)
begin
	if rising_edge(MIG_UI_CLKg) then
		RAMB1_ADDRA_D2 <= RAMB1_ADDRA_D;
		RAMB1_ADDRA_IS_STABLE_D <= RAMB1_ADDRA_IS_STABLE;
	
		if(RAMB1_ADDRA_IS_STABLE_D = '1') then
			RAMB1_ADDRA_D3 <= RAMB1_ADDRA_D2((RAMB_ADDR_WIDTH+3) downto 4);
		end if;
	end if;
end process;

-- compute used space in input elastic buffer
RAMB1_SIZE <= RAMB1_ADDRA_D3 - RAMB1_ADDRB;

-- request data from the input connector if buffer is less than 3/4 full
DATA1_IN_CTS_local <= '1' when (RAMB1_SIZE((RAMB1_SIZE'left) downto (RAMB1_SIZE'left -1)) /= "11") else '0'; -- (synchronous with MIG_UI_CLKg)

-- read pointer management
RAMB1_ADDRB_INC <= RAMB1_ADDRB + 1;
RAMB1_RPTRB_001: process(MIG_UI_CLKg)
begin
	if rising_edge(MIG_UI_CLKg) then
		if(SDRAM_READY = '0') or (MIG_UI_CLK_SYNC_RST = '1') then
			-- no transaction until the SDRAM is initialized and ready.
			RAMB1_ADDRB <= (others => '0'); -- points to the next address to read
		elsif (EVENT1234 = '1') then
			-- one write command in the next clock period
			RAMB1_ADDRB <= RAMB1_ADDRB_INC;
		end if;
	end if;
end process;
RAMB1_ADDRB0 <= RAMB1_ADDRB_INC when (EVENT1234 = '1')  else RAMB1_ADDRB; 

--// DATA1_OUT OUTPUT ELASTIC BUFFERS ---------------------------------------------------
-- A-side = user side
-- B-side = DDR3 memory side

-- write pointer management. Move pointer immediately after writing a cell.
RAMB2_WPTRB_001: process(MIG_UI_CLKg)
begin
	if falling_edge(MIG_UI_CLKg) then	-- FALLING EDGE !!!!!!!!!
		COUNTER2_MOD8 <= COUNTER2_MOD8 + 1;

		if(MIG_UI_CLK_SYNC_RST = '1') then	
			RAMB2_ADDRB <= (others => '0');
		elsif(RAMB2_WEB = '1') then	
			RAMB2_ADDRB <= RAMB2_ADDRB + 1;
		end if;

		-- update copy of write pointer once every 8 clocks
		if(COUNTER2_MOD8 = 7) then
			RAMB2_ADDRB_D <= RAMB2_ADDRB;
		end if;
		
		if(COUNTER2_MOD8 < 6) then
			-- safe to reclock RAMB2_ADDRA_D with another unrelated clock
			RAMB2_ADDRB_IS_STABLE <= '1';
		else
			RAMB2_ADDRB_IS_STABLE <= '0';
		end if;

	end if;
end process;

-- cross clock domain from MIG_UI_CLKg to CLK 
RAMB2_WEB <= app_rd_data_valid;

RAMB2_DIB <= app_rd_data;
-- B is input, A output (to meet the constraint DATA_WIDTHA <= DATA_WIDTHB)
RAMB_002: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => 32,		 
	ADDR_WIDTHA => RAMB_ADDR_WIDTH + 4,	-- 32b words
	DATA_WIDTHB => 512,		
	ADDR_WIDTHB => RAMB_ADDR_WIDTH	-- 512b words
)
PORT MAP(
    CSB => '1',
    CLKB => not MIG_UI_CLKg,
    WEB => RAMB2_WEB,
	 OEB => '0',
    ADDRB => std_logic_vector(RAMB2_ADDRB),  
    DIB => RAMB2_DIB,
    DOB => open,
    CSA => '1',
    CLKA => CLK,
    WEA => '0',
	 OEA => '1',
    ADDRA => std_logic_vector(RAMB2_ADDRA),
    DIA => (others => '0'),
    DOA => RAMB2_DOA
);

DATA1_OUT <= RAMB2_DOA;

-- reclock RAMB2_ADDRB_D with CLK clock when safe
RAMB2_WPTRA_002: process(CLK)
begin
	if rising_edge(CLK) then
		RAMB2_ADDRB_D2 <= RAMB2_ADDRB_D;
		RAMB2_ADDRB_IS_STABLE_D <= RAMB2_ADDRB_IS_STABLE;
	
		if(RAMB2_ADDRB_IS_STABLE_D = '1') then
			RAMB2_ADDRB_D3 <= RAMB2_ADDRB_D2 & "0000";
		end if;
	end if;
end process;

-- compute used space in input elastic buffer
RAMB2_SIZE <= RAMB2_ADDRB_D3 + not(RAMB2_ADDRA);
DATA1_OUT_EMPTY_local <= '1' when (RAMB2_SIZE = 0) else '0';

DATA1_OUT_EMPTY <= DATA1_OUT_EMPTY_local;

-- read pointer management
RAMB2_RPTRB_002: process(CLK)
begin
	if rising_edge(CLK) then
		DATA1_OUT_VALID <= DATA2_VALID_E; 	-- it takes one CLK to read data from the RAMB
		
		if(SYNC_RESET = '1') then
			RAMB2_ADDRA <= (others => '1');
			DATA2_VALID_E <= '0';
		elsif((DATA1_OUT_CTS = '1') or (SDRAM_READY = '0')) and (DATA1_OUT_EMPTY_local = '0') then
			-- read 32b word when available
			-- + flush output elastic buffer when SDRAM is not ready
			RAMB2_ADDRA <= RAMB2_ADDRA + 1;
			DATA2_VALID_E <= '1';
		else
			DATA2_VALID_E <= '0';
		end if;
	end if;
end process;


--// CONTROLS -----------------------------------------
-- reclock with MIG_UI_CLKg
-- Watch out for convoluted logic. Powerup Reset -> MIG_UI_CLKg is reset in DDR3 controller -> missing the control change flag after powerup
RECLOCK_002: process(MIG_UI_CLKg, DATA1_DELAY, DATA1_DELAY_CHANGE_TOGGLE)
begin
	if rising_edge(MIG_UI_CLKg) then
		DATA1_DELAY_CHANGE_TOGGLE_D <= DATA1_DELAY_CHANGE_TOGGLE;
		DATA1_DELAY_CHANGE_TOGGLE_D2 <= DATA1_DELAY_CHANGE_TOGGLE_D;
		WINDOW_SIZE <= unsigned(DATA1_CEILINGADDR) - unsigned(DATA1_STARTADDR);
		
		if(DATA1_DELAY_CHANGE_TOGGLE_D2 /= DATA1_DELAY_CHANGE_TOGGLE_D) or 
		((STATE = 0) and (MEMORY_RDY_local = '1')) then
			-- configuration has changed or DDR3 just completed its initialization
			DATA1_DELAY_D <= unsigned(DATA1_DELAY);	-- to avoid glitches
		end if;
	end if;
end process;


--// TRANSACTION SCHEDULER ---------------------------------------------------
EVENT1 <= '1' when (STATE = 1) and (RAMB1_SIZE >= 4) else '0';
	-- at least 4 512b words in input elastic buffer and SDRAM has been initialized. Initiate 1st word write command
EVENT2 <= '1' when (STATE = 2) and (app_rdy = '1') else '0';	
	-- 1st word write command was accepted. Initiate 2nd word write command
EVENT3 <= '1' when (STATE = 3) and (app_rdy = '1') else '0';	
	-- 2nd word write command was accepted. Initiate 3rd word write command
EVENT4 <= '1' when (STATE = 4) and (app_rdy = '1') else '0';	
	-- 3rd word write command was accepted. Initiate 4th word write command
EVENT1234 <= EVENT1 or EVENT2 or EVENT3 or EVENT4;

EVENT5 <= '1' when (STATE = 5) and (app_rdy = '1') else '0';	
	-- 4th word write command was accepted. Initiate 1st word read command
EVENT6 <= '1' when (STATE = 6) and (app_rdy = '1') else '0';	
	-- 1st word read command was accepted. Initiate 2nd word read command
EVENT7 <= '1' when (STATE = 7) and (app_rdy = '1') else '0';	
	-- 2nd word read command was accepted. Initiate 3rd word read command
EVENT8 <= '1' when (STATE = 8) and (app_rdy = '1') else '0';	
	-- 3rd word read command was accepted. Initiate 4th word read command
EVENT9 <= '1' when (STATE = 9) and (app_rdy = '1') else '0';	
	-- 4th word read command was accepted. back to idle
EVENT5678 <= EVENT5 or EVENT6 or EVENT7 or EVENT8;


STATE_001: process(MIG_UI_CLKg)
begin
	if rising_edge(MIG_UI_CLKg) then
		EVENT1234_D <= EVENT1234;
		EVENT5678_D <= EVENT5678;
		EVENT5678_D2 <= EVENT5678_D;
		EVENT5678_D3 <= EVENT5678_D2;
		
		if(MIG_UI_CLK_SYNC_RST = '1') then
			-- no transaction until MIG is ready
			STATE <= 0;
		elsif(STATE = 0) then
			if (MEMORY_RDY_local = '1') then
				-- calibration is complete. We can now do read/write transactions
				STATE <= 1;
			end if;
		elsif(EVENT1 = '1') then
			-- input buffer has at least 4 512b words of data. Start a read/write cycle, write first word
			STATE <= 2;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT2 = '1') then
			-- 1st word write command was accepted. Initiate 2nd word write command
			STATE <= 3;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT3 = '1') then
			-- 2nd word write command was accepted. Initiate 3rd word write command
			STATE <= 4;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT4 = '1') then
			-- 3rd word write command was accepted. Initiate 4th word write command
			STATE <= 5;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT5 = '1') then
			-- 4th word write command was accepted. Initiate 1st word read command
			STATE <= 6;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT6 = '1') then
			-- 1st word read command was accepted. Initiate 2nd word read command
			STATE <= 7;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT7 = '1') then
			-- 2nd word read command was accepted. Initiate 3rd word read command
			STATE <= 8;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT8 = '1') then
			-- 3rd word read command was accepted. Initiate 4th word read command
			STATE <= 9;
			STATE_TIMER <= 10;	-- timeout in case no response from SDRAM
		elsif(EVENT9 = '1') then
			-- 4th word read command was accepted. back to idle
			STATE <= 1;
			STATE_TIMER <= 0;	-- clear timer
		elsif(STATE_TIMER = 0) then
			-- timeout. Something wrong happened. go back to idle
			STATE <= 1;
		else
			STATE_TIMER <= STATE_TIMER - 1;
		end if;	
	end if;
end process;
SDRAM_READY <= '0' when (STATE = 0) else '1';

-- manage SDRAM write address
WR_ADDR_PLUS8 <= WR_ADDR + 8;	-- because BL8
SDRAM_WR_ADDR_001: process(MIG_UI_CLKg)
begin
	if rising_edge(MIG_UI_CLKg) then
		if(MIG_UI_CLK_SYNC_RST = '1') then
			-- initialize write address
			WR_ADDR <= unsigned(DATA1_STARTADDR);
		elsif(EVENT1234 = '1') then
			-- about to send write command to MIG
			-- prepare next address
			if (WR_ADDR_PLUS8 >= unsigned(DATA1_CEILINGADDR(WR_ADDR'left downto 0))) then
				-- reached the ceiling address. Rewind to start address.
				WR_ADDR <= unsigned(DATA1_STARTADDR);
			else
				-- auto-increment
				WR_ADDR <= WR_ADDR_PLUS8;
			end if;
		end if;
	end if;
end process;
-- detect no-change periods when we can reclock WR_ADDR with CLK for monitoring purposes
WR_ADDR_STABLE <= '0' when (EVENT1234 = '1') or (EVENT1234_D = '1') else '1';
-- reclock monitoring with CLK
WR_ADDR_003: process(CLK)
begin
	if rising_edge(CLK) then
		WR_ADDR_STABLE_D <= WR_ADDR_STABLE;
		
		if (WR_ADDR_STABLE = '1') then 
			DATA1_IN_ADDR <= std_logic_vector(WR_ADDR);	-- for monitoring purposes
		end if;
	end if;
end process;

-- manage SDRAM read addresses
-- RD_ADDR = NEXT ADDRESS to read from
RD_ADDR0 <= WR_ADDR - DATA1_DELAY_D;	
RD_ADDR_PLUS8 <= RD_ADDR + 8;

SDRAM_RD_ADDR_001: process(MIG_UI_CLKg)
begin
	if rising_edge(MIG_UI_CLKg) then
		if(EVENT1 = '1') then
			-- prepare first read address in burst
			if (RD_ADDR0(31) = '1') or (RD_ADDR0 < unsigned(DATA1_STARTADDR)) then
				-- out of range.
				RD_ADDR <= RD_ADDR0 + WINDOW_SIZE;
			else
				RD_ADDR <= RD_ADDR0;
			end if;
		elsif(EVENT5678 = '1') then
			-- about to send read command to MIG
			-- prepare next address
			if (RD_ADDR_PLUS8 >= unsigned(DATA1_CEILINGADDR(RD_ADDR'left downto 0))) then
				-- reached the ceiling address. Rewind to start address.
				RD_ADDR <= unsigned(DATA1_STARTADDR);
			else
				-- auto-increment
				RD_ADDR <= RD_ADDR_PLUS8;
			end if;
		end if;
	end if;
end process;

-- reclock monitoring with CLK (when RD_ADDR is stable)
RD_ADDR_003: process(CLK)
begin
	if rising_edge(CLK) then
		if (WR_ADDR_STABLE_D = '1') then 
			DATA1_OUT_ADDR <= std_logic_vector(RD_ADDR);	-- for monitoring purposes
		end if;
	end if;
end process;

-- MIG commands
-- Mirrors the STATE machine generation
-- conditions order is important
MIG_COMMANDS_001: process(MIG_UI_CLKg)
begin
	if rising_edge(MIG_UI_CLKg) then
		if(MIG_UI_CLK_SYNC_RST = '1') or (STATE = 0)then
			-- no transaction until MIG is ready
			app_en <= '0';
		elsif(EVENT1234 = '1') then
			-- input buffer has data. write 4 words
			-- send write command to MIG. await confirmation (app_rdy = '1')
			app_en <= '1';
			app_addr <= std_logic_vector(WR_ADDR(app_addr'left downto 0));
			app_cmd <= "000";	-- write
		elsif(EVENT5678 = '1')   then
			-- send read command to MIG. await confirmation (app_rdy = '1')
			app_en <= '1';
			app_addr <= std_logic_vector(RD_ADDR(app_addr'left downto 0));
			app_cmd <= "001";	-- read
		elsif  (app_rdy = '1') then	
			-- read/write command was accepted 
			app_en <= '0';
		elsif(STATE_TIMER = 0) then
			-- timeout. Something wrong happened. go back to idle
			app_en <= '0';
		end if;
	end if;
end process;

-- MIG write data
MIG_WR_DATA_001: process(MIG_UI_CLKg)
begin
	if rising_edge(MIG_UI_CLKg) then
		if(MIG_UI_CLK_SYNC_RST = '1') or (STATE = 0)then
			-- no transaction until MIG is ready
			app_wdf_wren <= '0';
		elsif(EVENT1234 = '1') then
			-- input buffer has data. write 4 words
			-- send write data to MIG
			app_wdf_wren <= '1';
			app_wdf_data <= RAMB1_DOB;
		elsif(app_wdf_rdy = '1') then
			-- word write data was accepted
			app_wdf_wren <= '0';
		end if;
	end if;
end process;
app_wdf_end <= app_wdf_wren;	-- 1:4 clock, no difference between _end and _wren


--//------ SDRAM CONTROLLER ------------------------------------
-- configured mig_7series_0_mig.vhd as: 
-- (default) auto refresh

app_wdf_mask <= (others => '0');
	-- no mask

	-- 1GB,4GB,8GB SODIMM case
u_mig_7series_0 : mig_7series_0
port map (
	-- Memory interface ports
	ddr3_addr                      => ddr3_addr,
	ddr3_ba                        => ddr3_ba,
	ddr3_cas_n                     => ddr3_cas_n,
	ddr3_ck_n                      => ddr3_ck_n,
	ddr3_ck_p                      => ddr3_ck_p,
	ddr3_cke                       => ddr3_cke,
	ddr3_ras_n                     => ddr3_ras_n,
	ddr3_reset_n                   => ddr3_reset_n,
	ddr3_we_n                      => ddr3_we_n,
	ddr3_dq                        => ddr3_dq,
	ddr3_dqs_n                     => ddr3_dqs_n,
	ddr3_dqs_p                     => ddr3_dqs_p,
	ddr3_cs_n                      => ddr3_cs_n,
	ddr3_dm                        => ddr3_dm,
	ddr3_odt                       => ddr3_odt,
	-- Application interface ports
	app_addr                       => app_addr,
	app_cmd                        => app_cmd,
	app_en                         => app_en,
	app_wdf_data                   => app_wdf_data,
	app_wdf_end                    => app_wdf_end,
	app_wdf_wren                   => app_wdf_wren,
	app_rd_data                    => app_rd_data,
	app_rd_data_end                => app_rd_data_end,	-- unused
	app_rd_data_valid              => app_rd_data_valid0,
	app_rdy                        => app_rdy0,
	app_wdf_rdy                    => app_wdf_rdy0,
	app_sr_req                     => '0',
	app_ref_req                    => '0',	-- auto refresh
	app_zq_req                     => '0',
	app_sr_active                  => open,
	app_ref_ack                    => open,
	app_zq_ack                     => app_zq_ack,
	ui_clk                         => MIG_UI_CLKg,	-- global
	ui_clk_sync_rst                => MIG_UI_CLK_SYNC_RST,
	app_wdf_mask                   => app_wdf_mask,	
	init_calib_complete            => init_calib_complete_i0,
	-- System Clock Ports
	sys_clk_i                       => CLK333p3g,
	-- Reference Clock Ports
	clk_ref_i                      => CLK200g,	-- reference clock. Must be 200 MHz
	sys_rst                        => SYNC_RESET100n	-- active low
);

---- NEW  12/27/16
---- reclock outputs at falling edge of MIG_UI_CLKg
--RECLOCK_003: process(MIG_UI_CLKg)
--begin
--	if falling_edge(MIG_UI_CLKg) then
--		app_rdy_df <= app_rdy0;
--	end if;
--end process;

-- because calibration takes so long, bypass mig_7series_0()
BYPASS_001: if(SIMULATION = '1') generate
	init_calib_complete_i <= '1';
	app_wdf_rdy <= '1';	-- max speed
	app_rdy <= '1';	-- max speed
	app_rd_data_valid <= EVENT5678_D3;
end generate;
NOBYPASS_001: if(SIMULATION = '0') generate
	app_wdf_rdy <= app_wdf_rdy0;
	init_calib_complete_i <= init_calib_complete_i0;
	app_rdy <= app_rdy0;
	app_rd_data_valid <= app_rd_data_valid0;
end generate;

--// TEST POINTS ---------
TP(1) <= WR_ADDR(3);
TP(2) <= WR_ADDR(23);
TP(3) <= EVENT1234; 
TP(4) <= EVENT5678;
TP(5) <= app_rd_data_valid;
TP(6) <= app_rdy;
TP(7) <= RD_ADDR(3); 



end Behavioral;

