From a8ad4917d331258e163e1786c0325a4b7e7d2e3e Mon Sep 17 00:00:00 2001 From: mattprost Date: Wed, 6 Apr 2022 14:00:06 -0500 Subject: n310: Add frontend bandwidth control Allow users to control the Mykonos frontend bandwidth settings for Rx and Tx. Note that this operation requires the daughterboard to re-initialize, so it may take some time. Values for frontend filter settings were derived using ADI's AD9371 Filter Wizard. This feature requires MPM version 4.1 or later on the device. Co-authored-by: bpadalino Signed-off-by: mattprost --- .../dboard/magnesium/magnesium_ad9371_iface.cpp | 47 ++++++++++--- .../dboard/magnesium/magnesium_radio_control.cpp | 81 ++++++++++++++++++---- .../dboard/magnesium/magnesium_radio_control.hpp | 4 ++ mpm/include/mpm/ad937x/ad937x_ctrl.hpp | 7 +- mpm/lib/mykonos/ad937x_device.cpp | 43 +++++++++--- mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp | 1 + mpm/python/usrp_mpm/dboard_manager/magnesium.py | 12 ++++ mpm/python/usrp_mpm/dboard_manager/mg_init.py | 4 ++ mpm/python/usrp_mpm/rpc_server.py | 2 +- 9 files changed, 164 insertions(+), 37 deletions(-) diff --git a/host/lib/usrp/dboard/magnesium/magnesium_ad9371_iface.cpp b/host/lib/usrp/dboard/magnesium/magnesium_ad9371_iface.cpp index 3683fae47..3bb0aeea6 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_ad9371_iface.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_ad9371_iface.cpp @@ -17,9 +17,23 @@ namespace { */ std::string _get_which(const direction_t dir, const size_t chan) { - UHD_ASSERT_THROW(dir == RX_DIRECTION or dir == TX_DIRECTION); - UHD_ASSERT_THROW(chan == 0 or chan == 1); - return str(boost::format("%s%d") % (dir == RX_DIRECTION ? "RX" : "TX") % (chan + 1)); + UHD_ASSERT_THROW(dir == RX_DIRECTION || dir == TX_DIRECTION); + UHD_ASSERT_THROW(chan == 0 || chan == 1); + return (dir == RX_DIRECTION ? "RX" : "TX") + std::to_string(chan + 1); +} + +/*! Return a valid 'which' string for use with AD9371 API duplex calls + * + * Extends _get_which by additionally allowing duplex values + */ +std::string _get_which_duplex(const direction_t dir, const size_t chan) +{ + if (dir == RX_DIRECTION || dir == TX_DIRECTION) { + return _get_which(dir, chan); + } + UHD_ASSERT_THROW(dir == DX_DIRECTION); + UHD_ASSERT_THROW(chan == 0 || chan == 1); + return "DX" + std::to_string(chan + 1); } } // namespace @@ -59,12 +73,23 @@ double magnesium_ad9371_iface::set_gain( // return 0.0; } +/*! brief Sets the frontend bandwidth settings for the dboard. Requires + * re-initializing the dboard, so it may take a significant amount of time. + * + * \param bandwidth target rf bandwidth value + * \param chan -not important- the bandwidth settings affect both channels on + * the dboard + * \param dir specifies which path to set the bandwidth filters for. Supports + * rx, tx, or dx for both + * \return actual rf bandwidth value + */ double magnesium_ad9371_iface::set_bandwidth( const double bandwidth, const size_t chan, const direction_t dir) { - auto const which = _get_which(dir, chan); - auto retval = request("set_bw_filter", which, bandwidth); - UHD_LOG_TRACE(_log_prefix, _rpc_prefix << "set_bw_filter returned " << retval); + const auto which = _get_which_duplex(dir, chan); + const auto retval = + request(MAGNESIUM_TUNE_TIMEOUT, "set_bandwidth", which, bandwidth); + UHD_LOG_TRACE(_log_prefix, _rpc_prefix << "set_bandwidth returned " << retval); return retval; } @@ -84,12 +109,12 @@ double magnesium_ad9371_iface::get_gain(const size_t chan, const direction_t dir return retval; } -double magnesium_ad9371_iface::get_bandwidth( - const size_t /*chan*/, const direction_t /*dir*/) +double magnesium_ad9371_iface::get_bandwidth(const size_t chan, const direction_t dir) { - // TODO: implement - UHD_LOG_WARNING(_log_prefix, "Ignoring attempt to get bandwidth"); - return 0.0; + const auto which = _get_which(dir, chan); + const auto retval = request("get_bandwidth", which); + UHD_LOG_TRACE(_log_prefix, _rpc_prefix << "get_bandwidth returned " << retval); + return retval; } std::string magnesium_ad9371_iface::set_lo_source( diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp index a2de485e3..8e2463721 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp @@ -122,6 +122,8 @@ magnesium_radio_control_impl::magnesium_radio_control_impl(make_args_ptr make_ar UHD_ASSERT_THROW(_n3xx_timekeeper); _rpcc = _n310_mb_control->get_rpc_client(); UHD_ASSERT_THROW(_rpcc); + _mpm_compat_num = _rpcc->request>("get_mpm_compat_num"); + UHD_ASSERT_THROW(_mpm_compat_num.size() == 2); _init_defaults(); _init_mpm(); @@ -356,28 +358,79 @@ double magnesium_radio_control_impl::set_rx_frequency( return radio_control_impl::get_rx_frequency(chan); } -double magnesium_radio_control_impl::set_rx_bandwidth( +double magnesium_radio_control_impl::set_tx_bandwidth( const double bandwidth, const size_t chan) { std::lock_guard l(_set_lock); - _ad9371->set_bandwidth(bandwidth, chan, RX_DIRECTION); - // FIXME: setting analog bandwidth on AD9371 take no effect. - // Remove this warning when ADI can confirm that it works. - RFNOC_LOG_WARNING("set_rx_bandwidth take no effect on AD9371. " - "Default analog bandwidth is 100MHz"); - return AD9371_RX_MAX_BANDWIDTH; + + if (_mpm_compat_num[0] < 4 || (_mpm_compat_num[0] == 4 && _mpm_compat_num[1] < 1)) { + RFNOC_LOG_WARNING("Setting tx bandwidth not supported. Please upgrade MPM to a " + "minimum version of 4.1."); + radio_control_impl::set_tx_bandwidth(bandwidth, 1 - chan); + return radio_control_impl::set_tx_bandwidth(bandwidth, chan); + } + + const auto curr_bw = get_tx_bandwidth(chan); + if (fp_compare_epsilon(curr_bw) == bandwidth) { + return curr_bw; + } + + // TX frontend components should be deactivated before running init_cals + const auto tx_atr_bits = + _cpld->get_tx_atr_bits(magnesium_cpld_ctrl::BOTH, magnesium_cpld_ctrl::IDLE); + _reset_tx_frontend(magnesium_cpld_ctrl::BOTH); + + RFNOC_LOG_INFO( + "Re-initializing dboard to apply bandwidth settings. This may take some time."); + const auto ret = _ad9371->set_bandwidth(bandwidth, chan, TX_DIRECTION); + RFNOC_LOG_INFO("Bandwidth settings applied: " + std::to_string(ret)); + + // Restore TX frontend components to previous state + _cpld->set_tx_atr_bits( + magnesium_cpld_ctrl::BOTH, magnesium_cpld_ctrl::IDLE, tx_atr_bits); + + // Save the updated bandwidth settings for both channels + radio_control_impl::set_tx_bandwidth(bandwidth, 1 - chan); + return radio_control_impl::set_tx_bandwidth(ret, chan); } -double magnesium_radio_control_impl::set_tx_bandwidth( +double magnesium_radio_control_impl::set_rx_bandwidth( const double bandwidth, const size_t chan) { std::lock_guard l(_set_lock); - _ad9371->set_bandwidth(bandwidth, chan, TX_DIRECTION); - // FIXME: setting analog bandwidth on AD9371 take no effect. - // Remove this warning when ADI can confirm that it works. - RFNOC_LOG_WARNING("set_tx_bandwidth take no effect on AD9371. " - "Default analog bandwidth is 100MHz"); - return AD9371_TX_MAX_BANDWIDTH; + + if (_mpm_compat_num[0] < 4 || (_mpm_compat_num[0] == 4 && _mpm_compat_num[1] < 1)) { + RFNOC_LOG_WARNING("Setting rx bandwidth not supported. Please upgrade MPM to a " + "minimum version of 4.1."); + radio_control_impl::set_rx_bandwidth(bandwidth, 1 - chan); + return radio_control_impl::set_rx_bandwidth(bandwidth, chan); + } + + const auto curr_bw = get_rx_bandwidth(chan); + if (fp_compare_epsilon(curr_bw) == bandwidth) { + return curr_bw; + } + + // TX frontend components should be deactivated before running init_cals + // + // We want to avoid any TX signals coming out of the frontend. This is + // necessary even during RX calibration. + const auto tx_atr_bits = + _cpld->get_tx_atr_bits(magnesium_cpld_ctrl::BOTH, magnesium_cpld_ctrl::IDLE); + _reset_tx_frontend(magnesium_cpld_ctrl::BOTH); + + RFNOC_LOG_INFO( + "Re-initializing dboard to apply bandwidth settings. This may take some time."); + const auto ret = _ad9371->set_bandwidth(bandwidth, chan, RX_DIRECTION); + RFNOC_LOG_INFO("Bandwidth settings applied: " + std::to_string(ret)); + + // Restore TX frontend components to previous state + _cpld->set_tx_atr_bits( + magnesium_cpld_ctrl::BOTH, magnesium_cpld_ctrl::IDLE, tx_atr_bits); + + // Save the updated bandwidth settings for both channels + radio_control_impl::set_rx_bandwidth(bandwidth, 1 - chan); + return radio_control_impl::set_rx_bandwidth(ret, chan); } double magnesium_radio_control_impl::set_tx_gain(const double gain, const size_t chan) diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp index edc417cfe..5ac1ca245 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp @@ -279,6 +279,9 @@ private: /************************************************************************** * Private attributes *************************************************************************/ + //! MPM Compatibility number {MAJOR, MINOR} + std::vector _mpm_compat_num; + //! Locks access to setter APIs std::recursive_mutex _set_lock; @@ -345,6 +348,7 @@ private: //! Low band enable std::map _is_low_band = { {RX_DIRECTION, false}, {TX_DIRECTION, false}}; + //! AD9371 gain double _ad9371_rx_gain = 0.0; double _ad9371_tx_gain = 0.0; diff --git a/mpm/include/mpm/ad937x/ad937x_ctrl.hpp b/mpm/include/mpm/ad937x/ad937x_ctrl.hpp index 787bc41b5..936655405 100644 --- a/mpm/include/mpm/ad937x/ad937x_ctrl.hpp +++ b/mpm/include/mpm/ad937x/ad937x_ctrl.hpp @@ -184,7 +184,12 @@ public: */ virtual std::string get_arm_version() = 0; - //! set the BW filter for the frontend which + /*! \brief set the BW filter for the frontend which + * + * \param which frontend string (rx, tx, dx) + * \param value target rf bandwidth value + * return actual rf bandwidth value + */ virtual double set_bw_filter(const std::string& which, const double value) = 0; /*! \brief set the gain for the frontend which diff --git a/mpm/lib/mykonos/ad937x_device.cpp b/mpm/lib/mykonos/ad937x_device.cpp index b476922f2..fba065983 100644 --- a/mpm/lib/mykonos/ad937x_device.cpp +++ b/mpm/lib/mykonos/ad937x_device.cpp @@ -10,6 +10,7 @@ #include "adi/mykonos_gpio.h" #include "config/ad937x_config_t.hpp" #include "config/ad937x_default_config.hpp" +#include #include #include #include @@ -39,6 +40,23 @@ static const uint32_t AD9371_PRODUCT_ID = 0x3; static const uint32_t AD9371_XBCZ_PRODUCT_ID = 0x1; static const size_t ARM_BINARY_SIZE = 98304; +/* Values derived from AD937x filter wizard tool + * https://github.com/analogdevicesinc/ad937x-filter-wizard/blob/ + * 3e407b059be92fe65c4a32d5368fe4cdc491b1a1/profilegen/generate_RxORxPFIR.m#L130 + * https://github.com/analogdevicesinc/ad937x-filter-wizard/blob/ + * 3e407b059be92fe65c4a32d5368fe4cdc491b1a1/profilegen/generate_TxPFIR.m#L42 + */ +static constexpr double AD9371_RX_MIN_BANDWIDTH = 8e6; // Hz +static constexpr double AD9371_RX_MAX_BANDWIDTH = 100e6; // Hz +static constexpr double AD9371_RX_BBF_MIN_CORNER = 20e6; // Hz +static constexpr double AD9371_RX_BBF_MAX_CORNER = 100e6; // Hz +static constexpr double AD9371_TX_MIN_BANDWIDTH = 20e6; // Hz +static constexpr double AD9371_TX_MAX_BANDWIDTH = 125e6; // Hz +static constexpr double AD9371_TX_BBF_MIN_CORNER = 20e6; // Hz +static constexpr double AD9371_TX_BBF_MAX_CORNER = 125e6; // Hz +static constexpr double AD9371_TX_DAC_FILT_MIN_CORNER = 92.0e6; // Hz +static constexpr double AD9371_TX_DAC_FILT_MAX_CORNER = 187.0e6; // Hz + static const uint32_t PLL_LOCK_TIMEOUT_MS = 200; // Amount of time to average samples for RX DC offset @@ -548,26 +566,31 @@ double ad937x_device::tune( double ad937x_device::set_bw_filter(const direction_t direction, const double value) { + auto bw = value; switch (direction) { case TX_DIRECTION: { - mykonos_config.device->tx->txProfile->rfBandwidth_Hz = value; - mykonos_config.device->tx->txProfile->txBbf3dBCorner_kHz = value / 1000; - mykonos_config.device->tx->txProfile->txDac3dBCorner_kHz = value / 1000; + bw = uhd::clip(value, AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH); + const auto bbf = + uhd::clip(bw, AD9371_TX_BBF_MIN_CORNER, AD9371_TX_BBF_MAX_CORNER); + mykonos_config.device->tx->txProfile->rfBandwidth_Hz = bw; + mykonos_config.device->tx->txProfile->txBbf3dBCorner_kHz = bbf / 1000; + const auto dacCorner = uhd::clip( + bw, AD9371_TX_DAC_FILT_MIN_CORNER, AD9371_TX_DAC_FILT_MAX_CORNER); + mykonos_config.device->tx->txProfile->txDac3dBCorner_kHz = dacCorner / 1000; break; } case RX_DIRECTION: { - mykonos_config.device->rx->rxProfile->rfBandwidth_Hz = value; - mykonos_config.device->rx->rxProfile->rxBbf3dBCorner_kHz = value / 1000; + bw = uhd::clip(value, AD9371_RX_MIN_BANDWIDTH, AD9371_RX_MAX_BANDWIDTH); + const auto bbf = + uhd::clip(bw, AD9371_RX_BBF_MIN_CORNER, AD9371_RX_BBF_MAX_CORNER); + mykonos_config.device->rx->rxProfile->rfBandwidth_Hz = bw; + mykonos_config.device->rx->rxProfile->rxBbf3dBCorner_kHz = bbf / 1000; break; } } - const auto state = _move_to_config_state(); - CALL_API(MYKONOS_writeArmProfile(mykonos_config.device)); - _restore_from_config_state(state); - return value; // TODO: what is coercer value? + return bw; } - double ad937x_device::set_gain( const direction_t direction, const chain_t chain, const double value) { diff --git a/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp index 4719f486e..9aa07631b 100644 --- a/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp +++ b/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp @@ -6,6 +6,7 @@ // #include +#include namespace py = pybind11; #define LIBMPM_PYTHON diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py index c4e2f4131..d5cc525dc 100644 --- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py +++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py @@ -480,6 +480,18 @@ class Magnesium(BfrfsEEPROM, DboardManagerBase): 'value': str(lock_status).lower(), } + ########################################################################## + # Filter API + ########################################################################## + def set_bandwidth(self, which, bw): + if which.lower()[0:2] in ('tx', 'dx'): + self.log.debug("ad9371 set_tx_bandwidth bw: {}".format(bw)) + self._init_args['tx_bw'] = bw + if which.lower()[0:2] in ('rx', 'dx'): + self.log.debug("ad9371 set_rx_bandwidth bw: {}".format(bw)) + self._init_args['rx_bw'] = bw + self._reinit(self.master_clock_rate) + return bw ########################################################################## # Debug diff --git a/mpm/python/usrp_mpm/dboard_manager/mg_init.py b/mpm/python/usrp_mpm/dboard_manager/mg_init.py index 00a2eab7d..02658abb9 100644 --- a/mpm/python/usrp_mpm/dboard_manager/mg_init.py +++ b/mpm/python/usrp_mpm/dboard_manager/mg_init.py @@ -464,6 +464,10 @@ class MagnesiumInitManager(object): jesdcore.send_sysref_pulse() time.sleep(0.001) # 17us... ish. jesdcore.send_sysref_pulse() + if args.get('tx_bw'): + self.mykonos.set_bw_filter('TX', args.get('tx_bw')) + if args.get('rx_bw'): + self.mykonos.set_bw_filter('RX', args.get('rx_bw')) async_exec(self.mykonos, "finish_initialization") # According to the AD9371 user guide, p.57, the RF cal must come before # the framer/deframer init. We tried otherwise, and failed. So don't diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py index 77b4d5a15..78b76ab19 100644 --- a/mpm/python/usrp_mpm/rpc_server.py +++ b/mpm/python/usrp_mpm/rpc_server.py @@ -32,7 +32,7 @@ from usrp_mpm.sys_utils import net TIMEOUT_INTERVAL = 5.0 # Seconds before claim expires (default value) TOKEN_LEN = 16 # Length of the token string # Compatibility number for MPM -MPM_COMPAT_NUM = (4, 0) +MPM_COMPAT_NUM = (4, 1) def no_claim(func): " Decorator for functions that require no token check " -- cgit v1.2.3