aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormattprost <matt.prost@ni.com>2022-04-06 14:00:06 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2022-04-07 13:28:02 -0700
commita8ad4917d331258e163e1786c0325a4b7e7d2e3e (patch)
treeb3cd5935981721f5ad98c11dad78122d013f7573
parenteacbf15e30313793841ca6e4abd9cad02fffa5b4 (diff)
downloaduhd-a8ad4917d331258e163e1786c0325a4b7e7d2e3e.tar.gz
uhd-a8ad4917d331258e163e1786c0325a4b7e7d2e3e.tar.bz2
uhd-a8ad4917d331258e163e1786c0325a4b7e7d2e3e.zip
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 <bpadalino@gmail.com> Signed-off-by: mattprost <matt.prost@ni.com>
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_ad9371_iface.cpp47
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp81
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp4
-rw-r--r--mpm/include/mpm/ad937x/ad937x_ctrl.hpp7
-rw-r--r--mpm/lib/mykonos/ad937x_device.cpp43
-rw-r--r--mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp1
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/magnesium.py12
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/mg_init.py4
-rw-r--r--mpm/python/usrp_mpm/rpc_server.py2
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<double>("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<double>(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<double>("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<std::vector<size_t>>("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<std::recursive_mutex> 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<double>(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<std::recursive_mutex> 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<double>(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<size_t> _mpm_compat_num;
+
//! Locks access to setter APIs
std::recursive_mutex _set_lock;
@@ -345,6 +348,7 @@ private:
//! Low band enable
std::map<direction_t, bool> _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 <uhd/utils/algorithm.hpp>
#include <boost/format.hpp>
#include <cmath>
#include <fstream>
@@ -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 <pybind11/pybind11.h>
+#include <pybind11/stl.h>
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 "