-- -- Copyright 2021 Ettus Research, a National Instruments Brand -- -- SPDX-License-Identifier: LGPL-3.0-or-later -- -- Module: rf_nco_reset -- -- Description: -- -- This entity has the logic needed to synchronously reset the NCO inside the -- RF section. -- library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; entity rf_nco_reset is port( -- AXI-lite clock used for RFDC configuration. ConfigClk : in std_logic; -- Radio clock used in the converter data path. DataClk : in std_logic; -- PL SYSREF dSysref : in std_logic; --Strobe dNcoResetEn for one DataClk cycle to initiate NCO reset. dStartNcoReset : in std_logic; --------------------------------------------------------------------------- -- NCO reset controls and status --------------------------------------------------------------------------- -- Port naming convention: -- cDac -- cAdc ----------------------------------- -- DAC Tile 228 ----------------------------------- -- DAC common NCO update controls and status. cDac0xNcoUpdateBusy : in std_logic_vector(1 downto 0); cDac0xNcoUpdateReq : out std_logic := '0'; cDac0xSysrefIntGating : out std_logic := '0'; cDac0xSysrefIntReenable : out std_logic := '0'; ----------------------------------- -- DAC Tile 229 ----------------------------------- -- DAC common NCO update controls and status. cDac1xNcoUpdateBusy : in std_logic; cDac1xNcoUpdateReq : out std_logic := '0'; ----------------------------------- --ADC Tile 224 ----------------------------------- -- ADC common NCO update controls and status. cAdc0xNcoUpdateBusy : in std_logic; cAdc0xNcoUpdateReq : out std_logic := '0'; ----------------------------------- --ADC Tile 226 ----------------------------------- -- ADC common NCO update controls and status. cAdc2xNcoUpdateBusy : in std_logic; cAdc2xNcoUpdateReq : out std_logic := '0'; -- NCO reset can be initiated only when cNcoPhaseRst is set to '1' and -- cNcoUpdateEn = 0x20. The FSM in this entity will set these values when -- an NCO reset is initiated during synchronization. These ports are common -- for all the converters. So, we will fan these signals out to each -- converter outside this entity. cNcoPhaseRst : out std_logic := '1'; cNcoUpdateEn : out std_logic_vector(5 downto 0) := "100000"; -- NCO reset status back to the user. dNcoResetDone : out std_logic := '0' ); end rf_nco_reset; architecture RTL of rf_nco_reset is -- State machine to sequence NCO reset across different RFDC tiles. type ResetState_t is (Idle, ReqGating, CheckGating, CheckUpdateDone, CheckResetDone, ResetDone); signal cResetState : ResetState_t := Idle; signal dNcoResetDone_ms, cNcoResetDone : std_logic := '0'; signal dStartNcoResetReg, cStartNcoReset_ms, cStartNcoReset : std_logic := '0'; signal cSysref_ms, cSysref, cSysrefDlyd : std_logic := '0'; signal cSysrefIntGating, dSysrefIntGating_ms, dSysrefIntGating : std_logic := '0'; begin -- NCO start signal from the user is a one DataClk cycle strobe. In this -- process, we register the NCO start request from the user. This NCO start -- request register is cleared after the NCO reset sequence is initiated. We -- used the signal used to gate SYSREF to clear this register. RegNcoStart: process(DataClk) begin if rising_edge(DataClk) then dSysrefIntGating_ms <= cSysrefIntGating; dSysrefIntGating <= dSysrefIntGating_ms; if dSysrefIntGating = '1' then dStartNcoResetReg <= '0'; elsif dStartNcoReset = '1' then dStartNcoResetReg <= '1'; end if; end if; end process RegNcoStart; -- Irrespective of when NCO reset strobe is issued by the user, we need to -- initiate NCO reset only on the rising edge of SYSREF. This is because, we -- have to complete the reset within a SYSREF period. ConfigClkCross: process(ConfigClk) begin if rising_edge(ConfigClk) then cSysref_ms <= dSysref; cSysref <= cSysref_ms; cSysrefDlyd <= cSysref; cStartNcoReset_ms <= dStartNcoResetReg; cStartNcoReset <= cStartNcoReset_ms; end if; end process ConfigClkCross; -- These signals can be set to a constant value as NCO phase reset is only -- initiated by *NcoUpdateReq signal. cNcoPhaseRst <= '1'; cNcoUpdateEn <= "100000"; -- ! STATE MACHINE STARTUP ! -- The state machine starts in Idle state and does not change state until -- cStartNcoReset is set to '1'. cStartNcoReset signal and cSysref are based -- of ConfigClock so changing state from Idle cannot go metastable. State -- machine to initiate NCO reset on all enabled RFDC tiles. This state -- machine was written based of the information provided in "NCO frequency -- hopping" section in PG269 (v2.2). We use multi-mode for NCO reset. ResetFsm: process(ConfigClk) begin if rising_edge(ConfigClk) then cResetState <= Idle; cNcoResetDone <= '0'; cDac0xNcoUpdateReq <= '0'; cSysrefIntGating <= '0'; cDac0xSysrefIntReenable <= '0'; cDac1xNcoUpdateReq <= '0'; cAdc0xNcoUpdateReq <= '0'; cAdc2xNcoUpdateReq <= '0'; case cResetState is -- Stay in this state until NCO reset sequence is initiated. NCO reset -- is initiated only on the rising edge of SYSREF. when Idle => if cSysref = '1' and cSysrefDlyd = '0' and cStartNcoReset = '1' then cResetState <= ReqGating; cSysrefIntGating <= '1'; end if; -- When NCO reset is initiated, gate the RFDC internal SYSREF. To gate -- internal SYSREF set cSysrefIntGating to '1'. To request NCO reset -- strobe cDac0xNcoUpdateReq for one ConfigClk period. At this point, -- we can only request NCO reset for RF-DAC tile 228. when ReqGating => cResetState <= CheckGating; cDac0xNcoUpdateReq <= '1'; cSysrefIntGating <= '1'; -- Since we are gating SYSREF inside RFDC, we need to wait until SYSREF -- is gated internally. RFDC sets cDac0xNcoUpdateBusy[0] to '1' when -- SYSREF is gated. cDac0xNcoUpdateBusy[1] is also set to '1' to -- indicate that NCO reset is still in progress. After the SYSREF is -- gated request NCO reset on all other converter tiles. when CheckGating => cSysrefIntGating <= '1'; cResetState <= CheckGating; if cDac0xNcoUpdateBusy = "11" then cResetState <= CheckUpdateDone; cDac1xNcoUpdateReq <= '1'; cAdc0xNcoUpdateReq <= '1'; cAdc2xNcoUpdateReq <= '1'; end if; -- In this state, we check if the RFDC block is ready for NCO reset. -- This check is done using the *Busy signal from RFDC. Once RFDC is -- ready for NCO reset, disable internal SYSREF gating. when CheckUpdateDone => cSysrefIntGating <= '1'; cResetState <= CheckUpdateDone; if cDac0xNcoUpdateBusy = "10" and cAdc0xNcoUpdateBusy = '0' and cAdc2xNcoUpdateBusy = '0' and cDac1xNcoUpdateBusy = '0' and cSysref = '1' and cSysrefDlyd = '0' then cDac0xSysrefIntReenable <= '1'; cResetState <= CheckResetDone; end if; -- NCO reset is done when cDac0xNcoUpdateBusy[1] is set to '0'. RFDC is -- programmed from software to reset the NCO on a SYSREF rising edge. when CheckResetDone => cSysrefIntGating <= '1'; cResetState <= CheckResetDone; if cDac0xNcoUpdateBusy = "00" then cResetState <= ResetDone; end if; -- Wait in this state until another NCO reset request is issued. when ResetDone => cNcoResetDone <= '1'; cResetState <= ResetDone; if cSysref = '1' and cSysrefDlyd = '0' and cStartNcoReset = '1' then cResetState <= ReqGating; cSysrefIntGating <= '1'; end if; end case; end if; end process ResetFsm; cDac0xSysrefIntGating <= cSysrefIntGating; -- Move the NCO reset done status to DataClk domain. DataClkCrossing: process(DataClk) begin if rising_edge(DataClk) then dNcoResetDone_ms <= cNcoResetDone; dNcoResetDone <= dNcoResetDone_ms; end if; end process DataClkCrossing; end RTL;