-------------------------------------------------------------------------------
--
-- Copyright (C) 2015
-- 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/>.
--
-------------------------------------------------------------------------------

--
--  Register definition:
--
--  wi  Bits    R/W Name          Default  Description
--  =============================================================================
--  0   [0]     WE  burstbegin    0x0
--  1   [0]     WO  wr_not_rd     0x0
--  2   [0]     RO  done          0x1
--  3                                      not used
--  4                                      not used
--  5   [31..0] WO  address       0x0
--  6   [31..0] WO  burstsize     0x0
--  7   [0]     WR  flush         0x0
--  =============================================================================
--
--  Legenda:
--
--    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
--
--  Remarks:
--  . Clock domain crossing between mm_clk and the DDR controller clock domain
--    needs to be done by io_ddr_cross_domain.vhd in io_ddr.

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

    -- 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'
  );

  signal i_dvr_mosi        : t_mem_ctlr_mosi;

begin

  dvr_mosi <= i_dvr_mosi;

  ------------------------------------------------------------------------------
  -- 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;
      i_dvr_mosi <= c_mem_ctlr_mosi_rst;

    elsif rising_edge(mm_clk) then
      -- Read access defaults
      sla_out.rdval <= '0';

      -- Write access defaults
      i_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 =>
            i_dvr_mosi.burstbegin <= sla_in.wrdata(0);  -- default to '0' so MM write '1' will yield a pulse of one mm_clk clock cycle
          when 1 =>
            i_dvr_mosi.wr         <= sla_in.wrdata(0);  -- = wr_not_rd, so no need to control i_dvr_mosi.rd
          when 5 =>
            i_dvr_mosi.address    <= sla_in.wrdata(c_mem_ctlr_address_w - 1 downto 0);
          when 6 =>
            i_dvr_mosi.burstsize  <= sla_in.wrdata(c_mem_ctlr_burstsize_w - 1 downto 0);
          when 7 =>
            i_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) <= dvr_miso.done;  -- read only
          when 7 =>
            sla_out.rddata(0) <= i_dvr_mosi.flush;  -- readback
          when others => null;  -- unused MM addresses
        end case;
      end if;
    end if;
  end process;

end rtl;