--------------------------------------------------------------------------------
-- CERN BE-CO-HT
--
-- https://www.ohwr.org/projects/wr2rf-vme
--------------------------------------------------------------------------------
--
-- unit name:   wr2rf_reset_logic
--
-- description: Provides wrapper for how the DDS sync, RFNCO and rf_t1_sync signals
-- are produced
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020
--------------------------------------------------------------------------------
-- 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;

library unisim;
use unisim.vcomponents.all;

use work.wishbone_pkg.all;

entity wr2rf_dds is

   port (
      clk62m5_i        : in  std_logic;
      clk125m_i        : in  std_logic;
      clk500m_i        : in  std_logic;
      rst_clk62m5_i    : in  std_logic;
      rst_clk125m_i    : in  std_logic;

      ioupdate_fdelay_i : in  std_logic_vector(3 downto 0);
      ioupdate_odelay_i : in  std_logic_vector(4 downto 0);
      dds_ioupdate_i   : in  std_logic;
      dds_ioupdate_o   : out std_logic;
      dds_sync_p_o     : out std_logic;
      dds_sync_n_o     : out std_logic );

   attribute keep_hierarchy              : STRING;
   attribute keep_hierarchy of wr2rf_dds : entity is "yes";

end entity;

architecture rtl of wr2rf_dds is

   -- We need to register pack the iob for dds_sync
   -- dds sync is a common master clock passed to each of the
   -- ad9910 throughout the installations. It ensures common
   -- timing on our DDSs. It is _not_ used to synchronise the
   -- ad9910 outputs - see ioupdate for that
   signal dds_sync   : std_logic;

   -- ioupdate fine delay
   signal ioupdate_delayed_r    : std_logic;
   signal ioupdate_delayed_h    : std_logic;
   signal ioupdate_delayed_hh   : std_logic;
   signal ioupdate_delayed_hhh  : std_logic;
   signal ioupdate_pattern      : std_logic_vector(23 downto 0);   
   signal ioupdate_pattern_r    : std_logic_vector(23 downto 0);
   signal ioupdate_fdelay_r     : std_logic_vector(3 downto 0);
   signal dds_ioupdate_fdelay   : std_logic_vector(7 downto 0);
   signal dds_ioupdate_fdelay_r : std_logic_vector(7 downto 0);
   signal dds_ioupdate_fdelayed : std_logic;
   signal dds_ioupdate          : std_logic;

