--------------------------------------------------------------------------------
--
-- Copyright (C) 2015
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.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/>.
--
--------------------------------------------------------------------------------


-- Purpose: DDR4 memory access component for Arria10.
-- Description:
-- Remarks:
-- . The local_init_done goes high some time after power up. It could have been
--   AND-ed with ctlr_miso.waitrequest_n. However the timing closure for
--   ctlr_miso.waitrequest_n can be critical, so therefore it is better not
--   to combinatorially load it with the AND local_init_done. Instead a
--   ctlr_miso.done field was added and used to pass on local_init_done. In fact
--   for normal operation it is sufficient to only wait for
--   ctlr_miso.waitrequest_n. The ctlr_miso.init_done is then only used for
--   DDR interface monitoring purposes.

-- Declare IP libraries to ensure default binding in simulation. The IP library clause is ignored by synthesis.
library ip_arria10_ddr4_4g_1600_altera_emif_150;
library ip_arria10_ddr4_4g_2000_altera_emif_150;

library IEEE, technology_lib, common_lib;
use IEEE.std_logic_1164.all;
use common_lib.common_pkg.all;
use common_lib.common_mem_pkg.all;
use technology_lib.technology_pkg.all;
use work.tech_ddr_pkg.all;
use work.tech_ddr_component_pkg.all;

entity tech_ddr_arria10 is
  generic (
    g_tech_ddr   : t_c_tech_ddr
  );
  port (
    -- PLL reference clock
    ref_clk           : in    std_logic;
    ref_rst           : in    std_logic;

    -- Controller user interface
    ctlr_gen_clk      : out   std_logic;
    ctlr_gen_rst      : out   std_logic;

    ctlr_mosi         : in    t_mem_ctlr_mosi;
    ctlr_miso         : out   t_mem_ctlr_miso;

    -- PHY interface
    phy_in            : in    t_tech_ddr4_phy_in;
    phy_io            : inout t_tech_ddr4_phy_io;
    phy_ou            : out   t_tech_ddr4_phy_ou
  );
end tech_ddr_arria10;


architecture str of tech_ddr_arria10 is

  constant c_gigabytes             : integer := func_tech_ddr_module_gigabytes(g_tech_ddr);

  constant c_ctlr_address_w        : natural := 26;  -- func_tech_ddr_ctlr_address_w(g_tech_ddr);
  constant c_ctlr_data_w           : natural := 576;  -- func_tech_ddr_ctlr_data_w(   g_tech_ddr);

  signal i_ctlr_gen_clk            : std_logic;
  signal ref_rst_n                 : std_logic;
  signal ctlr_gen_rst_n            : std_logic := '0';

  signal local_cal_success         : std_logic;
  signal local_cal_fail            : std_logic;

