-------------------------------------------------------------------------------
--
-- Copyright 2022
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-------------------------------------------------------------------------------
-- Author: Job van Wee
-- Purpose: Folding a stream of data into a mm data configuration so it can be
-- stored in a DDR RAM-stick.
--
-- Description:
--  First the data from the sosi array gets collected into one data vector.
--  After that this data vector gets resized to the right size data vector in 
--  order to make it storable in a DDR RAM-stick.
--  After that a address gets assigned to the data so the data can be found back.
--
-- Remark:
--  Use VHDL coding template from:
--  https://support.astron.nl/confluence/display/SBe/VHDL+design+patterns+for+RTL+coding
--  The maximum value of the address is determend by g_tech_ddr.

LIBRARY IEEE, technology_lib, tech_ddr_lib, common_lib, dp_lib, io_ddr_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE technology_lib.technology_pkg.ALL;
USE technology_lib.technology_select_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE io_ddr_lib.ALL;


ENTITY ddrctrl IS
  GENERIC (
    g_tech_ddr        : t_c_tech_ddr;                                                                                         -- type of memory
    g_sim_model       : BOOLEAN                                             := TRUE;                                          -- determens if this is a simulation
    g_technology      : NATURAL                                             := c_tech_select_default;
    g_nof_streams     : NATURAL                                             := 12;                                            -- number of input streams
    g_data_w          : NATURAL                                             := 14;                                            -- data with of input data vectors
    g_stop_percentage : NATURAL                                             := 50;
    g_block_size      : NATURAL                                             := 1024
  );
  PORT (
    clk               : IN  STD_LOGIC                                       := '0';
    rst               : IN  STD_LOGIC;
    mm_clk            : IN  STD_LOGIC                                       := '0';
    mm_rst            : IN  STD_LOGIC                                       := '0';
    in_sosi_arr       : IN  t_dp_sosi_arr;                                                                                    -- input data
    stop_in           : IN  STD_LOGIC                                       := '0';

    out_sosi_arr      : OUT t_dp_sosi_arr;

    term_ctrl_out     : OUT   t_tech_ddr3_phy_terminationcontrol;
    term_ctrl_in      : IN    t_tech_ddr3_phy_terminationcontrol            := c_tech_ddr3_phy_terminationcontrol_rst;

    -- DDR3 PHY external interface
    phy3_in           : IN    t_tech_ddr3_phy_in                            := c_tech_ddr3_phy_in_x;
    phy3_io           : INOUT t_tech_ddr3_phy_io;
    phy3_ou           : OUT   t_tech_ddr3_phy_ou;

    -- DDR4 PHY external interface
    phy4_in           : IN    t_tech_ddr4_phy_in                            := c_tech_ddr4_phy_in_x;
    phy4_io           : INOUT t_tech_ddr4_phy_io;
    phy4_ou           : OUT   t_tech_ddr4_phy_ou
  );
END ddrctrl;


ARCHITECTURE str OF ddrctrl IS

  -- constant for readability
  CONSTANT  c_io_ddr_data_w   : NATURAL                                     := func_tech_ddr_ctlr_data_w( g_tech_ddr );
  CONSTANT  c_wr_fifo_depth   : NATURAL                                     := 256;                                           -- defined at DDR side of the FIFO, >=16 and independent of wr burst size, default >= 256 because 32b*256 fits in 1 M9K so c_ctlr_data_w=256b will require 8 M9K
  CONSTANT  c_rd_fifo_depth   : NATURAL                                     := 256;                                           -- defined at DDR side of the FIFO, >=16 AND > max number of rd burst sizes (so > c_rd_fifo_af_margin), default >= 256 because 32b*256 fits in 1 M9K so c_ctlr_data_w=256b will require 8 M9K
  CONSTANT  c_rd_fifo_uw_w    : NATURAL                                     := ceil_log2(c_rd_fifo_depth*(func_tech_ddr_ctlr_data_w(g_tech_ddr)/c_io_ddr_data_w));

  -- signals for connecting the components
  SIGNAL    ctrl_clk     : STD_LOGIC;
  SIGNAL    ctrl_rst     : STD_LOGIC;
  SIGNAL    out_of       : NATURAL                                          := 0;
  SIGNAL    out_sosi     : t_dp_sosi                                        := c_dp_sosi_init;
  SIGNAL    out_adr      : NATURAL                                          := 0;
  SIGNAL    dvr_mosi     : t_mem_ctlr_mosi                                  := c_mem_ctlr_mosi_rst;
  SIGNAL    dvr_miso     : t_mem_ctlr_miso                                  := c_mem_ctlr_miso_rst;
  SIGNAL    wr_sosi      : t_dp_sosi                                        := c_dp_sosi_init;
  SIGNAL    rd_siso      : t_dp_siso                                        := c_dp_siso_rst;
  SIGNAL    rd_sosi      : t_dp_sosi                                        := c_dp_sosi_init;
  SIGNAL    stop         : STD_LOGIC;
  SIGNAL    rd_fifo_usedw: STD_LOGIC_VECTOR(c_rd_fifo_uw_w-1 DOWNTO 0);
  SIGNAL    rd_ready     : STD_LOGIC;
  SIGNAL    inp_ds       : NATURAL;
  SIGNAL    inp_bsn      : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
  SIGNAL    inp_bsn_adr  : NATURAL;
  SIGNAL    outp_ds      : NATURAL;
  SIGNAL    outp_bsn     : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);