begin

   -- We need the DDS SYNC_CLK to be one sixteenth of the DDS SYSCLK frequency (1 GHz)
   oddr_inst : oddr
      generic map (
         ddr_clk_edge   => "same_edge",
         init           => '0',
         srtype         => "async" )
      port map (
         d1             => '1',
         d2             => '0',
         c              => clk62m5_i,
         ce             => '1',
         q              => dds_sync,
         r              => '0',
         s              => '0' );

   dds_sync_i : obufds
      generic map(   
         slew => "FAST" )
      port map(
         o    => dds_sync_p_o,
         ob   => dds_sync_n_o,
         i    => dds_sync );

   ----------------------------------------------------
   -- Now for ioupdate
   ----------------------------------------------------
   ioupdate_pattern    <= std_logic_vector(unsigned'(X"0000FF") sll to_integer(unsigned(ioupdate_fdelay_r)));
   dds_ioupdate_fdelay <= ioupdate_pattern_r(7  downto  0) when ioupdate_delayed_r  = '1' and ioupdate_delayed_h   = '0' else
                          ioupdate_pattern_r(15 downto  8) when ioupdate_delayed_h  = '1' and ioupdate_delayed_hh  = '0' else
                          ioupdate_pattern_r(23 downto 16) when ioupdate_delayed_hh = '1' and ioupdate_delayed_hhh = '0' else (others => '0');

   process (clk125m_i) is
   begin
      if rising_edge(clk125m_i) then
         ioupdate_delayed_r    <= dds_ioupdate_i;
         ioupdate_delayed_h    <= ioupdate_delayed_r;
         ioupdate_delayed_hh   <= ioupdate_delayed_h;
         ioupdate_delayed_hhh  <= ioupdate_delayed_hh;
         ioupdate_pattern_r    <= ioupdate_pattern;
         ioupdate_fdelay_r     <= ioupdate_fdelay_i;
         dds_ioupdate_fdelay_r <= dds_ioupdate_fdelay;
      end if;
   end process;

   inst_ioupdate_OSERDESE2 : oserdese2
      generic map (
         DATA_RATE_OQ   => "DDR",    -- DDR, SDR
         DATA_RATE_TQ   => "SDR",    -- DDR, BUF, SDR
         DATA_WIDTH     => 8,        -- Parallel data width (2-8,10,14)
         INIT_OQ        => '0',      -- Initial value of OQ output (1'b0,1'b1)
         INIT_TQ        => '0',      -- Initial value of TQ output (1'b0,1'b1)
         SERDES_MODE    => "MASTER", -- MASTER, SLAVE
         SRVAL_OQ       => '0',      -- OQ output value when SR is used (1'b0,1'b1)
         SRVAL_TQ       => '0',      -- TQ output value when SR is used (1'b0,1'b1)
         TBYTE_CTL      => "FALSE",  -- Enable tristate byte operation (FALSE, TRUE)
         TBYTE_SRC      => "FALSE",  -- Tristate byte source (FALSE, TRUE)
         TRISTATE_WIDTH => 1 )       -- 3-state converter width (1,4)
      port map (
         OFB       => open,                 -- 1-bit output: Feedback path for data
         OQ        => dds_ioupdate_fdelayed,     -- 1-bit output: Data path output
         SHIFTOUT1 => open,                 -- SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
         SHIFTOUT2 => open,
         TBYTEOUT  => open,                 -- 1-bit output: Byte group tristate
         TFB       => open,                 -- 1-bit output: 3-state control
         TQ        => open,                 -- 1-bit output: 3-state control
         CLK       => clk500m_i,            -- 1-bit input: High speed clock
         CLKDIV    => clk125m_i,            -- 1-bit input: Divided clock
         D1        => dds_ioupdate_fdelay_r(0), -- D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
         D2        => dds_ioupdate_fdelay_r(1),
         D3        => dds_ioupdate_fdelay_r(2),
         D4        => dds_ioupdate_fdelay_r(3),
         D5        => dds_ioupdate_fdelay_r(4),
         D6        => dds_ioupdate_fdelay_r(5),
         D7        => dds_ioupdate_fdelay_r(6),
         D8        => dds_ioupdate_fdelay_r(7),
         OCE       => '1',                  -- 1-bit input: Output data clock enable
         RST       => rst_clk125m_i,        -- 1-bit input: Reset
         SHIFTIN1  => '0',                  -- SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
         SHIFTIN2  => '0',
         T1        => '0',                  -- T1 - T4: 1-bit (each) input: Parallel 3-state inputs
         T2        => '0',
         T3        => '0',
         T4        => '0',
         TBYTEIN   => '0',                  -- 1-bit input: Byte group tristate
         TCE       => '0' );                -- 1-bit input: 3-state clock enable
                                            --
   inst_sync_ODELAYE2 : ODELAYE2
      generic map (
         CINVCTRL_SEL          => "FALSE",    -- Enable dynamic clock inversion (FALSE, TRUE)
         DELAY_SRC             => "ODATAIN",  -- Delay input (ODATAIN, CLKIN)
         HIGH_PERFORMANCE_MODE => "TRUE",     -- Reduced jitter ("TRUE"), Reduced power ("FALSE")
         ODELAY_TYPE           => "VAR_LOAD", -- FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
         ODELAY_VALUE          => 0,          -- Output delay tap setting (0-31)
         PIPE_SEL              => "FALSE",    -- Select pipelined mode, FALSE, TRUE
         REFCLK_FREQUENCY      => 200.0,      -- IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
         SIGNAL_PATTERN        => "DATA" )    -- DATA, CLOCK input signal
      port map (
         CNTVALUEOUT => open,               -- output: Counter value output
         DATAOUT     => dds_ioupdate,     -- output: Delayed data/clock output
         C           => clk125m_i,          -- input: Clock input
         CE          => '0',                -- input: Active high enable increment/decrement input
         CINVCTRL    => '0',                -- input: Dynamic clock inversion input
         CLKIN       => '0',                -- input: Clock delay input
         CNTVALUEIN  => ioupdate_odelay_i,  -- input: Counter value input
         INC         => '0',                -- input: Increment / Decrement tap delay input
         LD          => '1',                -- input: Loads ODELAY_VALUE tap delay in VARIABLE mode
         LDPIPEEN    => '0',                -- input: Enables the pipeline register to load data
         ODATAIN     => dds_ioupdate_fdelayed,   -- input: Output delay data input
         REGRST      => rst_clk62m5_i );    -- input: Active-high reset tap-delay input

   dds_ioupdate_inst : obuf
      generic map(
         DRIVE => 16,
         slew  => "FAST" )
      port map(
         o    => dds_ioupdate_o,
         i    => dds_ioupdate );

   
end architecture;