begin

  ctlr_gen_clk <= i_ctlr_gen_clk;

  ref_rst_n    <= not ref_rst;
  ctlr_gen_rst <= not ctlr_gen_rst_n;

  gen_ip_arria10_ddr4_4g_2000 : if g_tech_ddr.name = "DDR4" and c_gigabytes = 4 and g_tech_ddr.mts = 2000 generate

    phy_ou.cs_n(1) <= '1';
    phy_ou.cke(1)  <= '0';
    phy_ou.odt(1)  <= '0';

    u_ip_arria10_ddr4_4g_2000 : ip_arria10_ddr4_4g_2000
    port map (
      amm_ready_0         => ctlr_miso.waitrequest_n,  -- ctrl_amm_avalon_slave_0.waitrequest_n
      amm_read_0          => ctlr_mosi.rd,  -- .read
      amm_write_0         => ctlr_mosi.wr,  -- .write
      amm_address_0       => ctlr_mosi.address(c_ctlr_address_w - 1 downto 0),  -- .address
      amm_readdata_0      => ctlr_miso.rddata(c_ctlr_data_w - 1 downto 0),  -- .readdata
      amm_writedata_0     => ctlr_mosi.wrdata(c_ctlr_data_w - 1 downto 0),  -- .writedata
      amm_burstcount_0    => ctlr_mosi.burstsize(g_tech_ddr.maxburstsize_w - 1 downto 0),  -- .burstcount
      amm_byteenable_0    => (others => '1'),  -- .byteenable
      amm_readdatavalid_0 => ctlr_miso.rdval,  -- .readdatavalid
      emif_usr_clk        => i_ctlr_gen_clk,  -- emif_usr_clk_clock_source.clk
      emif_usr_reset_n    => ctlr_gen_rst_n,  -- emif_usr_reset_reset_source.reset_n
      global_reset_n      => ref_rst_n,  -- global_reset_reset_sink.reset_n
      mem_ck              => phy_ou.ck(g_tech_ddr.ck_w - 1 downto 0),  -- mem_conduit_end.mem_ck
      mem_ck_n            => phy_ou.ck_n(g_tech_ddr.ck_w - 1 downto 0),  -- .mem_ck_n
      mem_a               => phy_ou.a(g_tech_ddr.a_w - 1 downto 0),  -- .mem_a
   sl(mem_act_n)          => phy_ou.act_n,  -- .mem_act_n
      mem_ba              => phy_ou.ba(g_tech_ddr.ba_w - 1 downto 0),  -- .mem_ba
      mem_bg              => phy_ou.bg(g_tech_ddr.bg_w - 1 downto 0),  -- .mem_bg
      mem_cke             => phy_ou.cke(g_tech_ddr.cke_w - 1 downto 0),  -- .mem_cke
      mem_cs_n            => phy_ou.cs_n(g_tech_ddr.cs_w - 1 downto 0),  -- .mem_cs_n
      mem_odt             => phy_ou.odt(g_tech_ddr.odt_w - 1 downto 0),  -- .mem_odt
   sl(mem_reset_n)        => phy_ou.reset_n,  -- .mem_reset_n
   sl(mem_par)            => phy_ou.par,  -- .mem_par
      mem_alert_n         => slv(phy_in.alert_n),  -- .mem_alert_n
      mem_dqs             => phy_io.dqs(g_tech_ddr.dqs_w - 1 downto 0),  -- .mem_dqs
      mem_dqs_n           => phy_io.dqs_n(g_tech_ddr.dqs_w - 1 downto 0),  -- .mem_dqs_n
      mem_dq              => phy_io.dq(g_tech_ddr.dq_w - 1 downto 0),  -- .mem_dq
      mem_dbi_n           => phy_io.dbi_n(g_tech_ddr.dbi_w - 1 downto 0),  -- .mem_dbi_n
      oct_rzqin           => phy_in.oct_rzqin,  -- oct_conduit_end.oct_rzqin
      pll_ref_clk         => ref_clk,  -- pll_ref_clk_clock_sink.clk
      local_cal_success   => local_cal_success,  -- status_conduit_end.local_cal_success
      local_cal_fail      => local_cal_fail  -- .local_cal_fail
    );

    -- Signals in DDR3 that are not available with DDR4:
    --
    --avl_burstbegin             => ctlr_mosi.burstbegin,                               --             .beginbursttransfer
    --   beginbursttransfer is obselete for new Avalon designs, because the slave can count valid data itself to know when a new burst starts
    --
    --local_init_done            => ctlr_miso.done,                                     --       status.local_init_done
    --   local_init_done = ctlr_init_done originally and mapped to ctlr_miso.done for the DDR3 IP. For the DDR4 IP the local_cal_success and
    --   NOT local_cal_fail seem  to serve as local_init_done

    ctlr_miso.done     <= local_cal_success and not local_cal_fail when rising_edge(i_ctlr_gen_clk);
    ctlr_miso.cal_ok   <= local_cal_success;
    ctlr_miso.cal_fail <= local_cal_fail;

  end generate;

  gen_ip_arria10_ddr4_4g_1600 : if g_tech_ddr.name = "DDR4" and c_gigabytes = 4 and g_tech_ddr.mts = 1600 generate

    phy_ou.cs_n(1) <= '1';
    phy_ou.cke(1)  <= '0';
    phy_ou.odt(1)  <= '0';

    u_ip_arria10_ddr4_4g_1600 : ip_arria10_ddr4_4g_1600
    port map (
      amm_ready_0         => ctlr_miso.waitrequest_n,  -- ctrl_amm_avalon_slave_0.waitrequest_n
      amm_read_0          => ctlr_mosi.rd,  -- .read
      amm_write_0         => ctlr_mosi.wr,  -- .write
      amm_address_0       => ctlr_mosi.address(c_ctlr_address_w - 1 downto 0),  -- .address
      amm_readdata_0      => ctlr_miso.rddata(c_ctlr_data_w - 1 downto 0),  -- .readdata
      amm_writedata_0     => ctlr_mosi.wrdata(c_ctlr_data_w - 1 downto 0),  -- .writedata
      amm_burstcount_0    => ctlr_mosi.burstsize(g_tech_ddr.maxburstsize_w - 1 downto 0),  -- .burstcount
      amm_byteenable_0    => (others => '1'),  -- .byteenable
      amm_readdatavalid_0 => ctlr_miso.rdval,  -- .readdatavalid
      emif_usr_clk        => i_ctlr_gen_clk,  -- emif_usr_clk_clock_source.clk
      emif_usr_reset_n    => ctlr_gen_rst_n,  -- emif_usr_reset_reset_source.reset_n
      global_reset_n      => ref_rst_n,  -- global_reset_reset_sink.reset_n
      mem_ck              => phy_ou.ck(g_tech_ddr.ck_w - 1 downto 0),  -- mem_conduit_end.mem_ck
      mem_ck_n            => phy_ou.ck_n(g_tech_ddr.ck_w - 1 downto 0),  -- .mem_ck_n
      mem_a               => phy_ou.a(g_tech_ddr.a_w - 1 downto 0),  -- .mem_a
   sl(mem_act_n)          => phy_ou.act_n,  -- .mem_act_n
      mem_ba              => phy_ou.ba(g_tech_ddr.ba_w - 1 downto 0),  -- .mem_ba
      mem_bg              => phy_ou.bg(g_tech_ddr.bg_w - 1 downto 0),  -- .mem_bg
      mem_cke             => phy_ou.cke(g_tech_ddr.cke_w - 1 downto 0),  -- .mem_cke
      mem_cs_n            => phy_ou.cs_n(g_tech_ddr.cs_w - 1 downto 0),  -- .mem_cs_n
      mem_odt             => phy_ou.odt(g_tech_ddr.odt_w - 1 downto 0),  -- .mem_odt
   sl(mem_reset_n)        => phy_ou.reset_n,  -- .mem_reset_n
   sl(mem_par)            => phy_ou.par,  -- .mem_par
      mem_alert_n         => slv(phy_in.alert_n),  -- .mem_alert_n
      mem_dqs             => phy_io.dqs(g_tech_ddr.dqs_w - 1 downto 0),  -- .mem_dqs
      mem_dqs_n           => phy_io.dqs_n(g_tech_ddr.dqs_w - 1 downto 0),  -- .mem_dqs_n
      mem_dq              => phy_io.dq(g_tech_ddr.dq_w - 1 downto 0),  -- .mem_dq
      mem_dbi_n           => phy_io.dbi_n(g_tech_ddr.dbi_w - 1 downto 0),  -- .mem_dbi_n
      oct_rzqin           => phy_in.oct_rzqin,  -- oct_conduit_end.oct_rzqin
      pll_ref_clk         => ref_clk,  -- pll_ref_clk_clock_sink.clk
      local_cal_success   => local_cal_success,  -- status_conduit_end.local_cal_success
      local_cal_fail      => local_cal_fail  -- .local_cal_fail
    );

    -- Signals in DDR3 that are not available with DDR4:
    --
    --avl_burstbegin             => ctlr_mosi.burstbegin,                               --             .beginbursttransfer
    --   beginbursttransfer is obselete for new Avalon designs, because the slave can count valid data itself to know when a new burst starts
    --
    --local_init_done            => ctlr_miso.done,                                     --       status.local_init_done
    --   local_init_done = ctlr_init_done originally and mapped to ctlr_miso.done for the DDR3 IP. For the DDR4 IP the local_cal_success and
    --   NOT local_cal_fail seem  to serve as local_init_done

    ctlr_miso.done     <= local_cal_success and not local_cal_fail when rising_edge(i_ctlr_gen_clk);
    ctlr_miso.cal_ok   <= local_cal_success;
    ctlr_miso.cal_fail <= local_cal_fail;

  end generate;

end str;