----------------------------------------------
-- MSS copyright 1999-2012
-- Filename: DDR2_SDRAM_CONTROLLER.VHD 
-- Version: Rev 5
-- Last modified: 1/4/2013
-- Authors: 
--		Poornima Sarvadevabhatla / MSS
-- 	Adam Kwiatkowski / MSS
--		Alain Zarembowitch / MSS
-- Inheritance: DDR2_SDRAM_CONTROLLER.VHD, 6-04-07 (COM-1500)
-- 
-- Description: 
-- Performs ONE burst transation (read or write) as ordered, automatic SDRAM refresh, 
-- power-up initialization and low-power self refresh.
-- Several configuration options must selected prior to synthesis (see the Generic section below)
-- Several other configurations are fixed in the code:
--   sequential burst type, burst length BL=4, normal mode, ODT enable, 75 Ohm Rtt, DQS# enable
--   RDQS disable, optional duty cycle corrector disabled, 
-- The controller keeps track of the open banks, and open/close them as needed. 
-- Up to 16 banks (8 banks * 2 ranks) can be opened at any given time.
--
-- Rev 2 2/25/12 AZ  
-- Added support for V5 platform.
--
-- Rev 3 11/6/12 AZ
-- Added SIMULATION parameter for faster simulation
--
-- Rev 4 12/21/12 AZ
-- limited 	SDRAM_DQ_DIR and SDRAM_DQS_DIR to simulation. (otherwise Mapping error)
-- Moved clock generation to this level (otherwise P&R has problem with CLK_90)
--
-- Rev 5 1/4/2013 AZ
-- trying a different clock strategy for a more reliable outcome: Use 480 MHz PLL, then create 4 phases by dividing by 4.
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;  
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
Library UNISIM;
use UNISIM.vcomponents.all;

entity DDR2_SDRAM_CONTROLLER is 
	Generic (
	PLATFORM: integer := 6;
		-- 5 = Xilinx Virtex-5
		-- 6 = Xilinx Spartan-6
	NSDRAM_BYTES: in integer := 8;
		-- number of data bytes on the SDRAM data bus 
		-- The same data width for both user data interface and DDR2 interface
		-- 1 = 8-bit word (IC)
		-- 2 = 16-bit word (IC, e.g. COM-1600)
		-- 4 = 32-bit word (ICs or partial DIMM/SODIMM)
		-- 8 = 64-bit word (DIMM/SODIMM, e.g. COM-1500)
	NCLKS: in integer := 1;
		-- number of differential clock pairs: 1 for IC, 2 for SODIMM
	ADDRESS_MAP: in std_logic_vector(4 downto 0) := "10000";
		-- the logical to physical address mapping can vary depending on the hardware.
		-- See DDR2 SDRAM hardware specifications.
		-- the most common mappings are described below:
		-- 00000: 1Gb x8 IC, column A[9:0], 8 banks BA[2:0], row A[13:0], 1 rank CS# 
		-- 00001: 1Gb x16 IC, column A[9:0], 8 banks BA[2:0], row A[12:0], 1 rank CS#
		-- 00010: 2Gb x8 IC, column A[9:0], 8 banks BA[2:0], row A[14:0], 1 rank CS#
		-- 00011: 2Gb x16 IC, column A[9:0], 8 banks BA[2:0], row A[13:0], 1 rank CS#
		-- 0xxxx: other ICs, reserved for future use
		-- 10000: 1GB DIMM/SODIMM, column A[9:0], 8 banks BA[2:0], row A[12:0], 2 ranks S0#/S1#
		-- 10001: 2GB DIMM/SODIMM, column A[9:0], 8 banks BA[2:0], row A[13:0], 2 ranks S0#/S1#
		-- 10010: 4GB DIMM/SODIMM, column A[9:0], 8 banks BA[2:0], row A[14:0], 2 ranks S0#/S1#
		-- 1xxxx: other DIMM/SODIMMs, reserved for future use
	CLOCK_CYCLES: in integer := 3;
		-- (CAS latency) -  (RAS-CAS-Delay time) -  (RAS precharge time)
		-- CL - tRCD - tRP
		-- Use CL = 3 for clock speed up to 200 MHz (400 MT/s).
		-- See DDR2 SDRAM hardware specifications.
		-- 3 =  3-3-3  DDR2-400
		-- 4 =  4-4-4  DDR2-533
		-- 5 =  5-5-5  DDR2-667
		-- 6 =  6-6-6  DDR2-800
	DDR2_CLK_FREQ: in integer := 1200;
		-- actual DDR2 clock frequency in 100KHz units (used to calculate timers within)
		-- For example: 1250 means a 125 MHz clock and a maximum throughput of 250 Mtransactions/s.
		-- The specified value can be significantly slower than the SDRAM DDR2 speed grade 
		-- (since the FPGA may be the bottleneck)
	RTDELAY_COARSE: in integer := 2
		-- goal: calibrate out signal round-trip delay. 
		-- Coarse settings: integer multiple of the 1/2 CLK period rounded up. 
		-- Fine calibration (within 1/2 CLK period) is automatic.
		-- Example Spartan-6 125 MHz: 
		--    Output delay for SDRAM_CKx: 4ns
		--    Input delay for SDRAM_DQSy: 3ns
		--    RTDELAY_COARSE = 7ns*2*125MHz =  1.75 rounded up to 2.

	);

	Port (
	--// CLOCKS, RESET
	CLK_DDR2_REF: in std_logic; 
		-- DDR2 SDRAM reference clock. 125 MHz min (as per DDR2 standard) but very likely works at 120 MHz and even slightly lower.
	ASYNC_RESET: in std_logic;
		-- asynchronous reset, active high
		-- Triggers the DCM, SDRAM configuration and calibration phases. Check SDRAM_READY afterwards to verify
		-- when the DDR2 SDRAM is available for read/write transactions.
		-- Use of ASYNC_RESET is not mandatory.
	CLK_DDR2G_OUT: out std_logic;
		-- global clock. generated internally by DCM.
	
	--// Control signals
	 -- Addresses are in bytes
	START_ADDR: in std_logic_vector(31 downto 0);
		-- start address in the memory (therefore expressed as a multiple of the NSDRAM_BYTES, not bytes)
	TRANSACTION_REQ: in std_logic_vector(1 downto 0);
		-- transaction type
		-- 00 = none
		-- 01 = burst read
		-- 10 = burst write
		-- others are undefined
	TRANSACTION_REQ_NO: in std_logic_vector(3 downto 0);
		-- continuously increasing number
		-- helps the user matching requests and data 
		-- (for example the read data comes much later after the read request)
		-- The controller identifies a new transaction request when the transaction number is incremented.
	TRANSACTION_REQ_ACK: out std_logic;
		-- acknowledgement (one CLK PULSE) that this transaction was accepted and processing has just started.
		-- The user can now send the next request (even while the current transaction is in progress).
	
	--// User Interface
	DATA_IN: in std_logic_vector((16*NSDRAM_BYTES - 1) downto 0);
		-- Two input data words (actual width specified by NSDRAM_BYTES). Reason: speed (DDR2 memory needs two words per clock!)
		-- When 16-bit DDR2 data width: use bits 15:0 for the first word, 31:16 for the second word.
		-- When 64-bit DDR2 data width: use bits 63:0 for the first word, 127:64 for the second word.
		-- IMPORTANT: the data must be supplied at exactly the right time, namely
		-- 2 CLKs after the DATA_IN_CTS pulse (independently of the CAS latency)
	DATA_IN_CTS: out std_logic;
		-- Clear to send, requests write data for just-in-time delivery.
		-- IMPORTANT: data must be provided exactly at the right time... 2 CLKs after the DATA_IN_CTS pulse.
		-- This interface is perfect for interfacing with an external dual-port block RAM 
		-- (since it takes 2 CLKs to read data from a RAMB).
	
	DATA_OUT: out std_logic_vector((16*NSDRAM_BYTES - 1) downto 0);
		-- Two output data words. (actual width specified by NSDRAM_BYTES). Reason: speed (DDR2 memory needs two words per clock!)
		-- When 16-bit DDR2 data width: use bits 15:0 for the first word, 31:16 for the second word.
		-- When 64-bit DDR2 data width: use bits 63:0 for the first word, 127:64 for the second word.
	DATA_OUT_EN: out std_logic;
		-- enable
		
	SELF_REFRESH_REQ: in std_logic;
		-- low-power mode can be activated by the user when no read/write transaction is scheduled.
		-- low-power is achieved by entering the self-refresh mode. 
		-- The SDRAM contents is retained.
		-- The DLL is automatically disabled upon entering this mode and restarted upon exiting.
		-- It takes 210(TBC) CLKs to resume normal DLL/read/write/refresh operation after the user disables SELF_REFRESH_REQ.
	
	--// DDR2 SDRAM Interface (Direct connection to SDRAM I/O pins)
	SDRAM_DQ: inout std_logic_vector((8*NSDRAM_BYTES - 1) downto 0);
		-- Bi-directional data lines. DDR
	SDRAM_DQS_P: inout std_logic_vector((NSDRAM_BYTES - 1) downto 0);
	SDRAM_DQS_N: inout std_logic_vector((NSDRAM_BYTES - 1) downto 0);
		-- Bi-directional data strobe lines (differential)

	SDRAM_ODT: out std_logic_vector(1 downto 0) := "00";
		--	On-Die Termination
	
	-- clocks
	SDRAM_CK_P: out std_logic_vector((NCLKS - 1) downto 0);
	SDRAM_CK_N: out std_logic_vector((NCLKS - 1) downto 0);
		-- clocks 0/1 (differential)
	SDRAM_CKE: out std_logic_vector((NCLKS - 1) downto 0);
		-- clock 0/1 enable
	SDRAM_CASN: out std_logic := '1';
	SDRAM_RASN: out std_logic := '1';
	SDRAM_WEN: out std_logic := '1';
	SDRAM_SN: out std_logic_vector(1 downto 0);
		--	Chip Select # (rank 0/1)
	
	SDRAM_DM: out std_logic_vector((NSDRAM_BYTES - 1) downto 0);
		-- Input Data Mask
	
	-- Address signals
	SDRAM_BA: out std_logic_vector(2 downto 0);
	SDRAM_A: out std_logic_vector(15 downto 0); 
		-- Address lines. 
	

   --// Monitoring + Test points 
	SDRAM_READY: out std_logic;
		-- '1' when the configuration and calibration phases are complete. 
		-- The SDRAM can now be used for read/write transactions.
		-- If SDRAM_READY stays low, the main cause is likely RTDELAY_COARSE out of range.
	SDRAM_DQ_DIR: out std_logic;
	SDRAM_DQS_DIR: out std_logic;
		-- direction of the bi-directional bus SDRAM_DQ and SDRAM_DQS
		-- primarily for simulation use: when simulating a delay between FPGA and SDRAM
		-- high=input, low=output
		
   SDRAM_TP: out std_logic_vector(10 downto 1)
		
	);
end entity;

architecture behavior of DDR2_SDRAM_CONTROLLER is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
function maximum(a,b: integer) return integer is
begin
	if(a > b) then return a;
	else return b;
	end if;
end function maximum;

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- Suffix _X indicates an extended precision version of the net with the same name
-- Suffix _R indicates a reduced precision version of the net with the same name
-- Suffix _P indicates positive differential signal
-- Suffix _N indicates negative differential signal
-- Suffix _LOCAL indicates an exact version of the (output signal) net with the same name
-- Suffix _DEC for decrement by one
-- Suffix _INC for increment by one

--// CLOCKS / RESET -------------------------------------------------------------------------------------------
signal CLKFBOUT: std_logic := '0';
signal CLK480: std_logic := '0';
signal CNTR480: std_logic_vector(1 downto 0) := "00";
signal CLK_DDR2: std_logic := '0';
signal CLK_DDR2_90: std_logic := '0';
signal CLK_DDR2_180: std_logic := '0';
signal CLK_DDR2_270: std_logic := '0';
signal CLK: std_logic := '0';
signal CLK_90: std_logic := '0';
signal CLK_180: std_logic := '0';
signal CLK_270: std_logic := '0';
signal CLK_90_OUT: std_logic_vector((NSDRAM_BYTES - 1) downto 0) := (others => '0');
signal CLK_90_DQS_OUT: std_logic_vector((NSDRAM_BYTES - 1) downto 0) := (others => '0');
signal DCM_LOCKED: std_logic := '0';
signal SYNC_RESET: std_logic := '0';
signal ASYNC_RESET_D: std_logic := '0'; 
signal ASYNC_RESET_D2: std_logic := '0'; 

--// TRANSACTION REQUEST MANAGER ---------------------------------------------------------------------------------
signal LAST_TRANSACTION_REQ_NO: std_logic_vector(3 downto 0) := "0000";
signal NEW_TRANSACTION_REQ: std_logic := '0';
signal NEW_TRANSACTION_REQ1: std_logic := '0';
signal NEW_TRANSACTION_REQ2: std_logic := '0';
signal BANK_IS_OPEN: std_logic := '0';
signal ACTIVE_ROW: std_logic_vector(14 downto 0) := "000000000000000";
signal TRANSACTION_REQ0: std_logic_vector(1 downto 0) := (others => '0');

signal TRANSACTION_REQ_D: std_logic_vector(1 downto 0) := (others => '0');
signal TRANSACTION_REQ_NO_D: std_logic_vector(3 downto 0) := (others => '0');
signal BANK_IS_OPEN_D: std_logic := '0';
signal ROW_IS_ACTIVE_D: std_logic := '0';

signal TRANSACTION_REQ_D2: std_logic_vector(1 downto 0) := (others => '0');

signal TRANSACTION_REQ_ACK_LOCAL: std_logic := '0';



--// ADDRESS REMAP ---------------------------------------------------------------------------------
signal START_ADDR0: std_logic_vector(31 downto 0) := (others => '0');
signal COL: std_logic_vector(14 downto 0) := (others => '0');
signal BANK: std_logic_vector(2 downto 0) := (others => '0');
signal ROW: std_logic_vector(14 downto 0) := (others => '0');
signal RANK: std_logic := '0';
signal COL_D: std_logic_vector(14 downto 0) := (others => '0');
signal BANK_D: std_logic_vector(2 downto 0) := (others => '0');
signal ROW_D: std_logic_vector(14 downto 0) := (others => '0');
signal RANK_D: std_logic := '0';
signal COL_D2: std_logic_vector(14 downto 0) := (others => '0');
signal BANK_D2: std_logic_vector(2 downto 0) := (others => '0');
signal ROW_D2: std_logic_vector(14 downto 0) := (others => '0');
signal RANK_D2: std_logic := '0';

--// EVENTS ---------------------------------------------------------------------------------
signal EVENT1: std_logic := '0';

--// STATE MACHINE ---------------------------------------------------------------------------------
signal STATE: std_logic_vector(4 downto 0) := (others => '0');  

