--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- WR2RF_VME
-- https://ohwr.org/project/vme-rf-wr-bobr
--------------------------------------------------------------------------------
--
-- unit name:   wr2rf_rfchan
--
-- description: instantiation of RFNCO and IQDAC modules
--
--------------------------------------------------------------------------------
-- Copyright (c) 2020 CERN (home.cern)
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- 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://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this 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.
--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.gencores_pkg.all;
use work.wishbone_pkg.all;
use work.wr_fabric_pkg.all;
use work.RFFrameTransceiver_pkg.all;
use work.axi4_pkg.all;

entity wr2rf_rfchan is

    generic (
        g_dac_dpins           : integer := 16;
        g_ila                 : string  := "iqdac" );

    port (
        clk125m_i             : in  std_logic;
        clk250m_i             : in  std_logic;

        rst_clk125m_i         : in  std_logic;
        rst_clk125m_n_i       : in  std_logic;
        rst_clk250m_i         : in  std_logic;
        rst_clk250m_n_i       : in  std_logic;

        dds_ftw_valid_i       : in  std_logic;
        dds_ftw_i             : in  std_logic_vector(47 downto 0);

        rx_RFmFramePayloads_i : in  t_RFmFramePayload;
        rx_Frame_valid_pX_i   : in  std_logic;
        iqdac_igain_i         : in  std_logic_vector(15 downto 0);
        iqdac_qgain_i         : in  std_logic_vector(15 downto 0);
        iqdac_ctrl_i          : in  std_logic_vector(15 downto 0);

        wb_rfnco_slave_i      : in  t_wishbone_slave_in;
        wb_rfnco_slave_o      : out t_wishbone_slave_out;

        rfnco_frev_o          : out std_logic;
        rfnco_frev_main_o     : out std_logic;
        iqdac_data_p_o        : out std_logic_vector(g_dac_dpins-1 downto 0);
        iqdac_data_n_o        : out std_logic_vector(g_dac_dpins-1 downto 0);
        iqdac_dci_p_o         : out std_logic;
        iqdac_dci_n_o         : out std_logic;

        ila_dbg_o             : out std_logic_vector(127 downto 0) );

    attribute keep_hierarchy : string;
    attribute keep_hierarchy of wr2rf_rfchan : entity is "yes";    
    attribute shreg_extract  : string;
    attribute shreg_extract  of wr2rf_rfchan : entity is "no";

end entity;

architecture rtl of wr2rf_rfchan is

    signal cos_if1             : std_logic_vector(24 downto 0);
    signal sin_if1             : std_logic_vector(24 downto 0);
    signal if_out              : std_logic_vector(63 downto 0);
    signal rfnco_iqdac_idata   : std_logic_vector(15 downto 0);
    signal rfnco_iqdac_qdata   : std_logic_vector(15 downto 0);
    signal dds_iqdac_idata     : std_logic_vector(15 downto 0);
    signal dds_iqdac_qdata     : std_logic_vector(15 downto 0);

    signal dds_iqdac_qdata_r   : std_logic_vector(15 downto 0);
    signal dds_iqdac_idata_r   : std_logic_vector(15 downto 0);

    signal rfnco_iqdac_qdata_r : std_logic_vector(15 downto 0);
    signal rfnco_iqdac_idata_r : std_logic_vector(15 downto 0);

    signal rfnco_frev          : std_logic;
    signal rfnco_frev_main     : std_logic;

    signal iqdac_idata         : std_logic_vector(15 downto 0);
    signal iqdac_qdata         : std_logic_vector(15 downto 0);
    signal iqdac_idata_r       : std_logic_vector(15 downto 0);
    signal iqdac_qdata_r       : std_logic_vector(15 downto 0);

    signal iqdac_ctrl_r        : std_logic_vector(15 downto 0);

    signal dds_ftw_valid_r     : std_logic;
    signal dds_ftw_r           : std_logic_vector(47 downto 0);
    signal dds_data            : std_logic_vector(31 downto 0);
    signal rf_sync             : std_logic;

    -- bypass option for iqmod block not used in wr2rf.
    signal bypass_in_i         : std_logic_vector(15 downto 0) := x"0000";
    signal bypass_in_q         : std_logic_vector(15 downto 0) := x"0000";
    signal ctrl_bypass_mod     : std_logic := '0';

    signal axi4_lite_mout32    : t_axi4_lite_master_out_32;
    signal axi4_lite_min32     : t_axi4_lite_master_in_32;
    signal coef_vld            : std_logic;

    --attribute keep_hierarchy : STRING;
    attribute keep_hierarchy of xaxi4lite_wb_bridge_i        : label is "yes";
    attribute keep_hierarchy of rfnco_wrapper_i              : label is "yes";
    attribute keep_hierarchy of noniqmodinterp2fir_wrapper_i : label is "yes";
    attribute keep_hierarchy of xilinx_dds_i                 : label is "yes";

