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

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

library WORK;
  use WORK.PkgRFDC_REGS_REGMAP.all;

entity rf_reset_controller is
  port(
    -- Clocks
    -- Config clock is async to all the others.
    ConfigClk          : in  std_logic;
    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
    dAdcResetPulse     : in  std_logic;
    dDacResetPulse     : in  std_logic;

    -- ADC Resets
    dAdcDataOutReset_n : out std_logic;
    r2AdcFirReset_n    : out std_logic;
    rAdcRfdcAxiReset_n : out std_logic;
    rAdcEnableData     : out std_logic;
    rAdcGearboxReset_n : out std_logic;

    -- DAC Resets
    dDacDataInReset_n  : out std_logic;
    r2DacFirReset_n    : out std_logic;
    d2DacFirReset_n    : out std_logic;
    rDacRfdcAxiReset_n : out std_logic;
    rDacGearboxReset_n : out std_logic;

    -- SW Control and Status
    -- Control to initiate resets to RFDC and decimation block including the
    -- gearboxes. The reset status is a sticky status of both ADC and DAC.
    cSoftwareControl   : in  std_logic_vector(31 downto 0);
    cSoftwareStatus    : out std_logic_vector(31 downto 0)
  );
end rf_reset_controller;


architecture RTL of rf_reset_controller is

  -- POR value for all resets are high.
  signal cTriggerAdcReset     : std_logic := '1';
  signal cTriggerAdcResetDlyd : std_logic := '1';
  signal cTriggerDacReset     : std_logic := '1';
  signal cTriggerDacResetDlyd : std_logic := '1';

  signal dTriggerAdcReset_ms : std_logic := '1';
  signal dTriggerAdcReset    : std_logic := '1';
  signal dTriggerDacReset_ms : std_logic := '1';
  signal dTriggerDacReset    : std_logic := '1';

  -- POR value of all reset done signals are set to low.
  signal cTriggerAdcResetDone_ms : std_logic := '0';
  signal cTriggerAdcResetDone    : std_logic := '0';
  signal cAdcResetDoneSticky     : std_logic := '0';
  signal cTriggerDacResetDone_ms : std_logic := '0';
  signal cTriggerDacResetDone    : std_logic := '0';
  signal cDacResetDoneSticky     : std_logic := '0';

  attribute ASYNC_REG : string;
  attribute ASYNC_REG of dTriggerAdcReset        : signal is "TRUE";
  attribute ASYNC_REG of dTriggerDacReset        : signal is "TRUE";
  attribute ASYNC_REG of cTriggerAdcResetDone    : signal is "TRUE";
  attribute ASYNC_REG of cTriggerDacResetDone    : signal is "TRUE";
  attribute ASYNC_REG of dTriggerAdcReset_ms     : signal is "TRUE";
  attribute ASYNC_REG of dTriggerDacReset_ms     : signal is "TRUE";
  attribute ASYNC_REG of cTriggerAdcResetDone_ms : signal is "TRUE";
  attribute ASYNC_REG of cTriggerDacResetDone_ms : signal is "TRUE";