BEGIN

  rd_siso.ready <= rd_ready;
  rd_siso.xon   <= '1';

  -- input to io_ddr
  u_ddrctrl_input : ENTITY work.ddrctrl_input
  GENERIC MAP(
    g_tech_ddr                => g_tech_ddr,
    g_sim_model               => g_sim_model,
    g_nof_streams             => g_nof_streams,
    g_data_w                  => g_data_w
  )
  PORT MAP(
    clk                       => clk,
    rst                       => rst,
    in_sosi_arr               => in_sosi_arr,
    in_stop                   => stop,
    out_of                    => out_of,
    out_sosi                  => out_sosi,
    out_adr                   => out_adr,
    out_bsn_ds                => inp_ds,
    out_bsn                   => inp_bsn,
    out_bsn_adr               => inp_bsn_adr
  );

  -- functions as a fifo buffer for input data into the sdram stick. also manages input to sdram stick.
  u_io_ddr : ENTITY io_ddr_lib.io_ddr
  GENERIC MAP(
    g_sim_model               => g_sim_model,
    g_technology              => g_technology,
    g_tech_ddr                => g_tech_ddr,
    g_cross_domain_dvr_ctlr   => FALSE,
    g_wr_data_w               => c_io_ddr_data_w,  
    g_wr_fifo_depth           => c_wr_fifo_depth,
    g_rd_fifo_depth           => c_rd_fifo_depth,
    g_rd_data_w               => c_io_ddr_data_w,
    g_wr_flush_mode           => "VAL",
    g_wr_flush_use_channel    => FALSE,
    g_wr_flush_start_channel  => 0,
    g_wr_flush_nof_channels   => 1
    )
    PORT MAP(

    -- DDR reference clock
    ctlr_ref_clk              => clk,
    ctlr_ref_rst              => rst,

    -- DDR controller clock domain
    ctlr_clk_out              => ctrl_clk,
    ctlr_rst_out              => ctrl_rst,
    
    ctlr_clk_in               => ctrl_clk,
    ctlr_rst_in               => ctrl_rst,
    
    -- MM clock + reset
    mm_rst                    => mm_rst,                                           
    mm_clk                    => mm_clk, 
    
    -- MM interface
    reg_io_ddr_mosi           => open,
    reg_io_ddr_miso           => open,
    
    -- Driver clock domain
    dvr_clk                   => clk,
    dvr_rst                   => rst,
    
    dvr_miso                  => dvr_miso,
    dvr_mosi                  => dvr_mosi,
    
    -- Write FIFO clock domain
    wr_clk                    => clk,
    wr_rst                    => rst,

    wr_fifo_usedw             => open,
    wr_sosi                   => wr_sosi,
    wr_siso                   => open,
  
    -- Read FIFO clock domain
    rd_clk                    => clk,
    rd_rst                    => rst,
    
    rd_fifo_usedw             => rd_fifo_usedw,
    rd_sosi                   => rd_sosi,
    rd_siso                   => rd_siso,

    term_ctrl_out             => term_ctrl_out,
    term_ctrl_in              => term_ctrl_in,
    
    -- DDR3 PHY external interface
    phy3_in                   => phy3_in,
    phy3_io                   => phy3_io,
    phy3_ou                   => phy3_ou,
    
    -- DDR4 PHY external interface
    phy4_in                   => phy4_in,
    phy4_io                   => phy4_io,
    phy4_ou                   => phy4_ou
  );

  -- reading ddr memory
  u_ddrctrl_output : ENTITY work.ddrctrl_output
  GENERIC MAP(
    g_tech_ddr                => g_tech_ddr,
    g_sim_model               => g_sim_model,
    g_in_data_w               => c_io_ddr_data_w,
    g_nof_streams             => g_nof_streams,
    g_data_w                  => g_data_w,
    g_block_size              => g_block_size
  )
  PORT MAP(
    clk                       => clk,
    rst                       => rst,

    in_sosi                   => rd_sosi,
    in_ds                     => outp_ds,
    in_bsn                    => outp_bsn,

    out_sosi_arr              => out_sosi_arr,
    out_ready                 => rd_ready
  );

  -- controller of ddrctrl
  u_ddrctrl_controller : ENTITY work.ddrctrl_controller
  GENERIC MAP(
    g_tech_ddr                => g_tech_ddr,
    g_stop_percentage         => g_stop_percentage,
    g_nof_streams             => g_nof_streams,
    g_out_data_w              => g_data_w,
    g_wr_data_w               => c_io_ddr_data_w,
    g_rd_fifo_depth           => c_rd_fifo_depth,
    g_rd_data_w               => c_io_ddr_data_w,
    g_block_size              => g_block_size,
    g_rd_fifo_uw_w            => c_rd_fifo_uw_w
  )
  PORT MAP(
    clk                       => clk,
    rst                       => rst,

    -- ddrctrl_input
    inp_of                    => out_of,
    inp_sosi                  => out_sosi,
    inp_adr                   => out_adr,
    inp_ds                    => inp_ds,
    inp_bsn                   => inp_bsn,
    inp_bsn_adr               => inp_bsn_adr,

    -- io_ddr
    dvr_mosi                  => dvr_mosi,
    dvr_miso                  => dvr_miso,
    wr_sosi                   => wr_sosi,
    rd_fifo_usedw             => rd_fifo_usedw,

    -- ddrctrl_output
    outp_ds                   => outp_ds,
    outp_bsn                  => outp_bsn,

    -- ddrctrl_controller
    stop_in                   => stop_in,
    stop_out                  => stop
  );

END str;