--// TIMERS ---------------------------------------------------------------------------------
-- DDR2_CLK_FREQ is expressed in 100KHz units
constant INIT_COUNT: integer := 210000*DDR2_CLK_FREQ/10000;  -- 200us + 10us margin
constant INIT_COUNT_SIMUL: integer := 2000*DDR2_CLK_FREQ/10000;  -- (shorten during simulation: 2us)
constant INIT2_COUNT: integer := 500*DDR2_CLK_FREQ/10000;  -- 400ns + 100ns margin
constant TRP_DEC: integer := (CLOCK_CYCLES - 1);   -- precharge time
constant TRCD_DEC: integer := (CLOCK_CYCLES - 1);   -- ras-cas delay
constant TMRD_DEC: integer := 1;  -- tmrd = 2 CLK
--constant TRFC: integer := 75*DDR2_CLK_FREQ/10000; -- refresh cycle time. Depends on device density. 256Mb
--constant TRFC: integer := 105*DDR2_CLK_FREQ/10000; -- refresh cycle time. Depends on device density. 512Mb
--constant TRFC: integer := 128*DDR2_CLK_FREQ/10000; -- refresh cycle time. Depends on device density. 1Gb
--constant TRFC: integer := 195*DDR2_CLK_FREQ/10000; -- refresh cycle time. Depends on device density. 2Gb
constant TRFC: integer := 328*DDR2_CLK_FREQ/10000; -- refresh cycle time. Depends on device density. 4Gb
constant TOIT: integer := 12*DDR2_CLK_FREQ/10000; -- OCD drive mode output delay
constant TREFI: integer := 7800*DDR2_CLK_FREQ/10000 - 1;   -- average periodic refresh interval 0-85C
--constant TREFI: integer := 3900*DDR2_CLK_FREQ/10000;   -- average periodic refresh interval 85C-95C
constant TAOFD_DEC: integer := 2;   -- ODT turn off time tAOFD = 2.5 CLKs rounded up 
constant TCKE_DEC: integer := 2;   -- CKE minimum pulse width (high and low pulse width) tCKE = 3 CLKs 
constant TXSRD_DEC: integer := 199;   -- exit self refresh to a read command. tXSRD = 200 CLKs 
constant TWR: integer := maximum(2, 15*DDR2_CLK_FREQ/10000);  -- write recovery  15ns, minimum is 2. 
constant TWTR: integer := 2;  -- 2 CLKs independent of the frequency  (write to read)
constant TRTDELAYCHECK: integer := 10;	-- periodic read for round-trip delay check, when enabled

signal TIMER1: std_logic_vector(15 downto 0) := x"0000";	
signal TIMER2: std_logic_vector(15 downto 0) := x"0000";	
signal TIMER3: std_logic_vector(15 downto 0) := x"0000";	
signal TIMER1_EXPIRED: std_logic := '1';
signal TIMER2_EXPIRED: std_logic := '1';
signal TIMER3_EXPIRED: std_logic := '1';



--// COMMANDS  ---------------------------------------------------------------------------------
type COMMAND_TYPE is (NOP, PRECHARGE, MODE_REGISTER_SET, ACTIVATE, WRITE, READ, REFRESH);
signal COMMAND: COMMAND_TYPE := NOP;


--// DDR2 CONTROLS ---------------------------------------------------------------------------------
signal SDRAM_A_LOCAL: std_logic_vector(15 downto 0) := (others => '0'); 
signal SDRAM_BA_LOCAL: std_logic_vector(2 downto 0) := (others => '0'); 
signal SDRAM_SN_LOCAL: std_logic_vector(1 downto 0) := (others => '0');

--// BANKS MANAGEMENT ---------------------------------------------------------------------------------
signal OPENBANK_S0B1STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S0B2STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S0B3STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S0B4STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S0B5STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S0B6STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S0B7STATUS: std_logic_vector(15 downto 0) := (others => '0'); 
signal OPENBANK_S0B8STATUS: std_logic_vector(15 downto 0) := (others => '0');

signal OPENBANK_S1B1STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B2STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B3STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B4STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B5STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B6STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B7STATUS: std_logic_vector(15 downto 0) := (others => '0');
signal OPENBANK_S1B8STATUS: std_logic_vector(15 downto 0) := (others => '0');
	-- keep track of banks status:
	-- bit 14:0  active row
	-- bit 15: open (1) or closed (0)

--// WRITE TRANSACTION -----------------------------------------------------
signal WRITE_EVENT: std_logic := '0';
signal WRITE_EVENT_D: std_logic := '0';
signal WRITE_EVENT_D2: std_logic := '0';
signal WRITE_EVENT_D3: std_logic := '0';
signal WRITE_EVENT_D4: std_logic := '0';
signal DATA_IN_CTS_LOCAL: std_logic := '0';
signal DATA_IN_CTS_D: std_logic := '0';
signal DATA_IN_CTS_D2: std_logic := '0';
signal DATA_IN_CTS_D3: std_logic := '0';
signal SDRAM_DQSZ: std_logic := '0';
signal SDRAM_DQ_CE: std_logic := '0';
signal DATA_IN_CTS_D1P75_N: std_logic := '0';
signal DATA_IN_CTS_D2P25_N: std_logic := '0';
signal DATA_IN_CTS_D2P75_N: std_logic := '0';
signal SDRAM_DQ_OUT: std_logic_vector((8*NSDRAM_BYTES -1) downto 0) := (others => '0');
signal SDRAM_DQZ_OUT: std_logic_vector((8*NSDRAM_BYTES -1) downto 0) := (others => '0');
signal SDRAM_DQ_IN: std_logic_vector((8*NSDRAM_BYTES -1) downto 0) := (others => '0');
signal SDRAM_DQ_IN_D: std_logic_vector((8*NSDRAM_BYTES -1) downto 0) := (others => '0');
signal SDRAM_DQZ: std_logic := '0';
signal SDRAM_DQSZ_OUT: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal SDRAM_DQS_IN: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal SDRAM_DQS_IN_D: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');

--// READ TRANSACTION -----------------------------------------------------
signal READ_EVENT: std_logic := '0';
signal READ_EVENT_D: std_logic := '0';
signal READ_EVENT_LONG_D: std_logic := '0';
signal READ_EVENT_LONG_D2: std_logic := '0';
signal READ_EVENT_LONG_D3: std_logic := '0';
signal READ_EVENT_LONG_D4: std_logic := '0';
signal READ_EVENT_LONG_D5: std_logic := '0';
signal READ_EVENT_LONG_D6: std_logic := '0';
signal READ_EVENT_LONG_D7: std_logic := '0';
signal READ_EVENT_LONG_D8: std_logic := '0';
signal READ_EVENT_LONG_D9: std_logic := '0';
signal READ_EVENT_LONG_DF: std_logic := '0';
signal READ_EVENT_LONG_DF2: std_logic := '0';
signal READ_EVENT_LONG_DF3: std_logic := '0';
signal READ_EVENT_LONG_DF4: std_logic := '0';
signal READ_EVENT_LONG_DF5: std_logic := '0';
signal READ_EVENT_LONG_DF6: std_logic := '0';
signal READ_EVENT_LONG_DF7: std_logic := '0';
signal READ_EVENT_LONG_DF8: std_logic := '0';
signal READ_EVENT_LONG_DF9: std_logic := '0';
signal READ_WINDOW: std_logic := '0';
signal READ_WINDOW_D: std_logic := '0';
signal READ_WINDOW_D2: std_logic := '0';
signal READ_DELAY: integer;
signal DATA_OUT0: std_logic_vector((16*NSDRAM_BYTES - 1) downto 0) := (others => '0');
signal DATA_OUT0_D: std_logic_vector((16*NSDRAM_BYTES - 1) downto 0) := (others => '0');


--//------- CALIBRATION ----------------------------------------------
signal S_90_0: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal S_90_1: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal S_90_2: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal S_270_0: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal S_270_1: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal S_270_2: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal TOA_LATE: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal TOA_SAMPLE_CLK: std_logic := '0';
signal IODELAY2_CE: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal IODELAY2_INC: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal IODELAY2_DQ_BUSY: std_logic_vector((8*NSDRAM_BYTES -1) downto 0) := (others => '0');
signal IODELAY2_DQS_BUSY: std_logic_vector((NSDRAM_BYTES -1) downto 0) := (others => '0');
signal IODELAY2_CAL: std_logic := '0';
signal CAL_STATE: std_logic_vector(2 downto 0) := (others => '0');
signal NCALBURSTREADS: std_logic_vector(8 downto 0) := (others => '0');  -- 255 maximum increments
signal SDRAM_READY_LOCAL: std_logic := '0';

constant SIMULATION: std_logic := '0';	-- 1 during simulation, 0 for release
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin 


--// generates 0/90/180/270 deg offset clock replica
-- Create 4 clock phases (90 deg apart) for the DDR2 SDRAM clock
-- Note: place and route is happier when the global buffers are instantiated here instead of a lower level components.
DCM_SP_GEN_S6A: if (PLATFORM = 6) generate
   PLL_BASE_inst : PLL_BASE
   generic map (
      BANDWIDTH => "OPTIMIZED",             -- "HIGH", "LOW" or "OPTIMIZED" 
      CLKFBOUT_MULT => 4,                   -- Multiply value for all CLKOUT clock outputs (1-64)
      CLKFBOUT_PHASE => 0.0,                -- Phase offset in degrees of the clock feedback output
                                            -- (0.0-360.0).
      CLKIN_PERIOD => 8.333,                  -- Input clock period in ns to ps resolution (i.e. 33.333 is 30
                                            -- MHz).
      -- CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: Divide amount for CLKOUT# clock output (1-128)
      CLKOUT0_DIVIDE => 1,
      CLKOUT1_DIVIDE => 1,
      CLKOUT2_DIVIDE => 1,
      CLKOUT3_DIVIDE => 1,
      CLKOUT4_DIVIDE => 1,
      CLKOUT5_DIVIDE => 1,
      -- CLKOUT0_DUTY_CYCLE - CLKOUT5_DUTY_CYCLE: Duty cycle for CLKOUT# clock output (0.01-0.99).
      CLKOUT0_DUTY_CYCLE => 0.5,
      CLKOUT1_DUTY_CYCLE => 0.5,
      CLKOUT2_DUTY_CYCLE => 0.5,
      CLKOUT3_DUTY_CYCLE => 0.5,
      CLKOUT4_DUTY_CYCLE => 0.5,
      CLKOUT5_DUTY_CYCLE => 0.5,
      -- CLKOUT0_PHASE - CLKOUT5_PHASE: Output phase relationship for CLKOUT# clock output (-360.0-360.0).
      CLKOUT0_PHASE => 0.0,
      CLKOUT1_PHASE => 0.0,
      CLKOUT2_PHASE => 0.0,
      CLKOUT3_PHASE => 0.0,
      CLKOUT4_PHASE => 0.0,
      CLKOUT5_PHASE => 0.0,
      CLK_FEEDBACK => "CLKFBOUT",           -- Clock source to drive CLKFBIN ("CLKFBOUT" or "CLKOUT0")
      COMPENSATION => "SYSTEM_SYNCHRONOUS", -- "SYSTEM_SYNCHRONOUS", "SOURCE_SYNCHRONOUS", "EXTERNAL" 
      DIVCLK_DIVIDE => 1,                   -- Division value for all output clocks (1-52)
      REF_JITTER => 0.1,                    -- Reference Clock Jitter in UI (0.000-0.999).
      RESET_ON_LOSS_OF_LOCK => FALSE        -- Must be set to FALSE
   )
   port map (
      CLKFBOUT => CLKFBOUT, -- 1-bit output: PLL_BASE feedback output
      -- CLKOUT0 - CLKOUT5: 1-bit (each) output: Clock outputs
      CLKOUT0 => CLK480,
      CLKOUT1 => open,
      CLKOUT2 => open,
      CLKOUT3 => open,
      CLKOUT4 => open,
      CLKOUT5 => open,
      LOCKED => DCM_LOCKED,     -- 1-bit output: PLL_BASE lock status output
      CLKFBIN => CLKFBOUT,   -- 1-bit input: Feedback clock input
      CLKIN => CLK_DDR2_REF,       -- 1-bit input: Clock input
      RST => ASYNC_RESET            -- 1-bit input: Reset input
   );
   
   -- 4 phases
   CLK_DDR2_GEN_001: process(CLK480)
   begin
    if rising_edge(CLK480) then
        CNTR480 <= CNTR480 + 1;
        CLK_DDR2 <= CNTR480(1);
        CLK_DDR2_90 <= CLK_DDR2;
        CLK_DDR2_180 <= CLK_DDR2_90;
        CLK_DDR2_270 <= CLK_DDR2_180;
    end if;
   end process;
  
end generate;

DCM_SP_GEN_V5A: if (PLATFORM = 5) generate
   -- DCM_BASE: Base Digital Clock Manager Circuit
   --           Virtex-5
   -- Xilinx HDL Language Template, version 13.4

   DCM_BASE_inst : DCM_BASE
   generic map (
      CLKDV_DIVIDE => 2.0, -- Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5
                           --   7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
      CLKFX_DIVIDE => 1,   -- Can be any integer from 1 to 32
      CLKFX_MULTIPLY => 2, -- Can be any integer from 2 to 32
      CLKIN_DIVIDE_BY_2 => FALSE, -- TRUE/FALSE to enable CLKIN divide by two feature
      CLKIN_PERIOD => 5.0, -- Specify period of input clock in ns from 1.25 to 1000.00
      CLKOUT_PHASE_SHIFT => "NONE", -- Specify phase shift mode of NONE or FIXED
      CLK_FEEDBACK => "1X",         -- Specify clock feedback of NONE or 1X
      DCM_PERFORMANCE_MODE => "MAX_SPEED",   -- Can be MAX_SPEED or MAX_RANGE
      DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS", -- SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or
                                             --   an integer from 0 to 15
      DFS_FREQUENCY_MODE => "LOW",   -- LOW or HIGH frequency mode for frequency synthesis
      DLL_FREQUENCY_MODE => "LOW",   -- LOW, HIGH, or HIGH_SER frequency mode for DLL
      DUTY_CYCLE_CORRECTION => TRUE, -- Duty cycle correction, TRUE or FALSE
      FACTORY_JF => X"F0F0",          -- FACTORY JF Values Suggested to be set to X"F0F0" 
      PHASE_SHIFT => 0, -- Amount of fixed phase shift from -255 to 1023
      STARTUP_WAIT => FALSE) -- Delay configuration DONE until DCM LOCK, TRUE/FALSE
   port map (
      CLK0 => CLK_DDR2,         	-- 1-bit 0 degree clock output
      CLK90 => CLK_DDR2_90,      -- 1-bit 90 degree clock output
      CLK180 => CLK_DDR2_180,    -- 1-bit 180 degree clock output
      CLK270 => CLK_DDR2_270,    -- 1-bit 270 degree clock output
      CLK2X => open,       		-- 1-bit 2X clock frequency clock output
      CLK2X180 => open, 			-- 1-bit 2X clock frequency, 180 degree clock output
      CLKDV => open,       		-- 1-bit Divided clock output
      CLKFX => open,       		-- 1-bit Digital Frequency Synthesizer output (DFS)
      CLKFX180 => open, 			-- 1-bit 180 degree CLKFX output
      LOCKED => DCM_LOCKED,     	-- 1-bit DCM Lock Output
      CLKFB => CLK,       	-- 1-bit Clock feedback input
      CLKIN => CLK_DDR2_REF,     -- 1-bit Clock input from PLL above
      RST => ASYNC_RESET         -- 1-bit Active high reset input
   );