begin

  -- rAdcEnableData is set to '1' as we don't control the flow of RX data.
  rAdcEnableData <= '1';

  cTriggerAdcReset <= cSoftwareControl(kADC_RESET);
  cTriggerDacReset <= cSoftwareControl(kDAC_RESET);

  cSoftwareStatus <= (
                       kADC_SEQ_DONE => cAdcResetDoneSticky,
                       kDAC_SEQ_DONE => cDacResetDoneSticky,
                       others => '0'
                     );

  -----------------------------------------------------------------------------
  -- High-Level Resets Using ConfigClk
  -----------------------------------------------------------------------------
  -- Pass the master FSM reset around to the other clock domains and then
  -- return them back to the ConfigClk domain. This is also a handy way to
  -- prove all your clocks are toggling to some extent.
  -----------------------------------------------------------------------------

  SeqResetDataClk : process(DataClk)
  begin
    if rising_edge(DataClk) then
      -- double-syncs have no sync reset!
      dTriggerAdcReset_ms  <= cTriggerAdcReset;
      dTriggerAdcReset     <= dTriggerAdcReset_ms;
      dTriggerDacReset_ms  <= cTriggerDacReset;
      dTriggerDacReset     <= dTriggerDacReset_ms;
    end if;
  end process;

  -----------------------------------------------------------------------------
  -- Reset Sequence Done Status
  -----------------------------------------------------------------------------
  -- Now back to ConfigClk! We provide the status for all software controlled
  -- resets. We move the signal from ConfigClk to DataClk domain and move it
  -- back to ConfigClk domain. This just proves that DataClk is toggling and
  -- the reset requested by software is sampled in the DataClk.
  -----------------------------------------------------------------------------

  SeqResetDone : process(ConfigClk)
  begin
    if rising_edge(ConfigClk) then
      -- double-syncs have no sync reset!
      cTriggerAdcResetDone_ms   <= dTriggerAdcReset;
      cTriggerAdcResetDone      <= cTriggerAdcResetDone_ms;
      cTriggerDacResetDone_ms   <= dTriggerDacReset;
      cTriggerDacResetDone      <= cTriggerDacResetDone_ms;
    end if;
  end process;

  -- ADC reset done
  SwAdcResetDone: process(ConfigClk)
  begin
    if rising_edge(ConfigClk) then
      cTriggerAdcResetDlyd <= cTriggerAdcReset;
      -- De-assert reset status on the rising edge of SW ADC reset.
      if cTriggerAdcReset = '1' and cTriggerAdcResetDlyd = '0' then
        cAdcResetDoneSticky <= '0';
      -- Assert and hold the ADC reset status on ADC reset strobe.
      elsif cTriggerAdcResetDone = '1' then
        cAdcResetDoneSticky <= '1';
      end if;
    end if;
  end process SwAdcResetDone;

  -- DAC reset done
  SwDacResetDone: process(ConfigClk)
  begin
    if rising_edge(ConfigClk) then
      cTriggerDacResetDlyd <= cTriggerDacReset;
      -- De-assert reset status on the rising edge of SW DAC reset.
      if cTriggerDacReset = '1' and cTriggerDacResetDlyd = '0' then
        cDacResetDoneSticky <= '0';
      -- Assert and hold the DAC reset status on DAC reset strobe.
      elsif cTriggerDacResetDone = '1' then
        cDacResetDoneSticky <= '1';
      end if;
    end if;
  end process SwDacResetDone;

  -----------------------------------------------------------------------------
  -- rf_reset Instances
  -----------------------------------------------------------------------------

  AdcResets: entity work.rf_reset (RTL)
    port map (
      DataClk     => DataClk,
      PllRefClk   => PllRefClk,
      RfClk       => RfClk,
      RfClk2x     => RfClk2x,
      DataClk2x   => DataClk2x,
      dTimedReset => dAdcResetPulse,
      dSwReset    => dTriggerAdcReset,
      dReset_n    => dAdcDataOutReset_n,
      d2Reset_n   => open,
      r2Reset_n   => r2AdcFirReset_n,
      rAxiReset_n => rAdcRfdcAxiReset_n,
      rReset_n    => rAdcGearboxReset_n
    );

  DacResets: entity work.rf_reset (RTL)
    port map (
      DataClk     => DataClk,
      PllRefClk   => PllRefClk,
      RfClk       => RfClk,
      RfClk2x     => RfClk2x,
      DataClk2x   => DataClk2x,
      dTimedReset => dDacResetPulse,
      dSwReset    => dTriggerDacReset,
      dReset_n    => dDacDataInReset_n,
      d2Reset_n   => d2DacFirReset_n,
      r2Reset_n   => r2DacFirReset_n,
      rAxiReset_n => rDacRfdcAxiReset_n,
      rReset_n    => rDacGearboxReset_n
    );

end RTL;