-------------------------------------------------------------------------------
--
-- Copyright (C) 2011
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-------------------------------------------------------------------------------

LIBRARY IEEE, common_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE IEEE.NUMERIC_STD.ALL;     

PACKAGE ddr3_pkg IS  
  
  -- DDR3 (definitions similar as in ug_altmemphy.pdf)
  TYPE t_c_ddr3_phy IS RECORD
    a_w                               : NATURAL;  -- = 16;
    a_row_w                           : NATURAL;  -- = 16;  --  = a_w, row address width, via a_w lines
    a_col_w                           : NATURAL;  -- = 10;  -- <= a_w, col address width, via a_w lines
    ba_w                              : NATURAL;  -- = 3;
    dq_w                              : NATURAL;  -- = 64;
    dqs_w                             : NATURAL;  -- = 8;   --  = dq_w / nof_dq_per_dqs;
    dm_w                              : NATURAL;  -- = 8;
    cs_w                              : NATURAL;  -- = 2;
    clk_w                             : NATURAL;  -- = 2;
  END RECORD;

  CONSTANT c_ddr3_phy                 : t_c_ddr3_phy := (16, 16, 10, 3, 64, 8, 8, 2, 2);
  CONSTANT c_ddr3_phy_4g              : t_c_ddr3_phy := (15, 15, 10, 3, 64, 8, 8, 2, 2);

  TYPE t_ddr3_phy_in IS RECORD  
    evt              : STD_LOGIC;
    oct_rup          : STD_LOGIC;
    oct_rdn          : STD_LOGIC;
    nc               : STD_LOGIC;   -- not connected, needed to be able to initialize constant record which has to have more than one field in VHDL
  END RECORD;
  
  TYPE t_ddr3_phy_io IS RECORD -- Do not use this type in Quartus! Use the _sel version instead.
    dq               : STD_LOGIC_VECTOR(c_ddr3_phy.dq_w-1 DOWNTO 0);   -- data bus
    dqs              : STD_LOGIC_VECTOR(c_ddr3_phy.dqs_w-1 DOWNTO 0);  -- data strobe bus
    dqs_n            : STD_LOGIC_VECTOR(c_ddr3_phy.dqs_w-1 DOWNTO 0);
    clk              : STD_LOGIC_VECTOR(c_ddr3_phy.clk_w-1 DOWNTO 0);  -- clock, positive edge clock
    clk_n            : STD_LOGIC_VECTOR(c_ddr3_phy.clk_w-1 DOWNTO 0);  -- clock, negative edge clock
    scl              : STD_LOGIC;                                      -- I2C
    sda              : STD_LOGIC;
  END RECORD;
  
  TYPE t_ddr3_phy_ou IS RECORD  
    a                : STD_LOGIC_VECTOR(c_ddr3_phy.a_w-1 DOWNTO 0);    -- row and column address
    ba               : STD_LOGIC_VECTOR(c_ddr3_phy.ba_w-1 DOWNTO 0);   -- bank address
    dm               : STD_LOGIC_VECTOR(c_ddr3_phy.dm_w-1 DOWNTO 0);   -- data mask bus
    cas_n            : STD_LOGIC; --_VECTOR(0 DOWNTO 0);                   -- column address strobe
    ras_n            : STD_LOGIC; --_VECTOR(0 DOWNTO 0);                   -- row address strobe
    we_n             : STD_LOGIC; --_VECTOR(0 DOWNTO 0);                   -- write enable signal
    reset_n          : STD_LOGIC;                                      -- reset signal
    odt              : STD_LOGIC_VECTOR(c_ddr3_phy.cs_w-1 DOWNTO 0);   -- on-die termination control signal
    cke              : STD_LOGIC_VECTOR(c_ddr3_phy.cs_w-1 DOWNTO 0);   -- clock enable
    cs_n             : STD_LOGIC_VECTOR(c_ddr3_phy.cs_w-1 DOWNTO 0);   -- chip select
  END RECORD;

  CONSTANT c_ddr3_phy_in_rst   : t_ddr3_phy_in := ('0', 'X', 'X', 'X');
  CONSTANT c_ddr3_phy_io_rst   : t_ddr3_phy_io := ((OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), '0', '0');
  CONSTANT c_ddr3_phy_ou_rst   : t_ddr3_phy_ou := ((OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), '0', '0', '0', '0', (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'));

  TYPE t_ddr3_phy_in_arr IS ARRAY(NATURAL RANGE <>) OF t_ddr3_phy_in;
  TYPE t_ddr3_phy_io_arr IS ARRAY(NATURAL RANGE <>) OF t_ddr3_phy_io; 
  TYPE t_ddr3_phy_ou_arr IS ARRAY(NATURAL RANGE <>) OF t_ddr3_phy_ou;

  TYPE t_ddr3_addr IS RECORD 
    chip      : STD_LOGIC_VECTOR(ceil_log2(c_ddr3_phy.cs_w)  -1 DOWNTO 0);  -- Note: The controller interprets the chip address as logical address (NOT individual chip sel lines), hence ceil_log2
    bank      : STD_LOGIC_VECTOR(          c_ddr3_phy.ba_w   -1 DOWNTO 0);
    row       : STD_LOGIC_VECTOR(          c_ddr3_phy.a_row_w-1 DOWNTO 0); 
    column    : STD_LOGIC_VECTOR(          c_ddr3_phy.a_col_w-1 DOWNTO 0);
  END RECORD;

  TYPE t_ddr3_addr_arr IS ARRAY(NATURAL RANGE <>) OF t_ddr3_addr; 

  CONSTANT c_ddr3_ctlr_data_w           : NATURAL := 256;  -- = 64 (PHY dq width) * 2 (use both PHY clock edges) * 2 (PHY transfer at double rate)
  CONSTANT c_ddr3_ctlr_rsl              : NATURAL := c_ddr3_ctlr_data_w / c_ddr3_phy.dq_w; -- =4
  CONSTANT c_ddr3_ctlr_rsl_w            : NATURAL := ceil_log2(c_ddr3_ctlr_rsl); 
  CONSTANT c_ddr3_ctlr_maxburstsize     : NATURAL := 64;
  CONSTANT c_ddr3_ctlr_maxburstsize_w   : NATURAL := ceil_log2(c_ddr3_ctlr_maxburstsize+1);
  CONSTANT c_ddr3_ctrl_nof_latent_reads : NATURAL := 100;  -- The downside to having a command cue: even after de-asserting read requests, the ALTMEMPHY keeps processing your cued read requests. 
                                                           -- This makes sure 100 words are still available in the read FIFO after it de-asserted its siso.ready signal towards the ddr3 read side.

  CONSTANT c_ddr3_phy_oct_w             : NATURAL := 14;
  CONSTANT c_ddr3_phy_oct_rs            : STD_LOGIC_VECTOR := TO_UVEC(0, c_ddr3_phy_oct_w);
  CONSTANT c_ddr3_phy_oct_rt            : STD_LOGIC_VECTOR := TO_UVEC(0, c_ddr3_phy_oct_w);

  CONSTANT c_ddr3_addr_lo               : t_ddr3_addr := ((OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'),  (OTHERS=>'0'));
  CONSTANT c_ddr3_address_lo            : NATURAL := 0;

  CONSTANT c_ddr3_addr_hi_4gb           : t_ddr3_addr := ((OTHERS=>'1'), (OTHERS=>'1'), (OTHERS=>'1'),  TO_UVEC(2**c_ddr3_phy_4g.a_col_w - c_ddr3_ctlr_rsl, c_ddr3_phy_4g.a_col_w));

  -- 4 rows * 1024 cols  * 64 bits / 128bits per associative mem array element = 2048 = default ALTMEMPHY mem_model array depth.
  CONSTANT c_ddr3_addr_hi_sim           : t_ddr3_addr := ((OTHERS=>'0'), (OTHERS=>'0'), TO_UVEC(3, c_ddr3_phy.a_row_w),  TO_UVEC(2**c_ddr3_phy_4g.a_col_w - c_ddr3_ctlr_rsl, c_ddr3_phy_4g.a_col_w));
  CONSTANT c_ddr3_address_hi_sim        : NATURAL := 4092; --TB uses generated mem model with 2ki addresses of 128k - so the array holds 4096 64-bit words. End address is 4092 (resolution=4: last write=4092,4093,4094,4095.)
  
  TYPE t_ddr3_seq IS RECORD 
    wr_chunksize    : POSITIVE; -- := 64;
    wr_nof_chunks   : POSITIVE; -- := 1;
    rd_chunksize    : POSITIVE; -- := 16;
    rd_nof_chunks   : POSITIVE; -- := 4;
    gapsize         : NATURAL;  -- := 0;
    nof_blocks      : POSITIVE; -- := 5;   
  END RECORD;

  CONSTANT c_ddr3_seq : t_ddr3_seq := (64, 1, 16, 4, 0, 5);  
  
  -- Manually derived VHDL entity from Verilog module $RADIOHDL/libraries/technology/ip_stratixiv/ddr3_uphy_4g_800_master/generated/ip_stratixiv_ddr3_uphy_4g_800_master.v
  COMPONENT ip_stratixiv_ddr3_uphy_4g_800_master IS
  PORT (
    pll_ref_clk                : IN    STD_LOGIC;                       --  pll_ref_clk.clk
    global_reset_n             : IN    STD_LOGIC;                       -- global_reset.reset_n
    soft_reset_n               : IN    STD_LOGIC;                       --   soft_reset.reset_n
    afi_clk                    : OUT   STD_LOGIC;                       --      afi_clk.clk
    afi_half_clk               : OUT   STD_LOGIC;                       -- afi_half_clk.clk
    afi_reset_n                : OUT   STD_LOGIC;                       --    afi_reset.reset_n
    mem_a                      : OUT   STD_LOGIC_VECTOR(14 DOWNTO 0);   --       memory.mem_a
    mem_ba                     : OUT   STD_LOGIC_VECTOR(2 DOWNTO 0);    --             .mem_ba
    mem_ck                     : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_ck
    mem_ck_n                   : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_ck_n
    mem_cke                    : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_cke
    mem_cs_n                   : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_cs_n
    mem_dm                     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);    --             .mem_dm
    mem_ras_n                  : OUT   STD_LOGIC;                       --             .mem_ras_n
    mem_cas_n                  : OUT   STD_LOGIC;                       --             .mem_cas_n
    mem_we_n                   : OUT   STD_LOGIC;                       --             .mem_we_n
    mem_reset_n                : OUT   STD_LOGIC;                       --             .mem_reset_n
    mem_dq                     : INOUT STD_LOGIC_VECTOR(63 DOWNTO 0);   --             .mem_dq
    mem_dqs                    : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);    --             .mem_dqs
    mem_dqs_n                  : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);    --             .mem_dqs_n
    mem_odt                    : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_odt
    avl_ready                  : OUT   STD_LOGIC;                       --          avl.waitrequest_n
    avl_burstbegin             : IN    STD_LOGIC;                       --             .beginbursttransfer
    avl_addr                   : IN    STD_LOGIC_VECTOR(26 DOWNTO 0);   --             .address
    avl_rdata_valid            : OUT   STD_LOGIC;                       --             .readdatavalid
    avl_rdata                  : OUT   STD_LOGIC_VECTOR(255 DOWNTO 0);  --             .readdata
    avl_wdata                  : IN    STD_LOGIC_VECTOR(255 DOWNTO 0);  --             .writedata
    avl_be                     : IN    STD_LOGIC_VECTOR(31 DOWNTO 0);   --             .byteenable
    avl_read_req               : IN    STD_LOGIC;                       --             .read
    avl_write_req              : IN    STD_LOGIC;                       --             .write
    avl_size                   : IN    STD_LOGIC_VECTOR(6 DOWNTO 0);    --             .burstcount
    local_init_done            : OUT   STD_LOGIC;                       --       status.local_init_done
    local_cal_success          : OUT   STD_LOGIC;                       --             .local_cal_success
    local_cal_fail             : OUT   STD_LOGIC;                       --             .local_cal_fail
    oct_rdn                    : IN    STD_LOGIC;                       --          oct.rdn
    oct_rup                    : IN    STD_LOGIC;                       --             .rup
    seriesterminationcontrol   : OUT   STD_LOGIC_VECTOR(13 DOWNTO 0);   --  oct_sharing.seriesterminationcontrol
    parallelterminationcontrol : OUT   STD_LOGIC_VECTOR(13 DOWNTO 0);   --             .parallelterminationcontrol
    pll_mem_clk                : OUT   STD_LOGIC;                       --  pll_sharing.pll_mem_clk
    pll_write_clk              : OUT   STD_LOGIC;                       --             .pll_write_clk
    pll_write_clk_pre_phy_clk  : OUT   STD_LOGIC;                       --             .pll_write_clk_pre_phy_clk
    pll_addr_cmd_clk           : OUT   STD_LOGIC;                       --             .pll_addr_cmd_clk
    pll_locked                 : OUT   STD_LOGIC;                       --             .pll_locked
    pll_avl_clk                : OUT   STD_LOGIC;                       --             .pll_avl_clk
    pll_config_clk             : OUT   STD_LOGIC;                       --             .pll_config_clk
    dll_delayctrl              : OUT   STD_LOGIC_VECTOR(5 DOWNTO 0)     --  dll_sharing.dll_delayctrl
  );
  END COMPONENT;

  -- Manually derived VHDL entity from Verilog module $RADIOHDL/libraries/technology/ip_stratixiv/ddr3_uphy_4g_800_slave/generated/ip_stratixiv_ddr3_uphy_4g_800_slave.v
  -- . diff with master is that only master has oct_* inputs and that the *terminationcontrol are inputs for the slave
  COMPONENT ip_stratixiv_ddr3_uphy_4g_800_slave IS
  PORT (
    pll_ref_clk                : IN    STD_LOGIC;                       --  pll_ref_clk.clk
    global_reset_n             : IN    STD_LOGIC;                       -- global_reset.reset_n
    soft_reset_n               : IN    STD_LOGIC;                       --   soft_reset.reset_n
    afi_clk                    : OUT   STD_LOGIC;                       --      afi_clk.clk
    afi_half_clk               : OUT   STD_LOGIC;                       -- afi_half_clk.clk
    afi_reset_n                : OUT   STD_LOGIC;                       --    afi_reset.reset_n
    mem_a                      : OUT   STD_LOGIC_VECTOR(14 DOWNTO 0);   --       memory.mem_a
    mem_ba                     : OUT   STD_LOGIC_VECTOR(2 DOWNTO 0);    --             .mem_ba
    mem_ck                     : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_ck
    mem_ck_n                   : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_ck_n
    mem_cke                    : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_cke
    mem_cs_n                   : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_cs_n
    mem_dm                     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);    --             .mem_dm
    mem_ras_n                  : OUT   STD_LOGIC;                       --             .mem_ras_n
    mem_cas_n                  : OUT   STD_LOGIC;                       --             .mem_cas_n
    mem_we_n                   : OUT   STD_LOGIC;                       --             .mem_we_n
    mem_reset_n                : OUT   STD_LOGIC;                       --             .mem_reset_n
    mem_dq                     : INOUT STD_LOGIC_VECTOR(63 DOWNTO 0);   --             .mem_dq
    mem_dqs                    : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);    --             .mem_dqs
    mem_dqs_n                  : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);    --             .mem_dqs_n
    mem_odt                    : OUT   STD_LOGIC_VECTOR(1 DOWNTO 0);    --             .mem_odt
    avl_ready                  : OUT   STD_LOGIC;                       --          avl.waitrequest_n
    avl_burstbegin             : IN    STD_LOGIC;                       --             .beginbursttransfer
    avl_addr                   : IN    STD_LOGIC_VECTOR(26 DOWNTO 0);   --             .address
    avl_rdata_valid            : OUT   STD_LOGIC;                       --             .readdatavalid
    avl_rdata                  : OUT   STD_LOGIC_VECTOR(255 DOWNTO 0);  --             .readdata
    avl_wdata                  : IN    STD_LOGIC_VECTOR(255 DOWNTO 0);  --             .writedata
    avl_be                     : IN    STD_LOGIC_VECTOR(31 DOWNTO 0);   --             .byteenable
    avl_read_req               : IN    STD_LOGIC;                       --             .read
    avl_write_req              : IN    STD_LOGIC;                       --             .write
    avl_size                   : IN    STD_LOGIC_VECTOR(6 DOWNTO 0);    --             .burstcount
    local_init_done            : OUT   STD_LOGIC;                       --       status.local_init_done
    local_cal_success          : OUT   STD_LOGIC;                       --             .local_cal_success
    local_cal_fail             : OUT   STD_LOGIC;                       --             .local_cal_fail
    seriesterminationcontrol   : IN    STD_LOGIC_VECTOR(13 DOWNTO 0);   --  oct_sharing.seriesterminationcontrol
    parallelterminationcontrol : IN    STD_LOGIC_VECTOR(13 DOWNTO 0);   --             .parallelterminationcontrol
    pll_mem_clk                : OUT   STD_LOGIC;                       --  pll_sharing.pll_mem_clk
    pll_write_clk              : OUT   STD_LOGIC;                       --             .pll_write_clk
    pll_write_clk_pre_phy_clk  : OUT   STD_LOGIC;                       --             .pll_write_clk_pre_phy_clk
    pll_addr_cmd_clk           : OUT   STD_LOGIC;                       --             .pll_addr_cmd_clk
    pll_locked                 : OUT   STD_LOGIC;                       --             .pll_locked
    pll_avl_clk                : OUT   STD_LOGIC;                       --             .pll_avl_clk
    pll_config_clk             : OUT   STD_LOGIC;                       --             .pll_config_clk
    dll_delayctrl              : OUT   STD_LOGIC_VECTOR(5 DOWNTO 0)     --  dll_sharing.dll_delayctrl
  );
  END COMPONENT;

  
  COMPONENT alt_mem_if_ddr3_mem_model_top_ddr3_mem_if_dm_pins_en_mem_if_dqsn_en IS
	GENERIC (
		MEM_IF_ADDR_WIDTH            : INTEGER := 0;
		MEM_IF_ROW_ADDR_WIDTH        : INTEGER := 0;
		MEM_IF_COL_ADDR_WIDTH        : INTEGER := 0;
		MEM_IF_CS_PER_RANK           : INTEGER := 0;
		MEM_IF_CONTROL_WIDTH         : INTEGER := 0;
		MEM_IF_DQS_WIDTH             : INTEGER := 0;
		MEM_IF_CS_WIDTH              : INTEGER := 0;
		MEM_IF_BANKADDR_WIDTH        : INTEGER := 0;
		MEM_IF_DQ_WIDTH              : INTEGER := 0;
		MEM_IF_CK_WIDTH              : INTEGER := 0;
		MEM_IF_CLK_EN_WIDTH          : INTEGER := 0;
		DEVICE_WIDTH                 : INTEGER := 1;
		MEM_TRCD                     : INTEGER := 0;
		MEM_TRTP                     : INTEGER := 0;
		MEM_DQS_TO_CLK_CAPTURE_DELAY : INTEGER := 0;
		MEM_CLK_TO_DQS_CAPTURE_DELAY : INTEGER := 0;
		MEM_IF_ODT_WIDTH             : INTEGER := 0;
		MEM_MIRROR_ADDRESSING_DEC    : INTEGER := 0;
		MEM_REGDIMM_ENABLED          : BOOLEAN := FALSE;
		DEVICE_DEPTH                 : INTEGER := 1;
		MEM_GUARANTEED_WRITE_INIT    : BOOLEAN := FALSE;
		MEM_VERBOSE                  : BOOLEAN := TRUE;
		MEM_INIT_EN                  : BOOLEAN := FALSE;
		MEM_INIT_FILE                : STRING  := "";
		DAT_DATA_WIDTH               : INTEGER := 32
	);
	PORT (
		mem_a       : IN    STD_LOGIC_VECTOR(14 DOWNTO 0) := (OTHERS => 'X'); -- mem_a
		mem_ba      : IN    STD_LOGIC_VECTOR(2 DOWNTO 0)  := (OTHERS => 'X'); -- mem_ba
		mem_ck      : IN    STD_LOGIC_VECTOR(1 DOWNTO 0)  := (OTHERS => 'X'); -- mem_ck
		mem_ck_n    : IN    STD_LOGIC_VECTOR(1 DOWNTO 0)  := (OTHERS => 'X'); -- mem_ck_n
		mem_cke     : IN    STD_LOGIC_VECTOR(1 DOWNTO 0)  := (OTHERS => 'X'); -- mem_cke
		mem_cs_n    : IN    STD_LOGIC_VECTOR(1 DOWNTO 0)  := (OTHERS => 'X'); -- mem_cs_n
		mem_dm      : IN    STD_LOGIC_VECTOR(7 DOWNTO 0)  := (OTHERS => 'X'); -- mem_dm
		mem_ras_n   : IN    STD_LOGIC_VECTOR(0 DOWNTO 0)  := (OTHERS => 'X'); -- mem_ras_n
		mem_cas_n   : IN    STD_LOGIC_VECTOR(0 DOWNTO 0)  := (OTHERS => 'X'); -- mem_cas_n
		mem_we_n    : IN    STD_LOGIC_VECTOR(0 DOWNTO 0)  := (OTHERS => 'X'); -- mem_we_n
		mem_reset_n : IN    STD_LOGIC                     := 'X';             -- mem_reset_n
		mem_dq      : INOUT STD_LOGIC_VECTOR(63 DOWNTO 0) := (OTHERS => 'X'); -- mem_dq
		mem_dqs     : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0)  := (OTHERS => 'X'); -- mem_dqs
		mem_dqs_n   : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0)  := (OTHERS => 'X'); -- mem_dqs_n
		mem_odt     : IN    STD_LOGIC_VECTOR(1 DOWNTO 0)  := (OTHERS => 'X')  -- mem_odt
	);
	END COMPONENT alt_mem_if_ddr3_mem_model_top_ddr3_mem_if_dm_pins_en_mem_if_dqsn_en;
  
END ddr3_pkg;

PACKAGE BODY ddr3_pkg IS   

END ddr3_pkg;