begin

    xaxi4lite_wb_bridge_i : entity work.xaxi4lite_wb_bridge
       port map (
          clk_i         => clk125m_i,
          rst_n_i       => rst_clk125m_n_i,

          wb_slave_i    => wb_rfnco_slave_i,
          wb_slave_o    => wb_rfnco_slave_o,

          axi4_master_o => axi4_lite_mout32,
          axi4_master_i => axi4_lite_min32 );

    rfnco_wrapper_i : entity work.rfnco_wrapper
        port map (
            am_auto_on_o                    => open,
            am_o                            => open,
            aximm_i_araddr                  => axi4_lite_mout32.araddr(11 downto 0),
            aximm_i_arprot                  => "000",
            aximm_i_arready                 => axi4_lite_min32.arready,
            aximm_i_arvalid                 => axi4_lite_mout32.arvalid,
            aximm_i_awaddr                  => axi4_lite_mout32.awaddr(11 downto 0),
            aximm_i_awprot                  => "000",
            aximm_i_awready                 => axi4_lite_min32.awready,
            aximm_i_awvalid                 => axi4_lite_mout32.awvalid,
            aximm_i_bready                  => axi4_lite_mout32.bready,
            aximm_i_bresp                   => axi4_lite_min32.bresp,
            aximm_i_bvalid                  => axi4_lite_min32.bvalid,
            aximm_i_rdata                   => axi4_lite_min32.rdata,
            aximm_i_rready                  => axi4_lite_mout32.rready,
            aximm_i_rresp                   => axi4_lite_min32.rresp,
            aximm_i_rvalid                  => axi4_lite_min32.rvalid,
            aximm_i_wdata                   => axi4_lite_mout32.wdata,
            aximm_i_wready                  => axi4_lite_min32.wready,
            aximm_i_wstrb                   => axi4_lite_mout32.wstrb,
            aximm_i_wvalid                  => axi4_lite_mout32.wvalid,

            clk_i                           => clk125m_i,
            coef_o                          => open,
            coef_vld_o                      => coef_vld,
            cos_if0_o                       => open,
            cos_if1_o                       => cos_if1,
            ext_reset_enable_o              => open,
            ext_reset_o                     => open,
            frev_main_o                     => rfnco_frev_main,
            frev_o                          => rfnco_frev,
            ftw_h1_o                        => open,
            ftw_h1_on_o                     => open,
            ftw_lo_0_i                      => (others => '0'),
            ftw_lo_1_i                      => (others => '0'),
            ftw_valid_o                     => open,
            interlock_o                     => open,
            offset_FSK_i_0                  => (others => '0'),
            offset_i_0                      => (others => '0'),
            phase_h1_comp_o                 => open,
            phase_h1_main_o                 => open,
            phase_h1_o                      => open,
            phase_H1_offset_o               => open,
            phase_H1_prog_offset_o          => open,
            phase_h1_slip_o                 => open,
            phase_if0_o                     => open,
            phase_if1_o                     => open,
            rst_i                           => rst_clk125m_i,
            sin_if0_o                       => open,
            sin_if1_o                       => sin_if1,
            sincos_vld_o                    => open,
            tw_h1_o                         => open,
            wr_rf_frame_bus_i_dftw_h1_slip0 => rx_RFmFramePayloads_i.dftw_h1_slip1(31 downto 0),
            wr_rf_frame_bus_i_dftw_h1_slip1 => rx_RFmFramePayloads_i.dftw_h1_slip2(31 downto 0),
            wr_rf_frame_bus_i_ftw_h1_main   => rx_RFmFramePayloads_i.ftw_h1_main,
            wr_rf_frame_bus_i_ftw_h1_on     => rx_RFmFramePayloads_i.ftw_h1_on,
            wr_rf_frame_bus_i_ftw_h1_prog   => rx_RFmFramePayloads_i.ftw_h1_prog,
            wr_rf_frame_bus_i_inputs_valid  => rx_Frame_valid_pX_i,
            wr_rf_frame_bus_i_rate          => rx_RFmFramePayloads_i.control(5 downto 3),
            wr_rf_frame_bus_i_reset_fsk     => rx_RFmFramePayloads_i.control(2),
            wr_rf_frame_bus_i_reset_nco     => rx_RFmFramePayloads_i.control(0),
            wr_rf_frame_bus_i_reset_slip    => rx_RFmFramePayloads_i.control(1) );

    if_out <= "0000000" & sin_if1 & "0000000" & cos_if1;
    noniqmodinterp2fir_wrapper_i : entity work.nonIQModInterp2FIR_wrapper
        port map (
            bypass_in_i_i     => bypass_in_i,
            bypass_in_q_i     => bypass_in_q,
            clk_dsp_125_i     => clk125m_i,
            clk_dsp_250_i     => clk250m_i,
            ctrl_bypass_mod_i => ctrl_bypass_mod,
            data_bb_i_i       => iqdac_igain_i, -- iqdac_igain and
            data_bb_q_i       => iqdac_qgain_i, -- iqdac_qgain may not meet timing...
            data_if_i_o       => rfnco_iqdac_idata,
            data_if_q_o       => rfnco_iqdac_qdata,
            if_out_i          => if_out,
            if_prefir_o       => open,
            interlock_o       => open,
            rst_dsp_250_i     => rst_clk250m_i );
    
    -- A CDC, but slow source from 62.5 -> 250 dest. _If_ it meets timing, it's safe.
    process (clk250m_i) is
    begin
       if rising_edge(clk250m_i) then
          if rst_clk250m_n_i = '0' then
             dds_ftw_valid_r <= '0';             
             dds_ftw_r       <= dds_ftw_i;
          else
             dds_ftw_valid_r <= dds_ftw_valid_i;
             dds_ftw_r       <= dds_ftw_i;
             end if;                              
       end if;
       
    end process;
    

    xilinx_dds_i : entity work.dds_compiler_v5_0
       port map (
          aclk                 => clk250m_i,
          aresetn              => rst_clk250m_n_i,
          s_axis_config_tvalid => dds_ftw_valid_r,
          s_axis_config_tdata  => dds_ftw_r,
          m_axis_data_tvalid   => open,
          m_axis_data_tdata    => dds_data );

    dds_iqdac_idata <= dds_data(15 downto  0);
    dds_iqdac_qdata <= dds_data(31 downto 16);

    iqdac_qdata     <= (others => '0')        when iqdac_ctrl_r(0) = '1' else
                       dds_iqdac_qdata_r      when iqdac_ctrl_r(1) = '1' else
                       rfnco_iqdac_qdata_r;
    iqdac_idata     <= (others => '0')        when iqdac_ctrl_r(0) = '1' else
                       dds_iqdac_idata_r      when iqdac_ctrl_r(1) = '1' else
                       rfnco_iqdac_idata_r;

    process (clk250m_i) is
    begin
       if rising_edge(clk250m_i) then
          iqdac_ctrl_r        <= iqdac_ctrl_i;
          rfnco_iqdac_idata_r <= rfnco_iqdac_idata;
          rfnco_iqdac_qdata_r <= rfnco_iqdac_qdata;
          dds_iqdac_idata_r   <= dds_iqdac_idata;
          dds_iqdac_qdata_r   <= dds_iqdac_qdata;
          iqdac_idata_r       <= iqdac_idata;
          iqdac_qdata_r       <= iqdac_qdata;
       end if;
    end process;


    iqdac_io_i : entity  work.iqdac_io
        port map (
            clk_i          => clk250m_i,
            reset_io_i     => '0',
            iqdac_qdata_i  => iqdac_qdata_r,
            iqdac_idata_i  => iqdac_idata_r,
            iqdac_data_p_o => iqdac_data_p_o,
            iqdac_data_n_o => iqdac_data_n_o,
            iqdac_dc_p_o   => iqdac_dci_p_o,
            iqdac_dc_n_o   => iqdac_dci_n_o );

    -- outputs
    rfnco_frev_o      <= rfnco_frev;
    rfnco_frev_main_o <= rfnco_frev_main;
    
    -- ila dbg
    g_axi_debug : if (g_ila = "axi") generate
    begin
       ila_dbg_o(31 downto  0)   <= axi4_lite_mout32.wdata;
       ila_dbg_o(63 downto 32)   <= axi4_lite_min32.rdata;
       ila_dbg_o(79 downto 64)   <= "0000" & axi4_lite_mout32.awaddr(11 downto 0);
       ila_dbg_o(95 downto 80)   <= "0000" & axi4_lite_mout32.araddr(11 downto 0);
       ila_dbg_o(96)             <= axi4_lite_mout32.awvalid;
       ila_dbg_o(97)             <= axi4_lite_mout32.wvalid;
       ila_dbg_o(98)             <= axi4_lite_mout32.arvalid;
       ila_dbg_o(99)             <= axi4_lite_min32.rvalid;
       ila_dbg_o(127 downto 100) <= (others => '0');
    end generate;

    g_dac_debug : if (g_ila = "iqdac") generate    
       ila_dbg_o(15 downto  0) <= iqdac_idata_r;
       ila_dbg_o(31 downto 16) <= iqdac_qdata_r;
       ila_dbg_o(56 downto 32) <= sin_if1;
       ila_dbg_o(63 downto 57) <= (others => '0');       
       ila_dbg_o(88 downto 64) <= cos_if1;
       ila_dbg_o(89)           <= coef_vld;
       ila_dbg_o(90)           <= rfnco_frev;
       ila_dbg_o(91)           <= rfnco_frev_main;
       ila_dbg_o(92)           <= rx_Frame_valid_pX_i;             
       ila_dbg_o(95 downto 93) <= (others => '0');
    end generate;

end architecture;
