diff options
Diffstat (limited to 'fpga/usrp3/top/x400/rf/sim/tb_rf_nco_reset.vhd')
-rw-r--r-- | fpga/usrp3/top/x400/rf/sim/tb_rf_nco_reset.vhd | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/rf/sim/tb_rf_nco_reset.vhd b/fpga/usrp3/top/x400/rf/sim/tb_rf_nco_reset.vhd new file mode 100644 index 000000000..066dd5f4b --- /dev/null +++ b/fpga/usrp3/top/x400/rf/sim/tb_rf_nco_reset.vhd @@ -0,0 +1,281 @@ +-- +-- Copyright 2021 Ettus Research, a National Instruments Brand +-- +-- SPDX-License-Identifier: LGPL-3.0-or-later +-- +-- Module: tb_rf_nco_reset +-- +-- Description: +-- +-- Self-checking testbench for NCO reset sequencing. +-- + +library IEEE; + use IEEE.std_logic_1164.all; + use IEEE.numeric_std.all; + +entity tb_rf_nco_reset is +end tb_rf_nco_reset; + + +architecture RTL of tb_rf_nco_reset is + + signal cAdc0xNcoUpdateReq : std_logic; + signal cAdc2xNcoUpdateReq : std_logic; + signal cDac0xNcoUpdateReq : std_logic; + signal cDac0xSysrefIntGating : std_logic; + signal cDac0xSysrefIntReenable : std_logic; + signal cDac1xNcoUpdateReq : std_logic; + signal cNcoPhaseRst : std_logic; + signal cNcoUpdateEn : std_logic_vector(5 downto 0); + signal dNcoResetDone : std_logic; + + signal cDac0xNcoUpdateBusy : std_logic_vector(1 downto 0) := "00"; + signal dStartNcoReset : std_logic := '0'; + signal cAdc0xNcoUpdateBusy : std_logic := '0'; + signal cAdc2xNcoUpdateBusy : std_logic := '0'; + signal cDac1xNcoUpdateBusy : std_logic := '0'; + + signal cSysref_ms, cSysref : std_logic := '0'; + signal cSysrefDlyd : std_logic_vector(1 downto 0) := "00"; + signal cDac0xSysrefIntGatingDlyd : std_logic := '0'; + signal cNcoPhaseRstDlyd : std_logic_vector(2 downto 0) := "000"; + + signal cWrCount : integer := 0; + type RfdcNcoState_t is (Idle, GateSysref, UpdateReq, CheckUpdate, + SysrefEn, WaitForSysref, ResetDone); + signal cRfdcNcoState : RfdcNcoState_t := Idle; + + signal StopSim : boolean; + constant kConfigClkPer : time := 25 ns; + -- SYSREF period is 2.5 MHz. + constant kSysrefPer : time := 400 ns; + -- DataClk period is 125 MHz and generated from the same clocking chip that + -- generated SYSREF and are related. + constant kDataClkPer : time := kSysrefPer/50; + + signal ConfigClk : std_logic := '0'; + signal DataClk : std_logic := '0'; + signal dSysref : std_logic := '0'; + + procedure DataClkWait(X : positive := 1) is + begin + for i in 1 to X loop + wait until rising_edge(DataClk); + end loop; + end procedure DataClkWait; + + procedure ConfigClkWait(X : positive := 1) is + begin + for i in 1 to X loop + wait until rising_edge(ConfigClk); + end loop; + end procedure ConfigClkWait; + + procedure SysrefWait(X : positive := 1) is + begin + for i in 1 to X loop + wait until rising_edge(dSysref); + end loop; + end procedure SysrefWait; + +begin + + ConfigClk <= not ConfigClk after kConfigClkPer/2 when not StopSim else '0'; + DataClk <= not DataClk after kDataClkPer/2 when not StopSim else '0'; + dSysref <= not dSysref after kSysrefPer/2 when not StopSim else '0'; + + -- Both cNcoPhaseRst and cNcoUpdateEn are constants in the DUT. + dut: entity WORK.rf_nco_reset (RTL) + port map ( + ConfigClk => ConfigClk, + DataClk => DataClk, + dSysref => dSysref, + dStartNcoReset => dStartNcoReset, + cDac0xNcoUpdateBusy => cDac0xNcoUpdateBusy, + cDac0xNcoUpdateReq => cDac0xNcoUpdateReq, + cDac0xSysrefIntGating => cDac0xSysrefIntGating, + cDac0xSysrefIntReenable => cDac0xSysrefIntReenable, + cDac1xNcoUpdateBusy => cDac1xNcoUpdateBusy, + cDac1xNcoUpdateReq => cDac1xNcoUpdateReq, + cAdc0xNcoUpdateBusy => cAdc0xNcoUpdateBusy, + cAdc0xNcoUpdateReq => cAdc0xNcoUpdateReq, + cAdc2xNcoUpdateBusy => cAdc2xNcoUpdateBusy, + cAdc2xNcoUpdateReq => cAdc2xNcoUpdateReq, + cNcoPhaseRst => cNcoPhaseRst, + cNcoUpdateEn => cNcoUpdateEn, + dNcoResetDone => dNcoResetDone + ); + + main: process + + -- Procedure to sweep the entire SYSREF period. + -- When we strobe dStartNcoReset for one DataClk cycle. NCO reset sequence + -- is initiated. In this procedure, we sweep the dStartNcoReset strobe the + -- entire SYSREF cycle. + procedure SysrefSweep is + constant kSysrefInRfCycles : integer := kSysrefPer/kDataClkPer; + begin + for i in 1 to kSysrefInRfCycles loop + wait until cDac0xSysrefIntGating = '0' for 1 us; + assert cDac0xSysrefIntGating = '0' + report "NCO phase reset does not de-assert" + severity error; + SysrefWait; + DataClkWait(i); + dStartNcoReset <= '0'; + DataClkWait; + dStartNcoReset <= '1'; + DataClkWait; + dStartNcoReset <= '0'; + -- Wait for a minimum of 3 SYSREF period. 1 SYSREF edge is used to + -- initiate NCO reset, 1 SYSREF edge is used to re-enable SYSREF and 1 + -- SYSREF edge is used by RFDC to reset all NCOs. + SysrefWait(3); + end loop; + end procedure; + + begin + + -- Strobe dStartNcoReset across entire SYSREF period. + SysrefSweep; + -- Wait for a minimum of 3 SYSREF cycles to make sure NCO reset is complete. + SysrefWait(3); + + StopSim <= true; + wait; + end process; + + -- Process to mimic RFDC NCO reset + -- This state machine is based of "NCO frequency hopping" section in PG269 + -- (v2.2). Refer to multi-mode subsection for more details. + MimicRfdc: process(ConfigClk) + begin + if falling_edge(ConfigClk) then + cRfdcNcoState <= Idle; + case cRfdcNcoState is + + -- Wait until SYSREF internal gating is asserted. + when Idle => + cWrCount <= 0; + if cDac0xSysrefIntGating = '1' then + cRfdcNcoState <= GateSysref; + end if; + + -- Change cDac0xNcoUpdateBusy to "11" to indicate SYSREF is gated + -- internally when NCO update is requested on DAC tile 228. + -- cDac0xNcoUpdateBusy(0) is set to '1', the SYSREF is gated and + -- cDac0xNcoUpdateBusy(1) is set to '1', to indicate the NCO reset + -- process has started, but not complete. + when GateSysref => + cRfdcNcoState <= GateSysref; + if cDac0xNcoUpdateReq = '1' then + cRfdcNcoState <= UpdateReq; + cDac0xNcoUpdateBusy <= "11"; + end if; + + -- If NCO reset is requested on other tiles, assert NCO update busy on + -- other tiles as well. + when UpdateReq => + cRfdcNcoState <= CheckUpdate; + cDac1xNcoUpdateBusy <= cDac1xNcoUpdateReq; + cAdc0xNcoUpdateBusy <= cAdc0xNcoUpdateReq; + cAdc2xNcoUpdateBusy <= cAdc2xNcoUpdateReq; + + -- It takes 5 clock cycles to update each RFDC internal registers with + -- the used request change. In rf_nco_reset entity, we only want to + -- reset the NCO, which is a single bit. So, it should take only 5 + -- ConfigClk for the update. When the internal register is updated, set + -- cDac0xNcoUpdateBusy(0) to '0'. + when CheckUpdate => + cRfdcNcoState <= CheckUpdate; + if cWrCount > 4 then + cRfdcNcoState <= SysrefEn; + cDac0xNcoUpdateBusy <= "10"; --Indicates that SYSREF is gated. + cDac1xNcoUpdateBusy <= '0'; + cAdc0xNcoUpdateBusy <= '0'; + cAdc2xNcoUpdateBusy <= '0'; + end if; + cWrCount <= cWrCount + 1; + + -- Wait until internal SYSREF gating is disabled. + when SysrefEn => + cWrCount <= 0; + cRfdcNcoState <= SysrefEn; + if cDac0xSysrefIntReenable = '1' then + if cSysrefDlyd(0) = '0' and cSysref = '1' then + cDac0xNcoUpdateBusy <= "00"; --Indicates that NCO reset is complete. + cRfdcNcoState <= ResetDone; + else + cRfdcNcoState <= WaitForSysref; + end if; + end if; + + -- NCO reset is done on the rising edge of SYSREF. When NCO reset is + -- complete, set cDac0xNcoUpdateBusy(1) to '0'. + when WaitForSysref => + cRfdcNcoState <= WaitForSysref; + if cSysrefDlyd(0) = '0' and cSysref = '1' then + cDac0xNcoUpdateBusy <= "00"; --Indicates that NCO reset is complete. + cRfdcNcoState <= ResetDone; + end if; + + -- Wait in this state, until the next NCO reset is requested. + when ResetDone => + cRfdcNcoState <= ResetDone; + if cDac0xSysrefIntGating = '1' then + cRfdcNcoState <= GateSysref; + end if; + end case; + end if; + end process; + + -- SYSREF clock crossing from DataClk to ConfigClk and some pipelines. + ConfigClkSysref: process(ConfigClk) + begin + if rising_edge(ConfigClk) then + cSysref_ms <= dSysref; + cSysref <= cSysref_ms; + cSysrefDlyd <= cSysrefDlyd(cSysrefDlyd'high-1) & cSysref; + cDac0xSysrefIntGatingDlyd <= cDac0xSysrefIntGating; + cNcoPhaseRstDlyd <= cNcoPhaseRstDlyd(cNcoPhaseRstDlyd'high downto 1) + & cDac0xNcoUpdateBusy(1); + end if; + end process; + + -- Assertions + process(ConfigClk) + begin + if falling_edge(ConfigClk) then + + --Check if cNcoPhaseRst is a constant of '1'. + assert cNcoPhaseRst = '1' + report "NCO phase reset signal should be constant." + severity error; + -- Check if cNcoUpdateEn is a constant of "100000". + assert cNcoUpdateEn = "100000" + report "NCO phase reset signal should be constant." + severity error; + -- Check if NCO reset was requested on the rising edge of SYSREF. + if cDac0xSysrefIntGating = '1' and cDac0xSysrefIntGatingDlyd = '0' then + assert cSysrefDlyd = "01" + report "NCO reset did not start on SYSREF rising edge" + severity error; + end if; + + -- We wait for couple of clock cycles after NCO done signal is toggled in + -- from the RFDC. RFDC uses cDac0xNcoUpdateBusy(1) to indicate NCO reset + -- process is done. It is important to wait a minimum of three clock + -- cycles before this check is done. This wait is needed for clock + -- crossing. + if cNcoPhaseRstDlyd(2) = '1' and cNcoPhaseRstDlyd(1) = '0' then + assert dNcoResetDone = '1' + report "NCO Reset done should have been asserted after NCO " & + "reset request is de-asserted" + severity error; + end if; + + end if; + end process; + +end RTL; |