-------------------------------------------------------------------------------
--
-- 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/>.
--
-------------------------------------------------------------------------------

--  RO  read only  (no VHDL present to access HW in write mode)
--  WO  write only (no VHDL present to access HW in read mode)
--  WE  write event (=WO)
--  WR  write control, read control
--  RW  read status, write control  
--  RC  read, clear on read
--  FR  FIFO read
--  FW  FIFO write
--
--  wi  Bits    R/W Name          Default  Description                 |REG_DDR3|                      
--  =============================================================================
--  0   [0]     WE  burstbegin    0x0
--  1   [0]     WO  wr_not_rd     0x0
--  2   [0]     RO  done          0x1
--  3   [1..0]  RO  cal_result    0x1      cal_fail & cal_ok
--  4   [0]     RO  waitrequest_n 0x1
--  5   [31..0] WO  address       0x0
--  6   [31..0] WO  burstsize     0x0
--  7   [0]     WR  flush         0x0
--  =============================================================================

LIBRARY IEEE, common_lib, diag_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE diag_lib.diag_pkg.ALL;


ENTITY io_ddr_reg IS
  PORT (
    -- Clocks and reset
    mm_rst            : IN  STD_LOGIC;  -- reset synchronous with mm_clk
    mm_clk            : IN  STD_LOGIC;  -- memory-mapped bus clock
    dp_rst            : IN  STD_LOGIC;  -- reset synchronous with dp_clk
    dp_clk            : IN  STD_LOGIC;  -- other clock domain clock
    
    -- Memory Mapped Slave in mm_clk domain
    sla_in            : IN  t_mem_mosi; -- actual ranges defined by c_mm_reg
    sla_out           : OUT t_mem_miso; -- actual ranges defined by c_mm_reg
    
    -- MM registers in st_clk domain
    dvr_miso          : IN  t_mem_ctlr_miso;
    dvr_mosi          : OUT t_mem_ctlr_mosi
  );
END io_ddr_reg;


ARCHITECTURE rtl OF io_ddr_reg IS

  CONSTANT c_mm_reg        : t_c_mem := (latency  => 1,
                                         adr_w    => ceil_log2(8),
                                         dat_w    => c_word_w,       -- Use MM bus data width = c_word_w = 32 for all MM registers
                                         nof_dat  => 8,
                                         init_sl  => '0');                                               
  -- Registers in mm_clk domain
  SIGNAL mm_dvr_miso       : t_mem_ctlr_miso;
  SIGNAL mm_dvr_mosi       : t_mem_ctlr_mosi;
     
