--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: rf_reset
--
-- Description:
--
--   Control RFDC, ADC, and DAC resets.
--

library IEEE;
  use IEEE.std_logic_1164.all;
  use IEEE.numeric_std.all;

entity rf_reset is
  port(
    -- Clocks used in the data path.
    DataClk     : in std_logic;
    PllRefClk   : in std_logic;
    RfClk       : in std_logic;
    RfClk2x     : in std_logic;
    DataClk2x   : in std_logic;

    -- Master resets from the Radio.
    dTimedReset : in std_logic;
    dSwReset    : in std_logic;

    -- Resets outputs.
    dReset_n    : out std_logic := '0';
    d2Reset_n   : out std_logic := '0';
    r2Reset_n   : out std_logic := '0';
    rAxiReset_n : out std_logic := '0';
    rReset_n    : out std_logic := '0'
  );
end rf_reset;


architecture RTL of rf_reset is

  -- POR value for all resets are active high or low.
  signal dResetPulseDly     : std_logic_vector(2 downto 0) := "111";
  signal dResetPulseStretch : std_logic := '1';
  signal pResetPulseStretch : std_logic_vector(1 downto 0) := "11";
  signal pResetPulse_n      : std_logic := '0';
  signal pAxiReset_n        : std_logic := '0';


begin

  -----------------------------------------------------------------------------
  -- Clock Phase Diagram
  -----------------------------------------------------------------------------
  -- Before we look into the details of the clock alignment, here is the clock
  -- frequencies of all the synchronous clocks that is used in the design.
  -- PllRefClk is the reference clock for the FPGA PLL and all other clocks are
  -- derived from PllRefClk. PllRefClk for X410 is ~62.5 MHz
  -- PllRefClk = ~62.5 MHz (Sample clock/48. This is the X410 configuration and
  --                        could be different for other x4xx variants.)
  -- DataClk   = PllRefClk*2
  -- DataClkx2 = PllRefClk*4
  -- RfClk     = PllRefClk*3
  -- RfClkx2   = PllRefClk*6
  -- DataClk = PllRefClk*4 for legacy mode. In legacy mode, we will not use
  -- DataClkx2 as the clock frequency will be too high to close timing.
  -- Five clocks with five different frequencies, all related and occasionally
  -- aligned. Rising edge of all clocks are aligned to the rising edge of
  -- PllRefClk. We will use the rising edge of PllRefClk as the reference to
  -- assert synchronous reset for all clock domains. The synchronous reset
  -- pulse is in the DataClk domain. As we can see from the timing diagram, the
  -- DataClk rising edge is not always aligned to the rising edge of all the
  -- other clocks. But, it is guaranteed that the DataClk will be aligned to
  -- all the other clock on the rising edge of PLL reference clock. In case 1,
  -- the synchronous reset pulse is on the DataClk edge where the data clock is
  -- not aligned to RfClk. We stretch the pulse from DataClk domain and send
  -- the reset out on the rising edge of PllRefClk where all the clocks rising
  -- edge is aligned. In case 2, the synchronous reset is received on the
  -- DataClk cycle where all the clocks are aligned. This is because, in
  -- case 2, the synchronous reset is received on the rising edge of PllRefClk.
  -- For case 1 and case 2, all the output resets are asserted only on the
  -- PllRefClk rising edge to guarantee a known relationship between the resets
  -- in different clock domains.
  --
  --  Alignment    *                       *                       *
  --                ___________             ___________             ___________             ___________             ___________
  --  PllRefClk  __|           |___________|           |___________|           |___________|           |___________|           |
  --                _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _
  --    RfClk2x  __| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_
  --                ___     ___     ___     ___     ___     ___     ___     ___     ___     ___     ___     ___     ___     ___
  --      RfClk  __|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|
  --                __    __    __    __    __    __    __    __    __    __    __    __    __    __    __    __    __    __
  --  DataClk2x  __|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|
  --                _____       _____       _____       _____       _____       _____       _____       _____       _____
  --    DataClk  __|     |_____|     |_____|     |_____|     |_____|     |_____|     |_____|     |_____|     |_____|     |_____|
  --                           .           :                       :                       :                       :
  -- --------- Case 1 ---------.--         :                       :                       :                       :
  --                           ^           :                       :                       ^                       :
  --          Reset Strobe --> |           :                      Aligned reset strobe  -->|                       :
  --                      ____________     :                       :                       :                       :
  --  dResetPulse________|            |______________________________________              :                       :
  --                                       :    _____________________________________________________________________________
  --  dResetPulseStretch ______________________|                   :
  --                                       :                          ________________________________________________
  --  pResetPulseStretch ____________________________________________|                     :                       :  |___
  --                _________________________________________________________________________                      :
  --  pResetPulse_n                        :                                                 |________________________________
  --                                       :                       :                       :                       :
  -- --------- Case 2 -----------          :                       :                       :                       :
  --                                       ^                       :                       ^                       :
  --                      Reset Strobe --> |                       :                       | <-- Aligned reset strobe
  --                                ____________                   :                       :                       :
  --  dResetPulse(0)       ________|            |______________________________________________________________________________
  --                                            _______________________________________________________________________________
  --  dResetPulseStretch ______________________|                   :
  --                                                                  ________________________________________________________
  --  pResetPulseStretch ____________________________________________|                     :
  --                _________________________________________________________________________
  --  pResetPulse_n                                                                          |________________________________
  -- --------------------------------------------------------------------------

  -----------------------------------------------------------------------------
  -- Implementation
  -----------------------------------------------------------------------------

  -- Since the dTimedReset is asserted only for one DataClk cycle, we need to
  -- stretch the strobe to four DataClk cycles, so the strobe is wide enough to
  -- be sampled by PllRefClk which is four times the DataClk period. Pulse
  -- stretch is done for 4 DataClk periods to support the legacy mode. We also
  -- do a logical OR on resets from software. Software resets are from the
  -- ConfigClock domain which is a slower clock than the PllRefClk. So, we
  -- don't have to stretch the software reset.
  PulseStretch: process(DataClk)
  begin
    if rising_edge(DataClk) then
      dResetPulseDly <= dResetPulseDly(1 downto 0) & (dTimedReset or dSwReset);
      dResetPulseStretch <= '0';
      if (dResetPulseDly /= "000") or dTimedReset = '1' or dSwReset = '1' then
        dResetPulseStretch <= '1';
      end if;
    end if;
  end process PulseStretch;

  -- Strobe reset pulse for 2 PllRefClk period to make sure we have the reset
  -- asserted for longer period. The FIR filter is the only design that
  -- requires reset to be asserted for 2 clock cycles. This requirement is
  -- satisfied with one PllRefClk period. RFDC does not have any AXI stream
  -- reset time requirement. We will reset all designs for two PllRefClk period
  -- just to be on the safer side. The same strategy is used for DAC resets as
  -- well.
  ResetOut: process(PllRefClk)
  begin
    if rising_edge(PllRefClk) then
      pResetPulseStretch <= pResetPulseStretch(0) & dResetPulseStretch;
      pResetPulse_n      <= not (pResetPulseStretch(1) or pResetPulseStretch(0));
    end if;
  end process ResetOut;

  -- We are using PllRefClk as the reference and issuing resets to all the
  -- other clock domains. We are not trying to align all the resets in
  -- different clock domains. We are making sure that all resets will be
  -- asserted with respect to each other at the same time from run to run.
  DataClkReset: process(DataClk)
  begin
      if rising_edge(DataClk) then
        dReset_n <= pResetPulse_n;
      end if;
  end process DataClkReset;

  DataClk2xReset: process(DataClk2x)
  begin
      if rising_edge(DataClk2x) then
        d2Reset_n <= pResetPulse_n;
      end if;
  end process DataClk2xReset;

  Rfclk2xReset: process(RfClk2x)
  begin
      if rising_edge(RfClk2x) then
        r2Reset_n <= pResetPulse_n;
      end if;
  end process Rfclk2xReset;

  RfclkReset: process(RfClk)
  begin
      if rising_edge(RfClk) then
        rReset_n <= pResetPulse_n;
      end if;
  end process RfclkReset;

  -------------------------------------
  -- RF Resets
  -------------------------------------
  -- RFDC resets are asserted only once and it should be done using the reset
  -- from software. This is because we want the RFDC AXI-S interface in reset
  -- until the RfClk is stable. The only way to know if the RfClk is stable is
  -- by reading the lock status of sample clock PLL and MMCM used to generate
  -- all clocks in the signal path. dSwReset is a software reset while is
  -- asserted for a longer period of time and it does not require any pulse
  -- stretch.

  RfdcReset: process(PllRefClk)
  begin
      if rising_edge(PllRefClk) then
        pAxiReset_n <= not dSwReset;
      end if;
  end process RfdcReset;

  RfclkAxiReset: process(RfClk)
  begin
      if rising_edge(RfClk) then
        rAxiReset_n <= pAxiReset_n;
      end if;
  end process RfclkAxiReset;

end RTL;