end generate;

BUFG_001: BUFG port map(I => CLK_DDR2, O => CLK);
BUFG_002: BUFG port map(I => CLK_DDR2_90, O => CLK_90);
CLK_270 <= not CLK_90;	-- use not CLK_90 instead. Seems to make the P&R tools happier.
CLK_180 <= not CLK;	-- use not CLK instead. Seems to make the P&R tools happier.

SYNC_RESET_GEN: process(CLK, DCM_LOCKED, ASYNC_RESET)
begin
	if (ASYNC_RESET = '1') then
		SYNC_RESET <= '1';
	elsif rising_edge(CLK) then
		ASYNC_RESET_D <= ASYNC_RESET;
		ASYNC_RESET_D2 <= ASYNC_RESET_D;
		
		if(DCM_LOCKED = '0') then
			SYNC_RESET <= '1';
		elsif(ASYNC_RESET_D2 = '0') then
			SYNC_RESET <= '0';
		end if;
	end if;
end process;


CLK_DDR2G_OUT <= CLK;  -- other external components will need this clock

--// EVENTS -----------------------------------------------------------
EVENT1 <= '1' when (STATE = 13) and (TIMER1_EXPIRED = '1') else '0';
	-- end of SDRAM initial configuration

--// TRANSACTION REQUEST MANAGER ---------------------------------------------------------------------------------
-- (1) User-requested transaction: Detect a new transaction request when the transaction request number is incremented
-- (2) Automatic read transaction during round-trip calibration phase
NEW_TRANSACTION_REQ1 <= '1' when (LAST_TRANSACTION_REQ_NO /= TRANSACTION_REQ_NO) and (SDRAM_READY_LOCAL = '1') else '0';
NEW_TRANSACTION_REQ2 <= '1' when ((CAL_STATE = 1) or (CAL_STATE = 2)) and (TIMER3_EXPIRED = '1') else '0';
NEW_TRANSACTION_REQ <= NEW_TRANSACTION_REQ1 or NEW_TRANSACTION_REQ2;  

TRANSACTION_REQ_ACK <= TRANSACTION_REQ_ACK_LOCAL;	-- ack only user-requested transaction. 

-- force a read transaction during the RT delay calibration phase
TRANSACTION_REQ0 <=  "01" when (SDRAM_READY_LOCAL = '0') else TRANSACTION_REQ;


-- latch in the transaction request parameters immediately upon ACK
-- (before the next transaction request comes in)
LATCH_INPUT_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(NEW_TRANSACTION_REQ = '1') then
			COL_D <= COL;
			BANK_D <= BANK;
			ROW_D <= ROW;
			RANK_D <= RANK;
			TRANSACTION_REQ_D <= TRANSACTION_REQ0;
			TRANSACTION_REQ_NO_D <= TRANSACTION_REQ_NO;
			BANK_IS_OPEN_D <= BANK_IS_OPEN;
			if(ROW = ACTIVE_ROW) then
				ROW_IS_ACTIVE_D <= '1';
			else
				ROW_IS_ACTIVE_D <= '0';
			end if;
		end if;
		
		if(TRANSACTION_REQ_ACK_LOCAL = '1') then
			LAST_TRANSACTION_REQ_NO <= TRANSACTION_REQ_NO;

			COL_D2 <= COL_D;
			BANK_D2 <= BANK_D;
			ROW_D2 <= ROW_D;
			RANK_D2 <= RANK_D;
			TRANSACTION_REQ_D2 <= TRANSACTION_REQ_D;
		end if;
	end if;
end process;

-- is the bank requested for the current transaction already open?
BANKSTATUS_002: process(RANK, BANK, OPENBANK_S0B1STATUS, OPENBANK_S0B2STATUS, OPENBANK_S0B3STATUS, OPENBANK_S0B4STATUS,
												OPENBANK_S0B5STATUS, OPENBANK_S0B6STATUS, OPENBANK_S0B7STATUS, OPENBANK_S0B8STATUS,
												OPENBANK_S1B1STATUS, OPENBANK_S1B2STATUS, OPENBANK_S1B3STATUS, OPENBANK_S1B4STATUS, 
												OPENBANK_S1B5STATUS, OPENBANK_S1B6STATUS, OPENBANK_S1B7STATUS, OPENBANK_S1B8STATUS)
begin
	if(RANK = '0') then
		case BANK is
			when "000" => BANK_IS_OPEN <= OPENBANK_S0B1STATUS(15);
			when "001" => BANK_IS_OPEN <= OPENBANK_S0B2STATUS(15);
			when "010" => BANK_IS_OPEN <= OPENBANK_S0B3STATUS(15);
			when "011" => BANK_IS_OPEN <= OPENBANK_S0B4STATUS(15);
			when "100" => BANK_IS_OPEN <= OPENBANK_S0B5STATUS(15);
			when "101" => BANK_IS_OPEN <= OPENBANK_S0B6STATUS(15);
			when "110" => BANK_IS_OPEN <= OPENBANK_S0B7STATUS(15);
			when others => BANK_IS_OPEN <= OPENBANK_S0B8STATUS(15);
		end case;
	else
		case BANK is
			when "000" => BANK_IS_OPEN <= OPENBANK_S1B1STATUS(15);
			when "001" => BANK_IS_OPEN <= OPENBANK_S1B2STATUS(15);
			when "010" => BANK_IS_OPEN <= OPENBANK_S1B3STATUS(15);
			when "011" => BANK_IS_OPEN <= OPENBANK_S1B4STATUS(15);
			when "100" => BANK_IS_OPEN <= OPENBANK_S1B5STATUS(15);
			when "101" => BANK_IS_OPEN <= OPENBANK_S1B6STATUS(15);
			when "110" => BANK_IS_OPEN <= OPENBANK_S1B7STATUS(15);
			when others => BANK_IS_OPEN <= OPENBANK_S1B8STATUS(15);
		end case;
	end if;
end process;
		
-- and, if so, is the requested row active for this bank?
BANKSTATUS_003: process(RANK, BANK, OPENBANK_S0B1STATUS, OPENBANK_S0B2STATUS, OPENBANK_S0B3STATUS, OPENBANK_S0B4STATUS,
												OPENBANK_S0B5STATUS, OPENBANK_S0B6STATUS, OPENBANK_S0B7STATUS, OPENBANK_S0B8STATUS,
												OPENBANK_S1B1STATUS, OPENBANK_S1B2STATUS, OPENBANK_S1B3STATUS, OPENBANK_S1B4STATUS, 
												OPENBANK_S1B5STATUS, OPENBANK_S1B6STATUS, OPENBANK_S1B7STATUS, OPENBANK_S1B8STATUS)
begin
	if(RANK = '0') then
		case BANK is
			when "000" => ACTIVE_ROW <= OPENBANK_S0B1STATUS(14 downto 0);
			when "001" => ACTIVE_ROW <= OPENBANK_S0B2STATUS(14 downto 0);
			when "010" => ACTIVE_ROW <= OPENBANK_S0B3STATUS(14 downto 0);
			when "011" => ACTIVE_ROW <= OPENBANK_S0B4STATUS(14 downto 0);
			when "100" => ACTIVE_ROW <= OPENBANK_S0B5STATUS(14 downto 0);
			when "101" => ACTIVE_ROW <= OPENBANK_S0B6STATUS(14 downto 0);
			when "110" => ACTIVE_ROW <= OPENBANK_S0B7STATUS(14 downto 0);
			when others => ACTIVE_ROW <= OPENBANK_S0B8STATUS(14 downto 0);
		end case;
	else
		case BANK is
			when "000" => ACTIVE_ROW <= OPENBANK_S1B1STATUS(14 downto 0);
			when "001" => ACTIVE_ROW <= OPENBANK_S1B2STATUS(14 downto 0);
			when "010" => ACTIVE_ROW <= OPENBANK_S1B3STATUS(14 downto 0);
			when "011" => ACTIVE_ROW <= OPENBANK_S1B4STATUS(14 downto 0);
			when "100" => ACTIVE_ROW <= OPENBANK_S1B5STATUS(14 downto 0);
			when "101" => ACTIVE_ROW <= OPENBANK_S1B6STATUS(14 downto 0);
			when "110" => ACTIVE_ROW <= OPENBANK_S1B7STATUS(14 downto 0);
			when others => ACTIVE_ROW <= OPENBANK_S1B8STATUS(14 downto 0);
		end case;
	end if;
end process;


--// ADDRESS REMAP ---------------------------------------------------------------------------------
-- no-delay

-- Force START_ADDR to zero when forcing fake read transactions during round-trip delay calibration phase 
START_ADDR0 <= START_ADDR when (SDRAM_READY_LOCAL = '1') else (others => '0');

REMAP_001: process(START_ADDR0)
begin
	case ADDRESS_MAP is
		-- IC
		when "00000" =>   -- 1Gb x8 IC, column A[9:0], 8 banks BA[2:0], row A[13:0], 1 rank CS#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= "0" & START_ADDR0(26 downto 13); -- row
			RANK <= '0'; -- rank			
		when "00001" =>  -- 1Gb x16 IC, column A[9:0], 8 banks BA[2:0], row A[12:0], 1 rank CS#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= "00" & START_ADDR0(25 downto 13); -- row
			RANK <= '0'; -- rank			
		when "00010" => -- 2Gb x8 IC, column A[9:0], 8 banks BA[2:0], row A[14:0], 1 rank CS#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= START_ADDR0(27 downto 13); -- row
			RANK <= '0'; -- rank			
		when "00011" => -- 2Gb x16 IC, column A[9:0], 8 banks BA[2:0], row A[13:0], 1 rank CS#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= "0" & START_ADDR0(26 downto 13); -- row
			RANK <= '0'; -- rank			

		-- DIMM/SODIMM
		when "10000" =>   -- 1GB DIMM/SODIMM, column A[9:0], 8 banks BA[2:0], row A[12:0], 2 ranks S0#/S1#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= "00" & START_ADDR0(25 downto 13); -- row
			RANK <= START_ADDR0(26); -- rank			
		when "10001" =>   -- 2GB DIMM/SODIMM, column A[9:0], 8 banks BA[2:0], row A[13:0], 2 ranks S0#/S1#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= "0" & START_ADDR0(26 downto 13); -- row
			RANK <= START_ADDR0(27); -- rank			
		when others => 
		--when "10010" =>   -- 4GB DIMM/SODIMM, column A[9:0], 8 banks BA[2:0], row A[14:0], 2 ranks S0#/S1#
			COL(14 downto 0) <= "00000" & START_ADDR0(9 downto 0);  -- column
			BANK(2 downto 0) <= START_ADDR0(12 downto 10); -- bank
			ROW(14 downto 0) <= START_ADDR0(27 downto 13); -- row
			RANK <= START_ADDR0(28); -- rank	
	end case;
end process;


--// STATE MACHINE ---------------------------------------------------------------------------------
 
SDRAM_CONTROLLER_STATE_MACHINE_001: process(CLK)
begin
   if rising_edge(CLK) then
		