BEGIN

  ------------------------------------------------------------------------------
  -- MM register access in the mm_clk domain
  -- . Hardcode the shared MM slave register directly in RTL instead of using
  --   the common_reg_r_w instance. Directly using RTL is easier when the large
  --   MM register has multiple different fields and with different read and
  --   write options per field in one MM register.
  ------------------------------------------------------------------------------
  
  p_mm_reg : PROCESS (mm_rst, mm_clk)
  BEGIN
    IF mm_rst = '1' THEN
      sla_out     <= c_mem_miso_rst;
      mm_dvr_mosi <= c_mem_ctlr_mosi_rst;
      
    ELSIF rising_edge(mm_clk) THEN
      -- Read access defaults
      sla_out.rdval <= '0';
      mm_dvr_mosi.burstbegin <= '0';
      
      -- Write access: set register value
      IF sla_in.wr = '1' THEN
        CASE TO_UINT(sla_in.address(c_mm_reg.adr_w-1 DOWNTO 0)) IS
          -- Write Block Sync
          WHEN 0 =>            
            mm_dvr_mosi.burstbegin <= sla_in.wrdata(0);
          WHEN 1 =>
            mm_dvr_mosi.wr         <= sla_in.wrdata(0);   
            mm_dvr_mosi.rd         <= NOT(sla_in.wrdata(0));          
          WHEN 5 =>                
            mm_dvr_mosi.address    <= sla_in.wrdata(c_mem_ctlr_address_w-1 DOWNTO 0);
          WHEN 6 =>        
            mm_dvr_mosi.burstsize  <= sla_in.wrdata(c_mem_ctlr_burstsize_w-1 DOWNTO 0);
          WHEN 7 =>            
            mm_dvr_mosi.flush      <= sla_in.wrdata(0);
          WHEN OTHERS => NULL;  -- unused MM addresses
        END CASE;

      -- Read access: get register value
      ELSIF sla_in.rd = '1' THEN
        sla_out       <= c_mem_miso_rst;    -- set unused rddata bits to '0' when read
        sla_out.rdval <= '1';               -- c_mm_reg.latency = 1
        CASE TO_UINT(sla_in.address(c_mm_reg.adr_w-1 DOWNTO 0)) IS
          -- Read Block Sync
          WHEN 2 =>            
            sla_out.rddata(0) <= mm_dvr_miso.done;
          WHEN 3 =>
            sla_out.rddata(1 DOWNTO 0) <= mm_dvr_miso.cal_fail & mm_dvr_miso.cal_ok;
          WHEN 4 =>
            sla_out.rddata(0) <= mm_dvr_miso.waitrequest_n;   
          WHEN 7 => 
            sla_out.rddata(0) <= mm_dvr_mosi.flush;    
          WHEN OTHERS => NULL;  -- unused MM addresses
        END CASE;
      END IF;
    END IF;
  END PROCESS;

  ------------------------------------------------------------------------------
  -- Transfer register value between mm_clk and st_clk domain.
  -- If the function of the register ensures that the value will not be used
  -- immediately when it was set, then the transfer between the clock domains
  -- can be done by wires only. Otherwise if the change in register value can
  -- have an immediate effect then the bit or word value needs to be transfered
  -- using:
  --
  -- . common_async            --> for single-bit level signal
  -- . common_spulse           --> for single-bit pulse signal
  -- . common_reg_cross_domain --> for a multi-bit (a word) signal
  --
  -- Typically always use a crossing component for the single bit signals (to
  -- be on the safe side) and only use a crossing component for the word
  -- signals if it is necessary (to avoid using more logic than necessary).
  ------------------------------------------------------------------------------
  
  u_spulse_en_evt : ENTITY common_lib.common_spulse
  PORT MAP (
    in_rst    => mm_rst,
    in_clk    => mm_clk,
    in_pulse  => mm_dvr_mosi.burstbegin,
    in_busy   => OPEN,
    out_rst   => dp_rst,
    out_clk   => dp_clk,
    out_pulse => dvr_mosi.burstbegin
  );
 
  u_async_wr : ENTITY common_lib.common_async
  GENERIC MAP (
    g_rst_level => '0'
  )
  PORT MAP (
    rst  => dp_rst,
    clk  => dp_clk,
    din  => mm_dvr_mosi.wr,
    dout => dvr_mosi.wr
  );

  u_async_rd : ENTITY common_lib.common_async
  GENERIC MAP (
    g_rst_level => '0'
  )
  PORT MAP (
    rst  => dp_rst,
    clk  => dp_clk,
    din  => mm_dvr_mosi.rd,
    dout => dvr_mosi.rd
  );

  u_async_flush : ENTITY common_lib.common_async
  GENERIC MAP (
    g_rst_level => '0'
  )
  PORT MAP (
    rst  => dp_rst,
    clk  => dp_clk,
    din  => mm_dvr_mosi.flush,
    dout => dvr_mosi.flush
  );

  u_async_done : ENTITY common_lib.common_async
  GENERIC MAP (
    g_rst_level => '0'
  )
  PORT MAP (
    rst  => mm_rst,
    clk  => mm_clk,
    din  => dvr_miso.done,
    dout => mm_dvr_miso.done
  );

  u_async_waitrequest_n : ENTITY common_lib.common_async
  GENERIC MAP (
    g_rst_level => '0'
  )
  PORT MAP (
    rst  => mm_rst,
    clk  => mm_clk,
    din  => dvr_miso.waitrequest_n,
    dout => mm_dvr_miso.waitrequest_n
  );

  mm_dvr_miso.cal_fail <= dvr_miso.cal_fail;
  mm_dvr_miso.cal_ok   <= dvr_miso.cal_ok;

  u_cross_domain_addr : ENTITY common_lib.common_reg_cross_domain
  PORT MAP (
    in_rst      => mm_rst,
    in_clk      => mm_clk,
    in_dat      => mm_dvr_mosi.address,
    in_done     => OPEN,
    out_rst     => dp_rst,
    out_clk     => dp_clk,
    out_dat     => dvr_mosi.address,
    out_new     => OPEN
  );

  u_cross_domain_burstsize : ENTITY common_lib.common_reg_cross_domain
  PORT MAP (
    in_rst      => mm_rst,
    in_clk      => mm_clk,
    in_dat      => mm_dvr_mosi.burstsize,
    in_done     => OPEN,
    out_rst     => dp_rst,
    out_clk     => dp_clk,
    out_dat     => dvr_mosi.burstsize,
    out_new     => OPEN
  );
 
END rtl;