# # Copyright 2023 Ettus Research, a National Instruments Brand # # SPDX-License-Identifier: GPL-3.0-or-later # import unittest import logging from base_tests import TestBase from usrp_mpm.periph_manager.x4xx_clock_policy import X440ClockPolicy from usrp_mpm.periph_manager.x4xx_clock_types import Spll1Vco from usrp_mpm.periph_manager.x4xx_rfdc_ctrl import X4xxRfdcCtrl class TestX440ClockConfig(TestBase): def test_4GHz(self): """ Checking values for 4 GHz converter rate """ log = logging.getLogger() cp = X440ClockPolicy(None, None, {}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) mcr = 2e9 ref_clock_freq = 10e6 clk_config = cp.get_config(ref_clock_freq, (mcr, mcr)) self.assertEqual(clk_config.mmcm_use_defaults, False) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate([mcr,mcr]): self.assertTrue(clk_config.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate = clock_rate * clk_config.rfdc_configs[idx].resampling self.assertEqual(clk_config.rfdc_configs[idx].conv_rate, conv_rate) lmk_vco_rate = clk_config.spll_config.output_divider * clk_config.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n = clk_config.spll_config.pll2_prescaler * clk_config.spll_config.pll2_n_div self.assertEqual(lmk_vco_rate, 100e6 * pll2_n) mmcm_vco_rate = lmk_vco_rate / clk_config.spll_config.prc_divider * clk_config.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r0_clk'], mcr / dsp_info['spc_rx']) self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r1_clk'], mcr / dsp_info['spc_rx']) self.assertEqual(clk_config.spll_config.vcxo_freq, Spll1Vco.VCO100MHz) self.assertEqual(clk_config.spll_config.sysref_div, 1200) self.assertEqual(clk_config.spll_config.clkin0_r_div, 200) self.assertEqual(clk_config.spll_config.pll2_n_cal_div, clk_config.spll_config.pll2_n_div) def test_2_94912GHz(self): """ Checking values for default 2.94912 GHz converter rate """ log = logging.getLogger() cp = X440ClockPolicy(None, None, {}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) mcr = 368.64e6 ref_clock_freq = 10e6 clk_config = cp.get_config(ref_clock_freq, [mcr, mcr]) self.assertEqual(clk_config.mmcm_use_defaults, False) # This should be a rate that we can achieve with PLL bypass: self.assertEqual(clk_config.rfdc_configs[0].conv_rate, clk_config.spll_config.output_freq) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate([mcr,mcr]): self.assertTrue(clk_config.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate = clock_rate * clk_config.rfdc_configs[idx].resampling self.assertEqual(clk_config.rfdc_configs[idx].conv_rate, conv_rate) lmk_vco_rate = clk_config.spll_config.output_divider * clk_config.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n = clk_config.spll_config.pll2_prescaler * clk_config.spll_config.pll2_n_div self.assertEqual(lmk_vco_rate, 122.88e6 * pll2_n) mmcm_vco_rate = lmk_vco_rate / clk_config.spll_config.prc_divider * clk_config.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r0_clk'], mcr / dsp_info['spc_rx']) self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r1_clk'], mcr / dsp_info['spc_rx']) self.assertEqual(clk_config.spll_config.vcxo_freq, Spll1Vco.VCO122_88MHz) self.assertEqual(clk_config.spll_config.sysref_div, 1152) self.assertEqual(clk_config.spll_config.clkin0_r_div, 250) self.assertEqual(clk_config.spll_config.pll2_n_cal_div, clk_config.spll_config.pll2_n_div) def test_253_90625M_Conv_Rate(self): """ Checking values for difficult master clock rate of 253.90625 MHz """ log = logging.getLogger() cp = X440ClockPolicy(None, None, {}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) mcr = {253.90625e6} ref_clock_freq = 10e6 mcr = cp.coerce_mcr(mcr) clk_config = cp.get_config(ref_clock_freq, mcr) self.assertEqual(clk_config.mmcm_use_defaults, False) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate(mcr): self.assertTrue(clk_config.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate = clock_rate * clk_config.rfdc_configs[idx].resampling self.assertEqual(clk_config.rfdc_configs[idx].conv_rate, conv_rate) lmk_vco_rate = clk_config.spll_config.output_divider * clk_config.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n = clk_config.spll_config.pll2_prescaler * clk_config.spll_config.pll2_n_div self.assertEqual(lmk_vco_rate, 100e6 * pll2_n) mmcm_vco_rate = lmk_vco_rate / clk_config.spll_config.prc_divider * clk_config.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r0_clk'], mcr[0] / dsp_info['spc_rx']) self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r1_clk'], mcr[1] / dsp_info['spc_rx']) self.assertEqual(clk_config.spll_config.vcxo_freq, Spll1Vco.VCO100MHz) self.assertEqual(clk_config.spll_config.sysref_div, 6400) self.assertEqual(clk_config.spll_config.clkin0_r_div, 200) self.assertEqual(clk_config.spll_config.pll2_n_cal_div, clk_config.spll_config.pll2_n_div) def test_two_sample_rates(self): """ Checking values for two different master clock rates """ log = logging.getLogger() cp = X440ClockPolicy(None, None, {}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) mcr = (250e6, 1500e6) ref_clock_freq = 10e6 mcr = cp.coerce_mcr(mcr) clk_config = cp.get_config(ref_clock_freq, mcr) self.assertEqual(clk_config.mmcm_use_defaults, False) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate(mcr): self.assertTrue(clk_config.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate = clock_rate * clk_config.rfdc_configs[idx].resampling self.assertEqual(clk_config.rfdc_configs[idx].conv_rate, conv_rate) lmk_vco_rate = clk_config.spll_config.output_divider * clk_config.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n = clk_config.spll_config.pll2_prescaler * clk_config.spll_config.pll2_n_div self.assertEqual(lmk_vco_rate, 100e6 * pll2_n) mmcm_vco_rate = lmk_vco_rate / clk_config.spll_config.prc_divider * clk_config.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r0_clk'], mcr[0] / dsp_info['spc_rx']) self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r1_clk'], mcr[1] / dsp_info['spc_rx']) self.assertEqual(clk_config.spll_config.vcxo_freq, Spll1Vco.VCO100MHz) self.assertEqual(clk_config.spll_config.sysref_div, 2400) self.assertEqual(clk_config.spll_config.clkin0_r_div, 200) self.assertEqual(clk_config.spll_config.pll2_n_cal_div, clk_config.spll_config.pll2_n_div) def test_two_sample_rates_inverted(self): """ Checking that configurations are only mirrored but not completely different if the two MCRs are swapped """ log = logging.getLogger() cp = X440ClockPolicy(None, None, {}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) mcr0 = [250e6, 1500e6] mcr1 = mcr0.copy() mcr1.reverse() ref_clock_freq = 10e6 mcr0 = cp.coerce_mcr(mcr0) mcr1 = cp.coerce_mcr(mcr1) clk_config0 = cp.get_config(ref_clock_freq, mcr0) clk_config1 = cp.get_config(ref_clock_freq, mcr1) self.assertEqual(clk_config0.mmcm_use_defaults, False) self.assertEqual(clk_config1.mmcm_use_defaults, False) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate(mcr0): self.assertTrue(clk_config0.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate0 = clock_rate * clk_config0.rfdc_configs[idx].resampling conv_rate1 = mcr1[1-idx] * clk_config1.rfdc_configs[1-idx].resampling self.assertEqual(conv_rate0, conv_rate1) lmk_vco_rate = clk_config0.spll_config.output_divider * clk_config1.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n0 = clk_config0.spll_config.pll2_prescaler * clk_config0.spll_config.pll2_n_div pll2_n1 = clk_config1.spll_config.pll2_prescaler * clk_config1.spll_config.pll2_n_div self.assertEqual(pll2_n0, pll2_n1) mmcm_vco_rate0 = lmk_vco_rate / clk_config0.spll_config.prc_divider * clk_config0.mmcm_feedback_divider mmcm_vco_rate1 = lmk_vco_rate / clk_config1.spll_config.prc_divider * clk_config1.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate0, mmcm_vco_rate1) self.assertEqual(clk_config0.spll_config.sysref_div, clk_config1.spll_config.sysref_div) self.assertEqual(clk_config0.spll_config.clkin0_r_div, clk_config1.spll_config.clkin0_r_div) self.assertEqual(clk_config0.mmcm_output_div_map['r0_clk'], clk_config1.mmcm_output_div_map['r1_clk']) self.assertEqual(clk_config0.mmcm_output_div_map['r1_clk'], clk_config1.mmcm_output_div_map['r0_clk']) self.assertEqual(clk_config0.spll_config.vcxo_freq, clk_config1.spll_config.vcxo_freq) self.assertEqual(clk_config0.rfdc_configs[0], clk_config1.rfdc_configs[1]) self.assertEqual(clk_config0.rfdc_configs[1], clk_config1.rfdc_configs[0]) def test_two_incompatible_sample_rates(self): """ Checking that for incompatible sample rates we fall back to MCR0 """ log = logging.getLogger() cp = X440ClockPolicy(None, None, {}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) mcr = (1000e6, 368.64e6) ref_clock_freq = 10e6 mcr = cp.coerce_mcr(mcr) # ...but since values are incompatible, we can focus on #0 clk_config = cp.get_config(ref_clock_freq, mcr) self.assertEqual(clk_config.mmcm_use_defaults, False) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate(mcr): self.assertTrue(clk_config.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate = clock_rate * clk_config.rfdc_configs[idx].resampling self.assertEqual(clk_config.rfdc_configs[idx].conv_rate, conv_rate) lmk_vco_rate = clk_config.spll_config.output_divider * clk_config.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n = clk_config.spll_config.pll2_prescaler * clk_config.spll_config.pll2_n_div self.assertEqual(lmk_vco_rate, 100e6 * pll2_n) mmcm_vco_rate = lmk_vco_rate / clk_config.spll_config.prc_divider * clk_config.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r0_clk'], mcr[0] / dsp_info['spc_rx']) self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r1_clk'], mcr[1] / dsp_info['spc_rx']) self.assertEqual(clk_config.spll_config.vcxo_freq, Spll1Vco.VCO100MHz) self.assertEqual(clk_config.spll_config.sysref_div, 1200) self.assertEqual(clk_config.spll_config.clkin0_r_div, 200) self.assertEqual(clk_config.spll_config.pll2_n_cal_div, clk_config.spll_config.pll2_n_div) def test_converter_rate_arg(self): """ Checks if the converter_rate argument is used. """ log = logging.getLogger() cr = 2e9 cp = X440ClockPolicy(None, None, {'converter_rate':cr}, log) dsp_info = { 'num_rx_chans': 4, 'num_tx_chans': 4, 'bw': 1600, 'extra_resampling': 1, 'spc_rx': 8, 'spc_tx': 8, } cp.set_dsp_info([dsp_info, dsp_info]) # In theory, for MCR=500e6, the Conv_Rate should go to 4e9. By passing the conv_rate # argument we should be able to influence this. mcr = (500e6,) ref_clock_freq = 10e6 mcr = cp.coerce_mcr(mcr) clk_config = cp.get_config(ref_clock_freq, mcr) self.assertEqual(clk_config.mmcm_use_defaults, False) # We need to calculate if the values lead to what we want for idx, clock_rate in enumerate(mcr): self.assertTrue(clk_config.rfdc_configs[idx].resampling in X4xxRfdcCtrl.RFDC_RESAMPLER) conv_rate = clock_rate * clk_config.rfdc_configs[idx].resampling self.assertEqual(conv_rate, cr) self.assertEqual(clk_config.rfdc_configs[idx].conv_rate, conv_rate) lmk_vco_rate = clk_config.spll_config.output_divider * clk_config.spll_config.output_freq # Overall PLL2 N divider as combination of prescalar and n div pll2_n = clk_config.spll_config.pll2_prescaler * clk_config.spll_config.pll2_n_div self.assertEqual(lmk_vco_rate, 100e6 * pll2_n) mmcm_vco_rate = lmk_vco_rate / clk_config.spll_config.prc_divider * clk_config.mmcm_feedback_divider self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r0_clk'], mcr[0] / dsp_info['spc_rx']) self.assertEqual(mmcm_vco_rate / clk_config.mmcm_output_div_map['r1_clk'], mcr[1] / dsp_info['spc_rx']) self.assertEqual(clk_config.spll_config.vcxo_freq, Spll1Vco.VCO100MHz) self.assertEqual(clk_config.spll_config.sysref_div, 1200) self.assertEqual(clk_config.spll_config.clkin0_r_div, 200) self.assertEqual(clk_config.spll_config.pll2_n_cal_div, clk_config.spll_config.pll2_n_div)