--//-- DDR2 MEMORY CONTROLLER POWER-UP INITIALIZATION ----------------------------------
		--// initialization sequence after power up. See JEDEC std Figure 14
		if (SYNC_RESET = '1') then
			-- Enable clock (CK, CK#), apply NOP or DESELECT commands
			STATE <= conv_std_logic_vector(1,STATE'LENGTH); 
			TRANSACTION_REQ_ACK_LOCAL <= '0';
		elsif(STATE(4) = '0') and (TIMER1_EXPIRED = '1') then
			if(STATE(3 downto 0) = 1)  then   
				-- 200us timer expired. Take CKE HIGH
				STATE <= conv_std_logic_vector(2,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 2) then   
				-- 400 ns timer expired. issue a PRECHARGE ALL command
				STATE <= conv_std_logic_vector(3,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 3) then   
				-- tRP timer expired. Issue EMRS(2)
				STATE <= conv_std_logic_vector(4,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 4) then   
				-- tMRD timer expired. Issue EMRS(3)
				STATE <= conv_std_logic_vector(5,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 5) then   
				-- tMRD timer expired. Issue EMRS enable DLL 
				STATE <= conv_std_logic_vector(6,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 6) then   
				-- tMRD timer expired. Issue a MRS DLL reset
				STATE <= conv_std_logic_vector(7,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 7) then   
				-- tMRD timer expired. Issue PRE ALL
				STATE <= conv_std_logic_vector(8,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 8) then   
				-- tRP timer expired. Issue 1st REF
				STATE <= conv_std_logic_vector(9,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 9) then   
				-- tRFC timer expired. Issue 2nd REF
				STATE <= conv_std_logic_vector(10,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 10) then   
				-- tRFC timer expired. Issue MRS command to initialize device operation
				STATE <= conv_std_logic_vector(11,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 11) and (TIMER2_EXPIRED = '1') then 
				-- 200 clk timer2 and tMRD timer1 expired. 
				-- skip OCD calibration (OCD function is no longer necessary for DDR2 SDRAM). 
				-- Issue EMRS(1) to set OCD calibration default
				STATE <= conv_std_logic_vector(12,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 12) then   
				-- tOIT timer expired. Issue EMRS(1) to exit OCD calibration mode
				STATE <= conv_std_logic_vector(13,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 13) then 
				-- tOIT timer expired. DDR2 is now READY for normal operation. waiting for a command or refresh timer timeout
				STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
			end if;
		
			
			
--//-- DDR2 MEMORY CONTROLLER INITIALIZED AND IDLE (READY FOR ANOTHER TRANSACTION) ----------------------------------
		elsif(STATE(4) = '1') then
			if(STATE(3 downto 0) = 0) and (TIMER2_EXPIRED = '1') then 
				-- State 16 - Refresh has priority over other read/write/power-down transactions 
				-- But first issue a precharge all command.
				STATE <= conv_std_logic_vector(17,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 0) and (SELF_REFRESH_REQ = '1') then 
				-- State 16 - Power-down request from the user. Issue precharge all to make all banks idle.
				STATE <= conv_std_logic_vector(21,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 0) and (NEW_TRANSACTION_REQ = '1') then
				-- State 16 - next transaction: read or write burst
				STATE <= conv_std_logic_vector(26,STATE'LENGTH); -- banks management.
				TRANSACTION_REQ_ACK_LOCAL <= NEW_TRANSACTION_REQ1;
					-- acknowledge only user-initiated requests. not internal calibration phase read requests.
			elsif(STATE(3 downto 0) = 1) and (TIMER1_EXPIRED = '1') then 
				-- State 19 - followed by a refresh command
				STATE <= conv_std_logic_vector(18,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 2) and (TIMER1_EXPIRED = '1') then 
				-- State 20 - refresh is complete. back to ready state 16
				STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 5) then 
				-- State 21 - entering power-down with self-refresh. Turn ODT OFF
				STATE <= conv_std_logic_vector(22,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 6) and (TIMER1_EXPIRED = '1') and (TIMER2_EXPIRED = '1') then 
				-- State 22 - tRP and tAOFD timers expired. Issue self-refresh. Turn CKE off.
				STATE <= conv_std_logic_vector(23,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 7) and (TIMER1_EXPIRED = '1') then 
				-- State 23 - tCKE timer expired. 
				STATE <= conv_std_logic_vector(24,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 8) and (SELF_REFRESH_REQ = '0') then 
				-- State 24 - User ends Power-down. wait tXSNR
				STATE <= conv_std_logic_vector(25,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 9) and (TIMER1_EXPIRED = '1') then 
				-- State 25 - back to normal operation
				STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 10) then
				TRANSACTION_REQ_ACK_LOCAL <= '0';
				-- State 26 - This is not really a state (no event) but we needed the 1CLK delay to reclock information in a timely manner.
				-- Banks management: there are four cases
				-- (a) current bank is closed => nothing to close. just activate current bank/row
				-- (b) current bank is open, but the active row does not match the current row => close current bank
				-- (c) current bank is open, and active row matches the current row => nothing to do. proceed to read/write
				if(BANK_IS_OPEN_D = '0') then
					-- case (a) activate
					STATE <= conv_std_logic_vector(28,STATE'LENGTH); 
				else
					if (ROW_IS_ACTIVE_D = '0') then
						-- case (b). Precharge current bank before activating it again for the requested row.
						STATE <= conv_std_logic_vector(27,STATE'LENGTH); 
					else
						-- case (c)
						if(TRANSACTION_REQ_D = "01") then
							-- read transaction
							STATE <= conv_std_logic_vector(29,STATE'LENGTH); 
						elsif(TRANSACTION_REQ_D = "10") then
							-- write transaction
							STATE <= conv_std_logic_vector(30,STATE'LENGTH); 
						else
							-- undefined command (ignore). back to idle
							STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
						end if;
					end if;
				end if;
			elsif(STATE(3 downto 0) = 11) and (TIMER1_EXPIRED = '1') then 
				-- State 27 - precharge done. activate bank
				STATE <= conv_std_logic_vector(28,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 12) and (TIMER1_EXPIRED = '1') then 
				-- State 28 - tRCD constraint met. ready for read/write command
				if(TRANSACTION_REQ_D2 = "01") then
					-- read transaction
					STATE <= conv_std_logic_vector(29,STATE'LENGTH); 
				elsif(TRANSACTION_REQ_D2 = "10") then
					-- write transaction
					STATE <= conv_std_logic_vector(30,STATE'LENGTH); 
				else
					-- undefined. back to idle
					STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
				end if;
			elsif(STATE(3 downto 0) = 13) and (NEW_TRANSACTION_REQ = '1') and (TRANSACTION_REQ0 = "01") and (TIMER2_EXPIRED = '0') then
				-- State 29 - successive burst reads. (and no autorefresh yet)
				-- read commands can be pipelined without waiting for the data to be completely read.
				STATE <= conv_std_logic_vector(26,STATE'LENGTH); 
				TRANSACTION_REQ_ACK_LOCAL <= NEW_TRANSACTION_REQ1;
					-- acknowledge only user-initiated requests. not internal calibration phase read requests.
			elsif(STATE(3 downto 0) = 13) and (TIMER1_EXPIRED = '1') then 
				-- State 29 - read transaction complete. ready for something totally different (back to idle)
				STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
			elsif(STATE(3 downto 0) = 14) and (NEW_TRANSACTION_REQ1 = '1') and (TRANSACTION_REQ0 = "10") and (TIMER2_EXPIRED = '0') then
				-- State 30 - successive burst writes. (and no autorefresh yet) 
				-- write commands can be pipelined without waiting for the data to be completely read.
				STATE <= conv_std_logic_vector(26,STATE'LENGTH); 
				TRANSACTION_REQ_ACK_LOCAL <= '1';
			elsif(STATE(3 downto 0) = 14) and (TIMER1_EXPIRED = '1') then 
				-- State 30 - burst write transaction complete. ready for something totally different (back to idle)
				STATE <= conv_std_logic_vector(16,STATE'LENGTH); 
			else
				TRANSACTION_REQ_ACK_LOCAL <= '0';
			end if;
		end if;
	end if;
end process;


--// TIMERS ---------------------------------------------------------------------------------
-- Timer is 0 when expired.
TIMER1_001: process(CLK)
begin
   if rising_edge(CLK) then
		if (SYNC_RESET = '1') then
			-- arm timer: 200us + margin
			if(SIMULATION = '0') then
				TIMER1 <= conv_std_logic_vector(INIT_COUNT,TIMER1'LENGTH); 
			else
				-- shorten timer for faster simulation
				TIMER1 <= conv_std_logic_vector(INIT_COUNT_SIMUL,TIMER1'LENGTH); 
			end if;
			TIMER1_EXPIRED <= '0';
		elsif(STATE(4) = '0') and (TIMER1_EXPIRED = '1') then
			if(STATE(3 downto 0) = 1) then   
				-- re-arm timer: 400ns + margin
				TIMER1 <= conv_std_logic_vector(INIT2_COUNT,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 2) then   
				-- re-arm timer: tRP
				TIMER1 <= conv_std_logic_vector(TRP_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 3) then   
				-- re-arm timer: tMRD
				TIMER1 <= conv_std_logic_vector(TMRD_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 4) then   
				-- re-arm timer: tMRD
				TIMER1 <= conv_std_logic_vector(TMRD_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 5) then   
				-- re-arm timer: tMRD
				TIMER1 <= conv_std_logic_vector(TMRD_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 6) then   
				-- re-arm timer: tMRD
				TIMER1 <= conv_std_logic_vector(TMRD_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 7) then   
				-- re-arm timer: tRP
				TIMER1 <= conv_std_logic_vector(TRP_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 8) then   
				-- re-arm timer: tRFC
				TIMER1 <= conv_std_logic_vector(TRFC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 9) then   
				-- re-arm timer: tRFC
				TIMER1 <= conv_std_logic_vector(TRFC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 10) then  
				-- re-arm timer: tMRD
				TIMER1 <= conv_std_logic_vector(TMRD_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 11) and (TIMER2_EXPIRED = '1') then 
				-- 200 clk timer2 and tMRD timer1 expired. 
				-- re-arm timer: tOIT
				TIMER1 <= conv_std_logic_vector(TOIT,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			elsif(STATE(3 downto 0) = 12) then  
				-- re-arm timer: tOIT
				TIMER1 <= conv_std_logic_vector(TOIT,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			end if;
		elsif(STATE = 16) and (TIMER2_EXPIRED = '1') then 
			-- re-arm timer: tRP
			TIMER1 <= conv_std_logic_vector(TRP_DEC,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif(STATE = 16) and (SELF_REFRESH_REQ = '1') then 
			-- Power-down request from the user. 
			-- re-arm timer: tRP
			TIMER1 <= conv_std_logic_vector(TRP_DEC,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif(STATE = 17) and (TIMER1_EXPIRED = '1') then   
			-- re-arm timer: tRFC
			TIMER1 <= conv_std_logic_vector(TRFC,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif(STATE = 22) and (TIMER1_EXPIRED = '1') and (TIMER2_EXPIRED = '1') then 
			-- re-arm timer: tCKE
			TIMER1 <= conv_std_logic_vector(TCKE_DEC,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif(STATE = 24) and (SELF_REFRESH_REQ = '0') then 
			-- user ends power-down/self refresh
			-- re-arm timer: tXSRD (200 CLK = much longer than the other tXSNR requirement)
			TIMER1 <= conv_std_logic_vector(TXSRD_DEC,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif (STATE = 26) then
			if (BANK_IS_OPEN_D = '0') then
				-- re-arm timer: tRCD
				TIMER1 <= conv_std_logic_vector(TRCD_DEC,TIMER1'LENGTH);  
				TIMER1_EXPIRED <= '0';
			else
				if (ROW_IS_ACTIVE_D = '0') then   
					-- re-arm timer: tRP
					TIMER1 <= conv_std_logic_vector(TRP_DEC,TIMER1'LENGTH);  
					TIMER1_EXPIRED <= '0';
				else   
					-- two different timers for read and write  TODO 
					if(TRANSACTION_REQ_D = "01") then
						-- burst read transaction. Can't go to idle until all data is read = CL + 1
						TIMER1 <= conv_std_logic_vector(CLOCK_CYCLES,TIMER1'LENGTH);  
						TIMER1_EXPIRED <= '0';
					elsif(TRANSACTION_REQ_D = "10") then
						-- burst write transaction. Delay to next read is CL - 1 + BL/2+ tWTR(2)
						-- but if the next transaction is a precharge, the delay should be CL - 1 + BL/2+ tWR which is longer
						-- to simplify the code, use the latter
						TIMER1 <= conv_std_logic_vector(CLOCK_CYCLES + TWR - 1,TIMER1'LENGTH);  
						TIMER1_EXPIRED <= '0';
					end if;
				end if;
			end if;
		elsif(STATE = 27) and (TIMER1_EXPIRED = '1') then 
			-- re-arm timer: tRCD
			TIMER1 <= conv_std_logic_vector(TRCD_DEC,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "01") then
			-- burst read transaction. Can't go to idle until all data is read = CL + 1
			TIMER1 <= conv_std_logic_vector(CLOCK_CYCLES,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "10") then
			-- burst write transaction 
			TIMER1 <= conv_std_logic_vector(CLOCK_CYCLES + TWR - 1,TIMER1'LENGTH);  
			TIMER1_EXPIRED <= '0';
		elsif (TIMER1_EXPIRED = '0') then 
			-- count-down to zero (expired)
			TIMER1 <= TIMER1 - 1;
			if(TIMER1 = 1) then
				TIMER1_EXPIRED <= '1';
			else
				TIMER1_EXPIRED <= '0';
			end if;
		end if;
	end if;
end process;


-- Timer 2 is mostly the refresh timer, although it is also used 
-- as a general purpose timer during power-up initialization, self-refresh, etc.
TIMER2_001: process(CLK)
begin
   if rising_edge(CLK) then
		if(STATE = 6) and (TIMER1_EXPIRED = '1') then   
			-- 200 CLKs timer
			TIMER2 <= conv_std_logic_vector(199,TIMER2'LENGTH);  
			TIMER2_EXPIRED <= '0';
		elsif(EVENT1 = '1') then  
			-- re-arm timer: tREFI
			TIMER2 <= conv_std_logic_vector(TREFI,TIMER2'LENGTH);  
			TIMER2_EXPIRED <= '0';
		elsif(STATE = 16) and (TIMER2_EXPIRED = '1') then 
			-- re-arm timer: tREFI
			TIMER2 <= conv_std_logic_vector(TREFI,TIMER2'LENGTH);  
			TIMER2_EXPIRED <= '0';
		elsif(STATE = 21) then 
			-- re-arm timer: tAOFD
			TIMER2 <= conv_std_logic_vector(TAOFD_DEC,TIMER2'LENGTH);  
			TIMER2_EXPIRED <= '0';
		elsif(STATE = 24) and (SELF_REFRESH_REQ = '0') then 
			-- clear auto refresh timer to force an auto refresh immediately upon exiting power-down/self-refresh 
			-- (better safe if the user wants to immediately re-enter power-down mode)
			TIMER2 <= (others => '0');
			TIMER2_EXPIRED <= '1';
		elsif (TIMER2_EXPIRED = '0') then 
			-- count-down to zero (expired)
			TIMER2 <= TIMER2 - 1;
			if(TIMER2 = 1) then
				TIMER2_EXPIRED <= '1';
			else
				TIMER2_EXPIRED <= '0';
			end if;
		end if;
	end if;
end process;

-- Timer 3 is for scheduling periodic reads when Round-trip delay check mode is enabled 
TIMER3_001: process(CLK)
begin
   if rising_edge(CLK) then
		if (SYNC_RESET = '1') then
			TIMER3_EXPIRED <= '1';
		elsif(SDRAM_READY_LOCAL = '0') and (TIMER3_EXPIRED = '1') then   
			-- Round-trip delay check mode is on. re-arm timer
			TIMER3 <= conv_std_logic_vector(TRTDELAYCHECK,TIMER3'LENGTH);  
			TIMER3_EXPIRED <= '0';
		elsif (TIMER3_EXPIRED = '0') then 
			-- count-down to zero (expired)
			TIMER3 <= TIMER3 - 1;
			if(TIMER3 = 1) then	-- look ahead for expired timer (better timing)
				TIMER3_EXPIRED <= '1';
			else
				TIMER3_EXPIRED <= '0';
			end if;
		end if;
	end if;
end process;

--// COMMANDS  ---------------------------------------------------------------------------------
COMMANDS_001: process(CLK)
begin
   if rising_edge(CLK) then
		
		if(STATE(4) = '0') and (TIMER1_EXPIRED = '1') then
			if(STATE(3 downto 0) = 1) then   
				COMMAND <= NOP; 
			elsif(STATE(3 downto 0) = 2) then   
				COMMAND <= PRECHARGE; 
			elsif(STATE(3 downto 0) = 3) then   
				COMMAND <= MODE_REGISTER_SET; 
			elsif(STATE(3 downto 0) = 4) then   
				COMMAND <= MODE_REGISTER_SET; 
			elsif(STATE(3 downto 0) = 5) then   
				COMMAND <= MODE_REGISTER_SET; 
			elsif(STATE(3 downto 0) = 6) then   
				COMMAND <= MODE_REGISTER_SET; 
			elsif(STATE(3 downto 0) = 7) then   
				COMMAND <= PRECHARGE; 
			elsif(STATE(3 downto 0) = 8) then   
				COMMAND <= REFRESH; 
			elsif(STATE(3 downto 0) = 9) then   
				COMMAND <= REFRESH; 
			elsif(STATE(3 downto 0) = 10) then   
				COMMAND <= MODE_REGISTER_SET; 
			elsif(STATE(3 downto 0) = 11) and (TIMER2_EXPIRED = '1') then 
				COMMAND <= MODE_REGISTER_SET; 
			elsif(STATE(3 downto 0) = 12) then   
				COMMAND <= MODE_REGISTER_SET; 
			end if;
		elsif(STATE = 16) and (TIMER2_EXPIRED = '1') then 
			COMMAND <= PRECHARGE; 
		elsif(STATE = 16) and (SELF_REFRESH_REQ = '1') then 
			COMMAND <= PRECHARGE; 
		elsif(STATE = 17) and (TIMER1_EXPIRED = '1') then 
			COMMAND <= REFRESH; 
		elsif(STATE = 22) and (TIMER1_EXPIRED = '1') and (TIMER2_EXPIRED = '1') then 
			COMMAND <= REFRESH; 
		elsif (STATE = 26) then
			if (BANK_IS_OPEN_D = '0') then
				COMMAND <= ACTIVATE;
			else 
				if (ROW_IS_ACTIVE_D = '0') then   
					COMMAND <= PRECHARGE; 
				else
					if(TRANSACTION_REQ_D = "01") then
						COMMAND <= READ; 
					elsif(TRANSACTION_REQ_D = "10") then
						COMMAND <= WRITE;
					end if;
				end if;
			end if;
		elsif(STATE = 27) and (TIMER1_EXPIRED = '1') then 
			COMMAND <= ACTIVATE;
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "01") then
				-- read transaction 
			COMMAND <= READ;
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "10") then
				-- write transaction 
			COMMAND <= WRITE;
		else
			COMMAND <= NOP; 
		end if;
	end if;
end process;



--// DDR2 CONTROLS ---------------------------------------------------------------------------------
CS_GEN_001: process(CLK)
begin
   if rising_edge(CLK) then
		if (SYNC_RESET = '1') then
			SDRAM_SN_LOCAL <= "11";  -- deselect 
		elsif (STATE = 1) and (TIMER1_EXPIRED = '1') then
			-- enable both ranks during power-up initialization
			SDRAM_SN_LOCAL <= "00"; 
		elsif(STATE = 16) and (TIMER2_EXPIRED = '1') then 
			-- precharge all
			SDRAM_SN_LOCAL <= "00"; 
		elsif(STATE = 16) and (SELF_REFRESH_REQ = '1') then 
			-- entering self-refresh. precharge all
			SDRAM_SN_LOCAL <= "00"; 
		elsif (STATE = 26) then
			if (BANK_IS_OPEN_D = '0') then
				-- activate the specified row/bank/rank
				SDRAM_SN_LOCAL(0) <= RANK_D; 
				SDRAM_SN_LOCAL(1) <= not RANK_D;
			else
				if (ROW_IS_ACTIVE_D = '0') then   
					-- precharge all
					SDRAM_SN_LOCAL <= "00"; 
				else    
					-- read/write from/to a specific bank/rank
					SDRAM_SN_LOCAL(0) <= RANK_D; 
					SDRAM_SN_LOCAL(1) <= not RANK_D;
				end if;
			end if;
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') then
			-- read/write from/to a specific bank/rank
			SDRAM_SN_LOCAL(0) <= RANK_D2; 
			SDRAM_SN_LOCAL(1) <= not RANK_D2;
		end if;
	end if;
end process;


CKE_GEN: for I in 0 to (NCLKS-1) generate
	CKE_GEN_001: process(CLK)
	begin
		if rising_edge(CLK) then
			if (SYNC_RESET = '1') then
				SDRAM_CKE(I) <= '0';
			elsif (STATE = 1) and (TIMER1_EXPIRED = '1') then
				-- always keep the clocks enabled after initialization  
				SDRAM_CKE(I) <= '1';
			elsif(STATE = 22) and (TIMER1_EXPIRED = '1') and (TIMER2_EXPIRED = '1') then 
				-- entering power-down/self-refresh mode
				SDRAM_CKE(I) <= '0';
			elsif(STATE = 24) and (SELF_REFRESH_REQ = '0') then 
				-- user ends power-down/self refresh
				SDRAM_CKE(I) <= '1';
			end if;
		end if;
	end process;
end generate;

ADDR_GEN_001: process(CLK)
begin
   if rising_edge(CLK) then
		if (SYNC_RESET = '1') then
			SDRAM_BA_LOCAL <= "000";  
			SDRAM_A_LOCAL(15 downto 0) <= (others => '0');
		elsif(STATE(4) = '0') then
			if(STATE(3 downto 0) = 2) then   
				SDRAM_A_LOCAL(10) <= '1';	-- precharge all
			elsif (STATE(3 downto 0) = 3) then
				-- e) issue EMRS command to EMR(2)
				SDRAM_BA_LOCAL <= "010";  
				SDRAM_A_LOCAL(15 downto 4) <= (others => '0');
					-- A7 high-temperature self-refresh rate disable
				SDRAM_A_LOCAL(3) <= '0';	-- Duty-Cycle Corrector disabled
				SDRAM_A_LOCAL(2 downto 0) <= "000";  -- partial array self refresh: full array
			elsif (STATE(3 downto 0) = 4) then
				-- f) issue EMRS command to EMR(3)
				SDRAM_BA_LOCAL <= "011";  
				SDRAM_A_LOCAL(15 downto 0) <= (others => '0');
			elsif (STATE(3 downto 0) = 5) then
				-- g) issue EMRS command to EMR(1) to enable DLL
				SDRAM_BA_LOCAL <= "001";  
				SDRAM_A_LOCAL(15 downto 13) <= (others => '0');
				SDRAM_A_LOCAL(12) <= '0';	-- Output buffer enabled
				SDRAM_A_LOCAL(11) <= '0';	-- RDQS disable
				SDRAM_A_LOCAL(10) <= '0';	-- DQS# enable
				SDRAM_A_LOCAL(9 downto 7) <= (others => '0');  -- OCD calibration mode exit; maintain setting
				SDRAM_A_LOCAL(5 downto 3) <= "000";  -- additive latency AL = 0
				SDRAM_A_LOCAL(6) <= '0';  -- On-die termination, Rtt 75 Ohm enabled.
				SDRAM_A_LOCAL(2) <= '1';  -- On-die termination, Rtt 75 Ohm enabled.
				SDRAM_A_LOCAL(1) <= '0';  -- Output drive strength normal
				SDRAM_A_LOCAL(0) <= '0';  -- enable DLL
			elsif (STATE(3 downto 0) = 6) then
				-- h) issue mode register set command for DLL reset
				SDRAM_BA_LOCAL <= "000";  
				SDRAM_A_LOCAL(7 downto 0) <= (others => '0');
				SDRAM_A_LOCAL(8) <= '1';	-- DLL reset
				SDRAM_A_LOCAL(15 downto 13) <= (others => '0');
			elsif(STATE(3 downto 0) = 7) then   
				SDRAM_A_LOCAL(10) <= '1';	-- precharge all
			elsif(STATE(3 downto 0) = 10) then   
				-- k) issue a MRS command to initialize device operation without resetting the DLL.
				SDRAM_A_LOCAL(12) <= '0';	-- Active power down exit time: fast exit
				SDRAM_A_LOCAL(11 downto 9) <= conv_std_logic_vector(TWR-1,3); -- write recovery for autoprecharge
				SDRAM_A_LOCAL(8) <= '0';	-- no DLL reset	
				SDRAM_A_LOCAL(7) <= '0';	-- Normal mode
				SDRAM_A_LOCAL(6 downto 4) <= conv_std_logic_vector(CLOCK_CYCLES,3);	-- CAS latency
				SDRAM_A_LOCAL(3) <= '0';	-- sequential burst type 
				SDRAM_A_LOCAL(2 downto 0) <= "010";	-- Burst length 4
			elsif(STATE(3 downto 0) = 11) then   
				-- l) issue EMRS command to EMR(1) to set OCD calibration default
				SDRAM_BA_LOCAL <= "001";  
				SDRAM_A_LOCAL(15 downto 13) <= (others => '0');
				SDRAM_A_LOCAL(12) <= '0';	-- Output buffer enabled
				SDRAM_A_LOCAL(11) <= '0';	-- RDQS disable
				SDRAM_A_LOCAL(10) <= '0';	-- DQS# enable
				SDRAM_A_LOCAL(9 downto 7) <= (others => '1');  -- OCD calibration default
				SDRAM_A_LOCAL(5 downto 3) <= "000";  -- additive latency
				SDRAM_A_LOCAL(6) <= '0';  -- On-die termination, Rtt 75 Ohm enabled.
				SDRAM_A_LOCAL(2) <= '1';  -- On-die termination, Rtt 75 Ohm enabled.
				SDRAM_A_LOCAL(1) <= '0';  -- Output drive strength normal
				SDRAM_A_LOCAL(0) <= '0';  -- enable DLL
			elsif(STATE(3 downto 0) = 12) then   
				 -- l) issue EMRS command to EMR(1) to exit OCD calibration mode
				SDRAM_BA_LOCAL <= "001"; 
	--			SDRAM_A_LOCAL(15 downto 13) <= (others => '0');
	--			SDRAM_A_LOCAL(12) <= '0';	-- Output buffer enabled
	--			SDRAM_A_LOCAL(11) <= '0';	-- RDQS disable
	--			SDRAM_A_LOCAL(10) <= '0';	-- DQS# enable
				SDRAM_A_LOCAL(9 downto 7) <= (others => '0');  -- Exit OCD calibration mode
	--			SDRAM_A_LOCAL(5 downto 3) <= "000";  -- additive latency
	--			SDRAM_A_LOCAL(6) <= '0';  -- On-die termination, Rtt 75 Ohm enabled.
	--			SDRAM_A_LOCAL(2) <= '1';  -- On-die termination, Rtt 75 Ohm enabled.
	--			SDRAM_A_LOCAL(1) <= '0';  -- Output drive strength normal
	--			SDRAM_A_LOCAL(0) <= '0';  -- enable DLL
			end if;
		else
			if(STATE(3 downto 0) = 0) and (TIMER2_EXPIRED = '1') then 
				-- State 16 - precharge all
				SDRAM_A_LOCAL(10) <= '1';
	--		elsif(STATE(3 downto 0) = 0) then
	--			-- State 16 - NOP. minimize power consumption in the case of pull-down resistors on the command lines
	--			SDRAM_A_LOCAL <= (others => '0');
	--			SDRAM_BA_LOCAL <= (others => '0');
			elsif (STATE(3 downto 0) = 10) then
				-- State 26
				if (BANK_IS_OPEN_D = '0') then
					-- activate bank/row
					SDRAM_BA_LOCAL <= BANK_D;  -- select the desired bank
					SDRAM_A_LOCAL(14 downto 0) <= ROW_D; -- activate this row
				else
					if (ROW_IS_ACTIVE_D = '0') then   
						-- close this current bank (we will activate a different row shortly)
						SDRAM_BA_LOCAL <= BANK_D; 
						SDRAM_A_LOCAL(10) <= '0';
					else    
						-- immediate read/write
						SDRAM_A_LOCAL(9 downto 0) <= COL_D(9 downto 0); -- burst start column
						SDRAM_A_LOCAL(10) <= '0';	-- autoprecharge always disabled
						SDRAM_A_LOCAL(14 downto 11) <= COL_D(13 downto 10); -- burst start column
						SDRAM_BA_LOCAL <= BANK_D; 
					end if;
				end if;
			elsif(STATE(3 downto 0) = 11) then 
				-- State 27 - activate bank/row
				SDRAM_BA_LOCAL <= BANK_D2;  -- select the desired bank
				SDRAM_A_LOCAL(14 downto 0) <= ROW_D2; -- activate this row
			elsif(STATE(3 downto 0) = 12) then 
				-- State 28 - burst read or write
				SDRAM_A_LOCAL(9 downto 0) <= COL_D2(9 downto 0); -- burst start column
				SDRAM_A_LOCAL(10) <= '0';	-- autoprecharge disabled
				SDRAM_A_LOCAL(14 downto 11) <= COL_D2(13 downto 10); -- burst start column
				SDRAM_BA_LOCAL <= BANK_D2; 
			end if;
		end if;
	end if;
end process;

-- simple code, but large delay to output pin
--RASN_GEN: process(COMMAND)
--begin	
--	case COMMAND is
--		when NOP => SDRAM_RASN <= '1';
--		when ACTIVATE => SDRAM_RASN <= '0';
--		when PRECHARGE => SDRAM_RASN <= '0';
--		when READ => SDRAM_RASN <= '1';
--		when WRITE => SDRAM_RASN <= '1';
--		when REFRESH => SDRAM_RASN <= '0';
--		when MODE_REGISTER_SET => SDRAM_RASN <= '0';
--		when others => SDRAM_RASN <= '0';
--	end case;
--end process;

--CASN_GEN: process(COMMAND)
--begin	
--	case COMMAND is
--		when NOP => SDRAM_CASN <= '1';
--		when ACTIVATE => SDRAM_CASN <= '1';
--		when PRECHARGE => SDRAM_CASN <= '1';
--		when READ => SDRAM_CASN <= '0';
--		when WRITE => SDRAM_CASN <= '0';
--		when REFRESH => SDRAM_CASN <= '0';
--		when MODE_REGISTER_SET => SDRAM_CASN <= '0';
--		when others => SDRAM_CASN <= '0';
--	end case;
--end process;

--WEN_GEN: process(COMMAND)
--begin	
--	case COMMAND is
--		when NOP => SDRAM_WEN <= '1';
--		when ACTIVATE => SDRAM_WEN <= '1';
--		when READ => SDRAM_WEN <= '1';
--		when REFRESH => SDRAM_WEN <= '1';
--		when WRITE => SDRAM_WEN <= '0';
--		when PRECHARGE => SDRAM_WEN <= '0';
--		when MODE_REGISTER_SET => SDRAM_WEN <= '0';
--		when others => SDRAM_WEN <= '0';
--	end case;
--end process;

RASN_CASN_WEN_GEN: process(CLK)
begin
   if rising_edge(CLK) then
		
		if(STATE(4) = '0') and (TIMER1_EXPIRED = '1') then
			if(STATE(3 downto 0) = 1) then  -- NOP
				SDRAM_RASN <= '1';
				SDRAM_CASN <= '1';
				SDRAM_WEN <= '1';
			elsif(STATE(3 downto 0) = 2) then -- PRECHARGE   
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '1';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 3) then -- MODE_REGISTER_SET
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 4) then -- MODE_REGISTER_SET  
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 5) then -- MODE_REGISTER_SET  
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 6) then -- MODE_REGISTER_SET  
				SDRAM_RASN <= '0'; 
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 7) then -- PRECHARGE  
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '1';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 8) then  -- REFRESH 
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '1';
			elsif(STATE(3 downto 0) = 9) then  -- REFRESH  
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '1';
			elsif(STATE(3 downto 0) = 10) then -- MODE_REGISTER_SET  
				SDRAM_RASN <= '0'; 
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 11) and (TIMER2_EXPIRED = '1') then -- MODE_REGISTER_SET
				SDRAM_RASN <= '0'; 
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			elsif(STATE(3 downto 0) = 12) then -- MODE_REGISTER_SET  
				SDRAM_RASN <= '0'; 
				SDRAM_CASN <= '0';
				SDRAM_WEN <= '0';
			end if;
		elsif(STATE = 16) and (TIMER2_EXPIRED = '1') then -- PRECHARGE
			SDRAM_RASN <= '0';
			SDRAM_CASN <= '1';
			SDRAM_WEN <= '0';
		elsif(STATE = 16) and (SELF_REFRESH_REQ = '1') then  -- PRECHARGE 
			SDRAM_RASN <= '0';
			SDRAM_CASN <= '1';
			SDRAM_WEN <= '0';
		elsif(STATE = 17) and (TIMER1_EXPIRED = '1') then -- REFRESH
			SDRAM_RASN <= '0';
			SDRAM_CASN <= '0';
			SDRAM_WEN <= '1';
		elsif(STATE = 22) and (TIMER1_EXPIRED = '1') and (TIMER2_EXPIRED = '1') then  -- REFRESH 
			SDRAM_RASN <= '0';
			SDRAM_CASN <= '0';
			SDRAM_WEN <= '1';
		elsif (STATE = 26) then
			if (BANK_IS_OPEN_D = '0') then -- ACTIVATE
				SDRAM_RASN <= '0';
				SDRAM_CASN <= '1';
				SDRAM_WEN <= '1';
			else
				if (ROW_IS_ACTIVE_D = '0') then   -- PRECHARGE
					SDRAM_RASN <= '0';
					SDRAM_CASN <= '1';
					SDRAM_WEN <= '0';
				else
					if(TRANSACTION_REQ_D = "01") then -- READ
						SDRAM_RASN <= '1';
						SDRAM_CASN <= '0';
						SDRAM_WEN <= '1';
					elsif(TRANSACTION_REQ_D = "10") then  -- WRITE
						SDRAM_RASN <= '1';
						SDRAM_CASN <= '0';
						SDRAM_WEN <= '0';
					end if;
				end if;
			end if;
		elsif(STATE = 27) and (TIMER1_EXPIRED = '1') then -- ACTIVATE
			SDRAM_RASN <= '0';
			SDRAM_CASN <= '1';
			SDRAM_WEN <= '1';
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "01") then  -- READ
			-- read transaction 
			SDRAM_RASN <= '1';
			SDRAM_CASN <= '0';
			SDRAM_WEN <= '1';
		elsif(STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "10") then  -- WRITE
				-- write transaction 
			SDRAM_RASN <= '1';
			SDRAM_CASN <= '0';
			SDRAM_WEN <= '0';
		else	-- NOP
			SDRAM_RASN <= '1';
			SDRAM_CASN <= '1';
			SDRAM_WEN <= '1';
		end if;
	end if;
end process;





-- 270 deg offset used to account for tAON min/max and tAOF min/max
ODT_GEN: process(CLK_90)
begin
   if falling_edge(CLK_90) then
		if (SYNC_RESET = '1') then
			-- keep ODT low while setting the Rtt impedance (EMR(1))
			SDRAM_ODT <=  "00";
		elsif(STATE = 21) then 
			-- turn ODT off before issuing Self Refresh command.
			SDRAM_ODT <= "00";
		else 
			-- write state. Accounts for tAOND(2clk)/tAOFD(2.5clk) delay in turning the termination on/off
			SDRAM_ODT <= (others => DATA_IN_CTS_LOCAL);
		end if;
	end if;
end process;

--// to SDRAM
SDRAM_A <= SDRAM_A_LOCAL;
SDRAM_BA <= SDRAM_BA_LOCAL;
SDRAM_SN <= SDRAM_SN_LOCAL;


--// BANKS MANAGEMENT ---------------------------------------------------------------------------------
-- Keep track of the last 4-banks open
BANKSTATUS_001: process(CLK)
begin
   if rising_edge(CLK) then
		if (SYNC_RESET = '1') or ((COMMAND = PRECHARGE) and (SDRAM_A_LOCAL(10) = '1')) then
			-- all banks closed by default at power-up/reset
			-- or precharge all closes all banks
			OPENBANK_S0B1STATUS(15) <= '0';
			OPENBANK_S0B2STATUS(15) <= '0';
			OPENBANK_S0B3STATUS(15) <= '0';
			OPENBANK_S0B4STATUS(15) <= '0';
			OPENBANK_S0B5STATUS(15) <= '0';
			OPENBANK_S0B6STATUS(15) <= '0';
			OPENBANK_S0B7STATUS(15) <= '0';
			OPENBANK_S0B8STATUS(15) <= '0';
			OPENBANK_S1B1STATUS(15) <= '0';
			OPENBANK_S1B2STATUS(15) <= '0';
			OPENBANK_S1B3STATUS(15) <= '0';
			OPENBANK_S1B4STATUS(15) <= '0';
			OPENBANK_S1B5STATUS(15) <= '0';
			OPENBANK_S1B6STATUS(15) <= '0';
			OPENBANK_S1B7STATUS(15) <= '0';
			OPENBANK_S1B8STATUS(15) <= '0';
		elsif(COMMAND = PRECHARGE) and (SDRAM_A_LOCAL(10) = '0') then
			-- precharge (i.e. close) a specific bank/rank
			if(SDRAM_SN_LOCAL(0) = '0') then
				-- rank 0
				case SDRAM_BA_LOCAL is
					when "000" => OPENBANK_S0B1STATUS(15) <= '0';
					when "001" => OPENBANK_S0B2STATUS(15) <= '0';
					when "010" => OPENBANK_S0B3STATUS(15) <= '0';
					when "011" => OPENBANK_S0B4STATUS(15) <= '0';
					when "100" => OPENBANK_S0B5STATUS(15) <= '0';
					when "101" => OPENBANK_S0B6STATUS(15) <= '0';
					when "110" => OPENBANK_S0B7STATUS(15) <= '0';
					when others => OPENBANK_S0B8STATUS(15) <= '0';
				end case;
			end if;
			if(SDRAM_SN_LOCAL(1) = '0') then
				-- rank 0
				case SDRAM_BA_LOCAL is
					when "000" => OPENBANK_S1B1STATUS(15) <= '0';
					when "001" => OPENBANK_S1B2STATUS(15) <= '0';
					when "010" => OPENBANK_S1B3STATUS(15) <= '0';
					when "011" => OPENBANK_S1B4STATUS(15) <= '0';
					when "100" => OPENBANK_S1B5STATUS(15) <= '0';
					when "101" => OPENBANK_S1B6STATUS(15) <= '0';
					when "110" => OPENBANK_S1B7STATUS(15) <= '0';
					when others => OPENBANK_S1B8STATUS(15) <= '0';
				end case;
			end if;
		elsif(COMMAND = ACTIVATE) then
			-- remember which row is opened for each bank/rank
			if(SDRAM_SN_LOCAL(0) = '0') then
				-- rank 0
				case SDRAM_BA_LOCAL is
					when "000" => OPENBANK_S0B1STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "001" => OPENBANK_S0B2STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "010" => OPENBANK_S0B3STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "011" => OPENBANK_S0B4STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "100" => OPENBANK_S0B5STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "101" => OPENBANK_S0B6STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "110" => OPENBANK_S0B7STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when others => OPENBANK_S0B8STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
				end case;
			end if;
			if(SDRAM_SN_LOCAL(1) = '0') then
				-- rank 0
				case SDRAM_BA_LOCAL is
					when "000" => OPENBANK_S1B1STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "001" => OPENBANK_S1B2STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "010" => OPENBANK_S1B3STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "011" => OPENBANK_S1B4STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "100" => OPENBANK_S1B5STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "101" => OPENBANK_S1B6STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when "110" => OPENBANK_S1B7STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
					when others => OPENBANK_S1B8STATUS(15 downto 0) <= "1" & SDRAM_A_LOCAL(14 downto 0);
				end case;
			end if;
		end if;
	end if;
end process;

-- data is never masked
SDRAM_DM <= (others => '0');



--// WRITE TRANSACTION -----------------------------------------------------
-- request data for just-in-time delivery, two CLKs before it is needed.
-- double the CTS width to request enough data for a burst length BL = 4.

-- advance notification that the next command is a WRITE.
WRITE_EVENT <= 	'1' when (STATE = 26) and (BANK_IS_OPEN_D = '1') and (ROW_IS_ACTIVE_D = '1') and 
							(TRANSACTION_REQ_D = "10") else
						'1' when (STATE = 28) and (TIMER1_EXPIRED = '1') and (TRANSACTION_REQ_D2 = "10") else
						'0';

DATA_IN_CTS_LOCAL <= (WRITE_EVENT or WRITE_EVENT_D) when (CLOCK_CYCLES = 3) else
							(WRITE_EVENT_D or WRITE_EVENT_D2) when (CLOCK_CYCLES = 4) else
							(WRITE_EVENT_D2 or WRITE_EVENT_D3) when (CLOCK_CYCLES = 5) else
							(WRITE_EVENT_D3 or WRITE_EVENT_D4) when (CLOCK_CYCLES = 6) else
							'0';
DATA_IN_CTS <= DATA_IN_CTS_LOCAL;
	
-- re-align DATA_IN with DATA_IN_CTS_D2
RECLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		WRITE_EVENT_D <= WRITE_EVENT;
		WRITE_EVENT_D2 <= WRITE_EVENT_D;
		WRITE_EVENT_D3 <= WRITE_EVENT_D2;
		WRITE_EVENT_D4 <= WRITE_EVENT_D3;

		DATA_IN_CTS_D <= DATA_IN_CTS_LOCAL;
		DATA_IN_CTS_D2 <= DATA_IN_CTS_D;
		DATA_IN_CTS_D3 <= DATA_IN_CTS_D2;
	end if;
end process;


-- place output data in high-impedance outside the tx burst + margin on each side.
-- DQS tx window is 2.5 CLK long  (burst = 2, tWPRE = 0.35 min, tWPST = 0.4 to 0.6)
-- extend length before ODDR2
SDRAM_DQS_EN_001: process(CLK_90, DATA_IN_CTS_D)
begin
	if falling_edge(CLK_90) then
		DATA_IN_CTS_D1P75_N <= not DATA_IN_CTS_D;
		DATA_IN_CTS_D2P75_N <= DATA_IN_CTS_D1P75_N;
	end if;
end process;
SDRAM_DQS_EN_002: process(CLK_90, DATA_IN_CTS_D1P75_N)
begin
	if rising_edge(CLK_90) then
		DATA_IN_CTS_D2P25_N <= DATA_IN_CTS_D1P75_N;
	end if;
end process;
-- SDRAM_DQSZ <= DATA_IN_CTS_D2P25_N and DATA_IN_CTS_D2P75_N;	-- not really sure why, but simulator has a problem here. use safer statement below instead.
SDRAM_DQSZ <= DATA_IN_CTS_D2P25_N and (not DATA_IN_CTS_D3);
SDRAM_DQ_CE <= DATA_IN_CTS_D2 or DATA_IN_CTS_D3;
SDRAM_DQZ <= not(DATA_IN_CTS_D2);

--// READ TRANSACTION -----------------------------------------------------
-- determine the boundaries of the expected read data burst, taking into account CL
-- and clock round-trip delay from FPGA to SDRAM and back. 

-- current command is a READ (unlike WRITE, no need for more complex advance notice).
READ_EVENT <= '1' when (COMMAND = READ) else '0';

-- extend READ_EVENT by one CLK
READ_EVENT_LONG_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		READ_EVENT_D <= READ_EVENT;
		READ_EVENT_LONG_D <= READ_EVENT or READ_EVENT_D;
		READ_EVENT_LONG_D2 <= READ_EVENT_LONG_D;
		READ_EVENT_LONG_D3 <= READ_EVENT_LONG_D2;
		READ_EVENT_LONG_D4 <= READ_EVENT_LONG_D3;
		READ_EVENT_LONG_D5 <= READ_EVENT_LONG_D4;
		READ_EVENT_LONG_D6 <= READ_EVENT_LONG_D5;
		READ_EVENT_LONG_D7 <= READ_EVENT_LONG_D6;
		READ_EVENT_LONG_D8 <= READ_EVENT_LONG_D7;
		READ_EVENT_LONG_D9 <= READ_EVENT_LONG_D8;
	end if;
end process;

RECLOCK_002b: process(CLK)
begin
	if falling_edge(CLK) then
		READ_EVENT_LONG_DF <= READ_EVENT or READ_EVENT_D;
		READ_EVENT_LONG_DF2 <= READ_EVENT_LONG_D;
		READ_EVENT_LONG_DF3 <= READ_EVENT_LONG_D2;
		READ_EVENT_LONG_DF4 <= READ_EVENT_LONG_D3;
		READ_EVENT_LONG_DF5 <= READ_EVENT_LONG_D4;
		READ_EVENT_LONG_DF6 <= READ_EVENT_LONG_D5;
		READ_EVENT_LONG_DF7 <= READ_EVENT_LONG_D6;
		READ_EVENT_LONG_DF8 <= READ_EVENT_LONG_D7;
		READ_EVENT_LONG_DF9 <= READ_EVENT_LONG_D8;
	end if;
end process;

-- read delay = CAS latency (CL) + RTDELAY_COARSE. 
-- fixed at synthesis.
READ_DELAY <= 2*CLOCK_CYCLES + RTDELAY_COARSE;

-- 1 CLK early version of READ_WINDOW
READ_WINDOW <= 
					READ_EVENT_LONG_D3 when (READ_DELAY = 6) else
					READ_EVENT_LONG_DF4 when (READ_DELAY = 7) else
					READ_EVENT_LONG_D4 when (READ_DELAY = 8) else
					READ_EVENT_LONG_DF5 when (READ_DELAY = 9) else
					READ_EVENT_LONG_D5 when (READ_DELAY = 10) else
					READ_EVENT_LONG_DF6 when (READ_DELAY = 11) else
					READ_EVENT_LONG_D6 when (READ_DELAY = 12) else
					READ_EVENT_LONG_DF7 when (READ_DELAY = 13) else
					READ_EVENT_LONG_D7 when (READ_DELAY = 14) else
					READ_EVENT_LONG_DF8 when (READ_DELAY = 15) else
					READ_EVENT_LONG_D8 when (READ_DELAY = 16) else
					READ_EVENT_LONG_DF9 when (READ_DELAY = 17) else
					READ_EVENT_LONG_D9 when (READ_DELAY = 18) else
					'0';

-- DATA_OUT is ready to be read.
DATA_OUT_EN_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		READ_WINDOW_D <= READ_WINDOW;
		READ_WINDOW_D2 <= READ_WINDOW_D;
	end if;
end process;

-- block fake reads during calibration
-- two cases: the optimum sampling clocks are different depending on whether 
-- RTDELAY_COARSE is even or odd multiple of 1/2 CLK period
DATA_OUT_EN <= 
		READ_WINDOW_D when (CAL_STATE = 4) and (2*(RTDELAY_COARSE/2) = RTDELAY_COARSE)  else 
		READ_WINDOW_D2 when (CAL_STATE = 4) and (2*(RTDELAY_COARSE/2) /= RTDELAY_COARSE)  else 
		'0';


--// RECEIVED DATA/DATA STROBE REALIGNMENT --------------------------------------
IODELAY2_001a: for I in 0 to (8*NSDRAM_BYTES - 1) generate   
	-- Control SDRAM_DQ data INPUT delay for best sampling time.
	-- Output SDRAM_DQ is not delayed.
	IODELAY2_GEN_S6A: if (PLATFORM = 6) generate
		IODELAY2_001b : IODELAY2
		generic map (
			COUNTER_WRAPAROUND => "STAY_AT_LIMIT", -- STAY_AT_LIMIT or WRAPAROUND
			DATA_RATE => "DDR",                 -- DDR (calibrate delay to 1 bit = 1/2 clock period)
			DELAY_SRC => "IDATAIN",             -- IO, ODATAIN or IDATAIN (output is not delayed)
			IDELAY_MODE => "NORMAL",            -- Unsupported
			IDELAY_TYPE => "VARIABLE_FROM_ZERO",-- FIXED, DEFAULT, VARIABLE_FROM_ZERO, VARIABLE_FROM_HALF_MAX or
															-- DIFF_PHASE_DETECTOR
			IDELAY_VALUE => 0,						-- Amount of input delay (0-255) 
			IDELAY2_VALUE => 0,                 -- Amount of secondary input Delay (0-255) 
			ODELAY_VALUE => 0,                	-- Amount of output delay (0-255)
			SERDES_MODE => "NONE",              -- NONE, MASTER or SLAVE
			SIM_TAPDELAY_VALUE => 50            -- Amount of delay used for simulation in pS
		)
		port map (
			-- IOB interface
			IDATAIN => SDRAM_DQ_IN(I), -- Data Signal from IOB
			DOUT => open,  				-- Delayed signal to IOB
			TOUT => open,  	 			-- Delayed Tristate signal to IOB
			-- logic interface
			ODATAIN => '0', 				-- Data from OLOGIC or OSERDES.
			DATAOUT => SDRAM_DQ_IN_D(I),-- Delayed data to D pin of ILOGIC2 or ISERDES2
			T => '1',              		-- Tristate control signal (input/output flow selection)
			DATAOUT2 => open, 	 		-- Secondary delayed data output. Unused.
			-- m&c
			CAL => IODELAY2_CAL,			-- Initiate calibration command
			BUSY => IODELAY2_DQ_BUSY(I),         		-- Busy after CAL. Unused.  Just for test purposes.
			CE => IODELAY2_CE(I/8),		-- 1-bit Enable increment/decrement
			CLK => CLK,           		-- Global clock input
			INC => IODELAY2_INC(I/8),  -- 1-bit Increment / Decrement input
			IOCLK0 => CLK,		 			-- primary clock input
			IOCLK1 => CLK_180,	 		-- secondary clock input/frequency doubler.
			RST => SYNC_RESET     		-- Reset to zero or 1/2 of total period
		);
	end generate;

	IODELAY_GEN_V5A: if (PLATFORM = 5) generate
   -- IODELAY: Input and/or Output Fixed/Variable Delay Element
   --          Virtex-5
   -- Xilinx HDL Language Template, version 13.4
   IODELAY_inst : IODELAY
		generic map (
			DELAY_SRC => "I", -- Specify which input port to be used
									-- "I"=IDATAIN, "O"=ODATAIN, "DATAIN"=DATAIN, "IO"=Bi-directional
			HIGH_PERFORMANCE_MODE => TRUE, -- TRUE specifies lower jitter
													 -- at expense of more power
			IDELAY_TYPE => "VARIABLE",  -- "FIXED" or "VARIABLE" 
			IDELAY_VALUE => 0,   -- 0 to 63 tap values
			ODELAY_VALUE => 0,   -- 0 to 63 tap values
			REFCLK_FREQUENCY => 200.0,   -- Frequency used for IDELAYCTRL
													-- 175.0 to 225.0
			SIGNAL_PATTERN => "DATA")    -- Input signal type, "CLOCK" or "DATA" 
		port map (
			DATAOUT => SDRAM_DQ_IN_D(I),-- 1-bit delayed data output
			C => CLK,           		-- 1-bit clock input
			DATAIN => '0', -- 1-bit internal data input
			IDATAIN => SDRAM_DQ_IN(I), -- Data Signal from IOB
			INC => IODELAY2_INC(I/8),  -- 1-bit Increment / Decrement input
			CE => IODELAY2_CE(I/8),		-- 1-bit Enable increment/decrement
			ODATAIN => '0', 				-- Data from OLOGIC or OSERDES.
			RST => SYNC_RESET,     		-- Reset to zero or 1/2 of total period
			T => '1'              		-- Tristate control signal (input/output flow selection)
		);
	end generate;

	--// Input DDR2
	IDDR2_GEN_S6A: if (PLATFORM = 6) generate
		-- IDDR2: Input Double Data Rate Input Register with Set, Reset
		--        and Clock Enable. 
		--        Spartan-6
		-- Xilinx HDL Language Template, version 11.4
		IDDR2_inst : IDDR2
		generic map(
			DDR_ALIGNMENT => "C0", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT_Q0 => '0', -- Sets initial state of the Q0 output to '0' or '1'
			INIT_Q1 => '0', -- Sets initial state of the Q1 output to '0' or '1'
			SRTYPE => "ASYNC") -- must be ASYNC in DDR mode with DDR_ALIGNMENT mode of C0 or C1
		port map (
			Q0 => DATA_OUT0(I + 8*NSDRAM_BYTES), -- 1-bit output captured with C0 clock
			Q1 => DATA_OUT0(I), -- 1-bit output captured with C1 clock
			C0 => CLK, -- 1-bit clock input
			C1 => CLK_180, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D => SDRAM_DQ_IN_D(I),   	-- 1-bit data input 
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	IDDR_GEN_V5A: if (PLATFORM = 5) generate
		-- IDDR: Double Data Rate Input Register with Set, Reset
		--       and Clock Enable. 
		--       Virtex-5
		-- Xilinx HDL Language Template, version 13.4

		IDDR_inst : IDDR 
		generic map (
			DDR_CLK_EDGE => "SAME_EDGE", -- "OPPOSITE_EDGE", "SAME_EDGE" 
														-- or "SAME_EDGE_PIPELINED" 
			INIT_Q1 => '0', -- Initial value of Q1: '0' or '1'
			INIT_Q2 => '0', -- Initial value of Q2: '0' or '1'
			SRTYPE => "ASYNC") -- Set/Reset type: "SYNC" or "ASYNC" 
		port map (
			Q1 => DATA_OUT0(I + 8*NSDRAM_BYTES), -- 1-bit output for positive edge of clock 
			Q2 => DATA_OUT0(I), -- 1-bit output for negative edge of clock
			C => CLK,   -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D => SDRAM_DQ_IN_D(I),   	-- 1-bit data input 
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
			);
	end generate;

	-- add another 1/2 CLK period delay when RTDELAY_COARSE is odd.
	DELAY_HALFPERIOD: process(CLK)
	begin
		if rising_edge(CLK) then
			DATA_OUT0_D(I + 8*NSDRAM_BYTES) <= DATA_OUT0(I + 8*NSDRAM_BYTES);
		end if;
	end process;
end generate;

ODD_EVEN_SEL: process(DATA_OUT0, DATA_OUT0_D)
begin
	for I in 0 to ((8*NSDRAM_BYTES) - 1) loop
		if (2*(RTDELAY_COARSE/2) = RTDELAY_COARSE) then
			-- even
			DATA_OUT(I) <= DATA_OUT0(I);
			DATA_OUT(I + 8*NSDRAM_BYTES) <= DATA_OUT0(I + 8*NSDRAM_BYTES);
		else
			-- 1/2 CLK delay.
			DATA_OUT(I) <= DATA_OUT0_D(I + 8*NSDRAM_BYTES);
			DATA_OUT(I + 8*NSDRAM_BYTES) <= DATA_OUT0(I);
		end if;
	end loop;
end process;



--// ROUND-TRIP DELAY CALIBRATION --------------------------------------
-- Calibration is performed in two steps:
-- (1) IODELAY calibration which normalizes the IODELAY2 maximum delay to one bit period. 
-- The trigger is IODELAY2_CAL. It ends when IODELAY2/BUSY goes low. 
-- (2) The RT delay cancellation algorithm then takes over and adjust the IODELAY2
-- so that the two data strobe pulses received from the DDR2 SDRAM during a burst read 
-- are aligned with CLK_90/CLK_270 after IODELAY2.
-- This calibration is performed only once at power up or reset.

-- for calibration purposes, delay the strobe signal from the DDR2 SDRAM by the same
-- amount as the incoming data through a IODELAY2 component. 
-- Then route the resulting signal out for comparison with reference clock
IODELAY2_002: for I in 0 to (NSDRAM_BYTES - 1) generate   
	IODELAY2_GEN_S6B: if (PLATFORM = 6) generate
		IODELAY2_002b : IODELAY2
		generic map (
			COUNTER_WRAPAROUND => "STAY_AT_LIMIT", -- STAY_AT_LIMIT or WRAPAROUND
			DATA_RATE => "DDR",                 -- DDR (calibrate delay to 1 bit = 1/2 clock period)
			DELAY_SRC => "IDATAIN",             -- IO, ODATAIN or IDATAIN (output is not delayed)
			IDELAY_MODE => "NORMAL",            -- Unsupported
			IDELAY_TYPE => "VARIABLE_FROM_ZERO",             -- FIXED, DEFAULT, VARIABLE_FROM_ZERO, VARIABLE_FROM_HALF_MAX or
															-- DIFF_PHASE_DETECTOR
			IDELAY_VALUE => 0,						-- Amount of input delay (0-255) 
			IDELAY2_VALUE => 0,                 -- Amount of secondary input Delay (0-255) 
			ODELAY_VALUE => 0,                	-- Amount of output delay (0-255)
			SERDES_MODE => "NONE",              -- NONE, MASTER or SLAVE
			SIM_TAPDELAY_VALUE => 50            -- Amount of delay used for simulation in pS
		)
		port map (
			-- IOB interface
			IDATAIN => SDRAM_DQS_IN(I), -- Data Signal from IOB
			DOUT => open,  				-- Delayed signal to IOB
			TOUT => open,  	 			-- Delayed Tristate signal to IOB
			-- logic interface
			ODATAIN => '0', 				-- Data from OLOGIC or OSERDES.
			DATAOUT => SDRAM_DQS_IN_D(I),-- Delayed data to D pin of ILOGIC2 or ISERDES2
			T => '1',      				-- Tristate control signal (input/output flow selection)
			DATAOUT2 => open, 	 		-- Secondary delayed data output. Unused.
			-- m&c
			CAL => IODELAY2_CAL,   		-- Initiate calibration command
			BUSY => IODELAY2_DQS_BUSY(I),  -- Busy after CAL
			CE => IODELAY2_CE(I),      -- 1-bit Enable increment/decrement
			CLK => CLK,           		-- Global clock input
			INC => IODELAY2_INC(I),    -- 1-bit Increment / Decrement input
			IOCLK0 => CLK_90,			 	-- primary clock input
			IOCLK1 => CLK_270,	 			-- secondary clock input/frequency doubler. Needed for DDR.
			RST => SYNC_RESET     		-- Reset to zero or 1/2 of total period
		);
	end generate;
	IODELAY_GEN_V5B: if (PLATFORM = 5) generate
   -- IODELAY: Input and/or Output Fixed/Variable Delay Element
   --          Virtex-5
   -- Xilinx HDL Language Template, version 13.4
   IODELAY_inst : IODELAY
		generic map (
			DELAY_SRC => "I", -- Specify which input port to be used
									-- "I"=IDATAIN, "O"=ODATAIN, "DATAIN"=DATAIN, "IO"=Bi-directional
			HIGH_PERFORMANCE_MODE => TRUE, -- TRUE specifies lower jitter
													 -- at expense of more power
			IDELAY_TYPE => "VARIABLE",  -- "FIXED" or "VARIABLE" 
			IDELAY_VALUE => 0,   -- 0 to 63 tap values
			ODELAY_VALUE => 0,   -- 0 to 63 tap values
			REFCLK_FREQUENCY => 200.0,   -- Frequency used for IDELAYCTRL
													-- 175.0 to 225.0
			SIGNAL_PATTERN => "DATA")    -- Input signal type, "CLOCK" or "DATA" 
		port map (
			DATAOUT => SDRAM_DQS_IN_D(I),-- Delayed data to D pin of ILOGIC2 or ISERDES2
			C => CLK,           		-- 1-bit clock input
			DATAIN => '0', -- 1-bit internal data input
			IDATAIN => SDRAM_DQS_IN(I), -- Data Signal from IOB
			INC => IODELAY2_INC(I),    -- 1-bit Increment / Decrement input
			CE => IODELAY2_CE(I),      -- 1-bit Enable increment/decrement
			ODATAIN => '0', 				-- Data from OLOGIC or OSERDES.
			RST => SYNC_RESET,     		-- Reset to zero or 1/2 of total period
			T => '1'              		-- Tristate control signal (input/output flow selection)
		);
	end generate;
	
	--// Input DDR2
	IDDR2_GEN_S6B: if (PLATFORM = 6) generate
		-- IDDR2: Input Double Data Rate Input Register with Set, Reset
		--        and Clock Enable. 
		--        Spartan-6
		-- Xilinx HDL Language Template, version 11.4
		
		IDDR2_inst : IDDR2
		generic map(
			DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT_Q0 => '0', -- Sets initial state of the Q0 output to '0' or '1'
			INIT_Q1 => '0', -- Sets initial state of the Q1 output to '0' or '1'
			SRTYPE => "ASYNC") -- must be ASYNC in DDR mode with DDR_ALIGNMENT mode of C0 or C1
		port map (
			Q0 => S_90_2(I), -- 1-bit output captured with C0 clock
			Q1 => S_270_2(I), -- 1-bit output captured with C1 clock
			C0 => CLK_90, 	-- 1-bit clock input
			C1 => CLK_270, -- 1-bit clock input
			CE => '1',  	-- 1-bit clock enable input
			D => SDRAM_DQS_IN_D(I),   	-- 1-bit data input 
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	IDDR_GEN_V5B: if (PLATFORM = 5) generate
		-- IDDR: Double Data Rate Input Register with Set, Reset
		--       and Clock Enable. 
		--       Virtex-5
		-- Xilinx HDL Language Template, version 13.4

		IDDR_inst : IDDR 
		generic map (
			DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE", "SAME_EDGE" 
														-- or "SAME_EDGE_PIPELINED" 
			INIT_Q1 => '0', -- Initial value of Q1: '0' or '1'
			INIT_Q2 => '0', -- Initial value of Q2: '0' or '1'
			SRTYPE => "ASYNC") -- Set/Reset type: "SYNC" or "ASYNC" 
		port map (
			Q1 => S_90_2(I), -- 1-bit output captured with C0 clock
			Q2 => S_270_2(I), -- 1-bit output captured with C1 clock
			C => CLK_90, 	-- 1-bit clock input
			CE => '1',  	-- 1-bit clock enable input
			D => SDRAM_DQS_IN_D(I),   	-- 1-bit data input 
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
			);
	end generate;
end generate;


--// PHYSICAL INTERFACE DDR2 -----------------------------------------------
-- Define I/O standard in constraint file as needed

-- 1. clock output  -------------------------
OBUFDS_CLK_001: for I in 0 to (NCLKS -1) generate  
	-- It is recommended to only use a BUFG resource to drive clock loads.
	-- It is recommended to use a Clock Forwarding technique to create a reliable and repeatable 
	-- low skew solution, i.e. use ODDR2 to regenerate the output clock.
	ODDR2_GEN_S6A: if (PLATFORM = 6) generate
		ODDR2_inst : ODDR2
		generic map(
			DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
		port map (
			Q => CLK_90_OUT(I), -- 1-bit output data
			C0 => CLK_90, -- 1-bit clock input
			C1 => CLK_270, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D0 => '1',   -- 1-bit data input (associated with C0)
			D1 => '0',   -- 1-bit data input (associated with C1)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	ODDR_GEN_V5A: if (PLATFORM = 5) generate
		-- ODDR: Output Double Data Rate Output Register with Set, Reset
		--       and Clock Enable. 
		--       Virtex-5
		-- Xilinx HDL Language Template, version 13.4
		
		ODDR_inst : ODDR
		generic map(
			DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE" 
			INIT => '0',   -- Initial value for Q port ('1' or '0')
			SRTYPE => "SYNC") -- Reset Type ("ASYNC" or "SYNC")
		port map (
			Q => CLK_90_OUT(I), -- 1-bit output data
			C => CLK_90, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D1 => '1',   -- 1-bit data input (positive edge)
			D2 => '0',   -- 1-bit data input (negative edge)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;
	
	OBUFDS_001 : OBUFDS
--	generic map (
--		IOSTANDARD => "DIFF_SSTL18_II")
	port map (
		O => SDRAM_CK_P(I),     -- Diff_p output (connect directly to top-level port)
		OB => SDRAM_CK_N(I),   -- Diff_n output (connect directly to top-level port)
		I => CLK_90_OUT(I)      -- Buffer input 
	);
end generate;

-- 2. SDRAM_DQ tri-stated I/O -------------------------
DATAIO: for I in 0 to (8*NSDRAM_BYTES -1) generate   
	ODDR2_GEN_S6B: if (PLATFORM = 6) generate
		DIO_001 : ODDR2
		generic map(
			DDR_ALIGNMENT => "C0", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "ASYNC") -- must be ASYNC in DDR mode with DDR_ALIGNMENT mode of C0 or C1
		port map (
			Q => SDRAM_DQ_OUT(I), 	-- 1-bit output data
			C0 => CLK, 		-- 1-bit clock input
			C1 => CLK_180, 	-- 1-bit clock input
			CE => SDRAM_DQ_CE,  	-- 1-bit clock enable input (don't waste power when not writing) 
			D0 => DATA_IN(I),   -- 1-bit data input (associated with C0)
			D1 => DATA_IN(I + 8*NSDRAM_BYTES),   -- 1-bit data input (associated with C1)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	ODDR_GEN_V5B: if (PLATFORM = 5) generate
		-- ODDR: Output Double Data Rate Output Register with Set, Reset
		--       and Clock Enable. 
		--       Virtex-5
		-- Xilinx HDL Language Template, version 13.4
		
		ODDR_inst : ODDR
		generic map(
			DDR_CLK_EDGE => "SAME_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE" 
			INIT => '0',   -- Initial value for Q port ('1' or '0')
			SRTYPE => "ASYNC") -- Reset Type ("ASYNC" or "SYNC")
		port map (
			Q => SDRAM_DQ_OUT(I), 	-- 1-bit DDR output
			C => CLK, 		-- 1-bit clock input
			CE => SDRAM_DQ_CE,  	-- 1-bit clock enable input (don't waste power when not writing) 
			D1 => DATA_IN(I),   -- 1-bit data input (positive edge)
			D2 => DATA_IN(I + 8*NSDRAM_BYTES),   -- 1-bit data input (negative edge)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	-- The 3-state enable (T-control pin) must also use an ODDR2 primitive configured
	-- in the same mode as the ODDR2 primitive used for data output (Xilinx ug381).
	ODDR2_GEN_S6C: if (PLATFORM = 6) generate
		DIO_002 : ODDR2
		generic map(
			DDR_ALIGNMENT => "C0", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "ASYNC") -- must be ASYNC in DDR mode with DDR_ALIGNMENT mode of C0 or C1
		port map (
			Q => SDRAM_DQZ_OUT(I), 	-- 1-bit output data
			C0 => CLK, 				-- 1-bit clock input
			C1 => CLK_180, 		-- 1-bit clock input
			CE => '1',  			-- 1-bit clock enable input 
			D0 => SDRAM_DQZ,   	-- 1-bit data input (associated with C0)
			D1 => SDRAM_DQZ,   	-- 1-bit data input (associated with C1)
			R => '0',    	-- 1-bit reset input
			S => '0'     			-- 1-bit set input
		);
	end generate;

	ODDR_GEN_V5C: if (PLATFORM = 5) generate
		-- ODDR: Output Double Data Rate Output Register with Set, Reset
		--       and Clock Enable. 
		--       Virtex-5
		-- Xilinx HDL Language Template, version 13.4
		
		ODDR_inst : ODDR
		generic map(
			DDR_CLK_EDGE => "SAME_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE" 
			INIT => '0',   -- Initial value for Q port ('1' or '0')
			SRTYPE => "ASYNC") -- Reset Type ("ASYNC" or "SYNC")
		port map (
			Q => SDRAM_DQZ_OUT(I), 	-- 1-bit output data
			C => CLK, 				-- 1-bit clock input
			CE => '1',  			-- 1-bit clock enable input 
			D1 => SDRAM_DQZ,   	-- 1-bit data input (positive edge)
			D2 => SDRAM_DQZ,   	-- 1-bit data input (negative edge)
			R => '0',    	-- 1-bit reset input
			S => '0'     			-- 1-bit set input
		);
	end generate;


	DIO_003 : IOBUF
--	generic map (
--	--	DRIVE => 12,
--	--	SLEW => "SLOW",
--		IOSTANDARD => "SSTL18_II"
--	)
	port map (
		O => SDRAM_DQ_IN(I),     	-- Buffer output
		IO => SDRAM_DQ(I),   		-- Buffer inout port (connect directly to top-level port)
		I => SDRAM_DQ_OUT(I),     	-- Buffer input
		T => SDRAM_DQZ_OUT(I)      -- 3-state enable input, high=input, low=output 
	);
end generate;

SIM_001: if(SIMULATION = '1') generate
	SDRAM_DQ_DIR <= SDRAM_DQZ_OUT(0); -- primarily for simulation use
end generate;

-- 3. DRAM_DQS tri-stated differential I/O -------------------------
DATASTROBEIO: for I in 0 to (NSDRAM_BYTES - 1) generate   
	-- It is recommended to only use a BUFG resource to drive clock loads.
	-- It is recommended to use a Clock Forwarding technique to create a reliable and repeatable 
	-- low skew solution, i.e. use ODDR2 to regenerate the output clock.
	ODDR2_GEN_S6D: if (PLATFORM = 6) generate
		DSIO_001 : ODDR2
		generic map(
			DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
		port map (
			Q => CLK_90_DQS_OUT(I), -- 1-bit output data
			C0 => CLK_90, -- 1-bit clock input
			C1 => CLK_270, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D0 => '1',   -- 1-bit data input (associated with C0)
			D1 => '0',   -- 1-bit data input (associated with C1)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
		
		-- The 3-state enable (T-control pin) must also use an ODDR2 primitive configured
		-- in the same mode as the ODDR2 primitive used for data output (Xilinx ug381).
		DSIO_002 : ODDR2
		generic map(
			DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
		port map (
			Q => SDRAM_DQSZ_OUT(I), -- 1-bit output data
			C0 => CLK_90, -- 1-bit clock input
			C1 => CLK_270, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D0 => SDRAM_DQSZ,   -- 1-bit data input (associated with C0)
			D1 => SDRAM_DQSZ,   -- 1-bit data input (associated with C1)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	ODDR_GEN_V5D: if (PLATFORM = 5) generate
		-- ODDR: Output Double Data Rate Output Register with Set, Reset
		--       and Clock Enable. 
		--       Virtex-5
		-- Xilinx HDL Language Template, version 13.4
		
		DSIO_001 : ODDR
		generic map(
			DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE" 
			INIT => '0',   -- Initial value for Q port ('1' or '0')
			SRTYPE => "SYNC") -- Reset Type ("ASYNC" or "SYNC")
		port map (
			Q => CLK_90_DQS_OUT(I), -- 1-bit output data
			C => CLK_90, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D1 => '1',   -- 1-bit data input (positive edge)
			D2 => '0',   -- 1-bit data input (negative edge)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);

		-- The 3-state enable (T-control pin) must also use an ODDR2 primitive configured
		-- in the same mode as the ODDR2 primitive used for data output (Xilinx ug381).
		DSIO_002 : ODDR
		generic map(
			DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE" 
			INIT => '0',   -- Initial value for Q port ('1' or '0')
			SRTYPE => "SYNC") -- Reset Type ("ASYNC" or "SYNC")
		port map (
			Q => SDRAM_DQSZ_OUT(I), -- 1-bit output data
			C => CLK_90, -- 1-bit clock input
			CE => '1',  -- 1-bit clock enable input
			D1 => SDRAM_DQSZ,   -- 1-bit data input (positive edge)
			D2 => SDRAM_DQSZ,   -- 1-bit data input (negative edge)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;

	IOBUFDS_DATA_STROBE_003 : IOBUFDS
	generic map (
		IOSTANDARD => "DIFF_SSTL18_II")
	port map (
		O => SDRAM_DQS_IN(I),     	-- Buffer output
		IO => SDRAM_DQS_P(I),   	-- Diff_p inout (connect directly to top-level port)
		IOB => SDRAM_DQS_N(I), 		-- Diff_n inout (connect directly to top-level port)
		I => CLK_90_DQS_OUT(I),    -- Buffer input
		T => SDRAM_DQSZ_OUT(I)      -- 3-state enable input, high=input, low=output
	);
	
end generate;

SIM_002: if(SIMULATION = '1') generate
	SDRAM_DQS_DIR <= SDRAM_DQSZ_OUT(0);	-- primarily for simulation use
end generate;
--//------- ROUND TRIP DELAY CALIBRATION ----------------------------------------------
-- The objective is to find out the best input delay to sample the (delayed) read data
-- at the optimum time (i.e. in the middle of the data bit).

-- The algorithm's first step is to build a time-of-arrival discriminator with a large output change when 
-- the delayed input data strobe is sampled at the optimum sampling instant. 
-- Resampling SDRAM_DQS_IN_D slightly too early will generate a 0101 pattern whereas 
-- resampling slightly too late will generate a 1010 pattern. 
-- This happens when resampling SDRAM_DQS_IN_D with CLK_90 and CLK_270 in the read window READ_WINDOW 
-- (for a read burst length of 4 words). 

CALIBRATION_001: for I in 0 to (NSDRAM_BYTES - 1) generate   
	CALIBRATION_001a: process(CLK, S_90_2)
	begin
		if rising_edge(CLK) then
			S_90_1(I) <= S_90_2(I);
			S_90_0(I) <= S_90_1(I);
		end if;
	end process;

	CALIBRATION_001b: process(CLK, S_270_2)
	begin
		if falling_edge(CLK) then
			S_270_1(I) <= S_270_2(I);
			S_270_0(I) <= S_270_1(I);
		end if;
	end process;

	-- two cases: the early/late characteristics are different depending on whether 
	-- RTDELAY_COARSE is even or odd multiple of 1/2 CLK period
	
	RTDELAY_COARSE_EVEN: if (2*(RTDELAY_COARSE/2) = RTDELAY_COARSE) generate
		-- discriminator output. Best situation is when we go from TOA early to TOA late. Sweet spot.
		-- reclock with CLK
		TOA_LATE(I) <= '1' when (S_90_0(I) = '0') and (S_90_1(I) = '0') and (S_270_0(I) = '1') and (S_270_1(I) = '1') else '0';
	end generate;

	RTDELAY_COARSE_ODD: if (2*(RTDELAY_COARSE/2) /= RTDELAY_COARSE) generate
		-- discriminator output. Best situation is when we go from TOA early to TOA late. Sweet spot.
		-- reclock with CLK
		TOA_LATE(I) <= '1' when (S_90_1(I) = '1') and (S_90_2(I) = '1') and (S_270_0(I) = '0') and (S_270_1(I) = '0') else '0';
	end generate;

		-- The self-calibration algorithm next step is to increase/decrease the input delay around 
		-- a center value where the discriminator detects a large output change from TOA early to TOA late
		-- The same delay is applied to the input data and input data strobe.
		DELAY_INC_GEN: process(CLK)
		begin
			if rising_edge(CLK) then
				if(CAL_STATE = 2) and (TOA_SAMPLE_CLK = '1') and (TOA_LATE(I) = '1') then
					-- decrease delay until we reach a satisfactory timing
					IODELAY2_CE(I) <= '1';
					IODELAY2_INC(I) <= '0';	-- decrement IODELAY2
				elsif(CAL_STATE = 2) and (TOA_SAMPLE_CLK = '1') then
					-- increase delay until we reach a satisfactory timing
					-- Always increase delay unless it is clear decreasing is required (solves some start conditions)
					IODELAY2_CE(I) <= '1';
					IODELAY2_INC(I) <= '1';	-- increment IODELAY2
				else
					IODELAY2_CE(I) <= '0';
				end if;
			end if;
		end process;

end generate;

-- Assess TOA_LATE(I) at the end of the READ_WINDOW
TOA_SAMPLE_CLK <= '1' when ((READ_WINDOW = '0') and (READ_WINDOW_D = '1')) else '0';

-- trigger the IODELAY2 calibration once at power up or after reset. Trigger upon entering state 16
IODELAY2_TRIGGER1: process (CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			CAL_STATE <= "000";
			NCALBURSTREADS <= (others => '0');
		elsif (CAL_STATE = 0) and (EVENT1 = '1') then
			-- DDR2 SDRAM is configured. Start IODELAY2's internal calibration. 
			CAL_STATE <= "001";	-- trigger IODELAY2_CAL
		elsif (CAL_STATE = 1) and (IODELAY2_DQS_BUSY(0) = '0') then
			-- completed IODELAY2 internal calibration. Start the calibration 2nd part.
			-- trigger the second part of the round-trip calibration. For 256 burst read cycles, 
			-- increment/decrement the IODELAY2 so that the delayed data strobe pulses from the DDR2 SDRAM
			-- are aligned with CLK_90/CLK_270 after IODELAY2.
			CAL_STATE <= "010";
		elsif (CAL_STATE = 2) and (TIMER3_EXPIRED = '1') and (NCALBURSTREADS(8) = '0') then
			-- another burst read transaction request
			NCALBURSTREADS <= NCALBURSTREADS + 1;
		elsif (CAL_STATE = 2) and (NCALBURSTREADS(8) = '1') and (SIMULATION = '0') then
			-- completed 256 burst read cycles. RT calibration is nearly complete. wait for last read to complete
			CAL_STATE <= "011";
		elsif (CAL_STATE = 2) and (NCALBURSTREADS(4) = '1') and (SIMULATION = '1') then
			-- (during simulation, shorter calibration) completed 16 burst read cycles. RT calibration is nearly complete. wait for last read to complete
			CAL_STATE <= "011";
		elsif (CAL_STATE = 3) and (TIMER3_EXPIRED = '1') then
			CAL_STATE <= "100";
		end if;
	end if;
end process;

IODELAY2_TRIGGER2: process (CLK)
begin
	if rising_edge(CLK) then
		if (CAL_STATE = 0) and (EVENT1 = '1') then
			-- DDR2 SDRAM is configured. Start IODELAY2's internal calibration. 
			IODELAY2_CAL <= '1' ;
		else
			IODELAY2_CAL <= '0' ;
		end if;
	end if;
end process;

SDRAM_READY_LOCAL <= '1' when (CAL_STATE = 4) else '0';
SDRAM_READY <= SDRAM_READY_LOCAL;
	-- '1' when the configuration and calibration phases are complete. 
	-- The SDRAM can now be used for read/write transactions.

--//------- TEST POINTS ----------------------------------------------
--SDRAM_TP(1) <= READ_WINDOW;
--SDRAM_TP(2) <= SDRAM_DQ_IN(0);
--SDRAM_TP(3) <= '1' when (COMMAND = READ) else '0';
--SDRAM_TP(4) <= '1' when (COMMAND = WRITE) else '0';
--SDRAM_TP(5) <= SDRAM_DQS_N_IN(0);
--SDRAM_TP(6) <= SYNC_RESET;
--SDRAM_TP(7) <= DCM_LOCKED;
--SDRAM_TP(8) <= SDRAM_DQ_IN(1);
--SDRAM_TP(9) <= SDRAM_DQ_IN(2);
--SDRAM_TP(10) <= SDRAM_DQSZ;
 
	

end behavior;