aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_constants.hpp10
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp32
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp26
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp93
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/rh_periphs.py74
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/rhodium.py73
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py2
7 files changed, 306 insertions, 4 deletions
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp b/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
index 1d76994d0..dd703a612 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
@@ -7,6 +7,7 @@
#ifndef INCLUDED_LIBUHD_RHODIUM_CONSTANTS_HPP
#define INCLUDED_LIBUHD_RHODIUM_CONSTANTS_HPP
+#include <array>
#include <vector>
#include <string>
#include <cstddef>
@@ -68,6 +69,15 @@ static constexpr char RHODIUM_LO_GAIN[] = "dsa";
//! LO output power
static constexpr char RHODIUM_LO_POWER[] = "lo";
+static constexpr int NUM_LO_OUTPUT_PORT_NAMES = 4;
+
+static constexpr std::array<const char*, NUM_LO_OUTPUT_PORT_NAMES> LO_OUTPUT_PORT_NAMES = {
+ "LO_OUT_0",
+ "LO_OUT_1",
+ "LO_OUT_2",
+ "LO_OUT_3"
+};
+
static constexpr size_t RHODIUM_NUM_CHANS = 1;
#endif /* INCLUDED_LIBUHD_RHODIUM_CONSTANTS_HPP */
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp
index 4f4dd925c..c070dabea 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp
@@ -114,6 +114,12 @@ public:
bool get_tx_lo_export_enabled(const std::string& name, const size_t chan);
bool get_rx_lo_export_enabled(const std::string& name, const size_t chan);
+ // LO Distribution Control
+ void set_tx_lo_output_enabled(const bool enabled, const std::string& port_name, const size_t chan);
+ void set_rx_lo_output_enabled(const bool enabled, const std::string& port_name, const size_t chan);
+ bool get_tx_lo_output_enabled(const std::string& port_name, const size_t chan);
+ bool get_rx_lo_output_enabled(const std::string& port_name, const size_t chan);
+
// LO Gain Control
//! Set the external gain for a TX LO
@@ -185,6 +191,25 @@ private:
const direction_t dir
);
+ //! Validate that port_name is valid, and that LO distribution functions
+ // can be called in this instance
+ void _validate_output_port(
+ const std::string& port_name,
+ const std::string& function_name
+ );
+
+ //! Configure LO Distribution board's termination switches
+ void _set_lo_output_enabled(
+ const bool enabled,
+ const std::string& port_name,
+ const direction_t dir
+ );
+
+ bool _get_lo_output_enabled(
+ const std::string& port_name,
+ const direction_t dir
+ );
+
//! Configure LO1's output power
// Out of range values will be coerced to [0-63]
double _set_lo1_power(
@@ -336,6 +361,13 @@ private:
double _rx_lo_freq = 0.0;
double _tx_lo_freq = 0.0;
+ //! LO Distribution board
+ bool _lo_dist_present = false;
+
+ //! LO Distribution board output status
+ bool _lo_dist_rx_out_enabled[4] = { false, false, false, false };
+ bool _lo_dist_tx_out_enabled[4] = { false, false, false, false };
+
}; /* class radio_ctrl_impl */
}} /* namespace uhd::rfnoc */
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
index 8c2e6f231..6f7e37c26 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
@@ -213,6 +213,10 @@ void rhodium_radio_ctrl_impl::_init_peripherals()
_tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
_tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
_tx_fe_core->populate_subtree(_tree->subtree(_root_path / "tx_fe_corrections" / 0));
+
+ UHD_LOG_TRACE(unique_id(), "Checking for existence of LO Distribution board");
+ _lo_dist_present = _rpcc->request_with_token<bool>(_rpc_prefix + "is_lo_dist_present");
+ UHD_LOG_DEBUG(unique_id(), str(boost::format("LO distribution board is%s present") % (_lo_dist_present ? "" : " NOT")));
}
void rhodium_radio_ctrl_impl::_init_frontend_subtree(
@@ -661,6 +665,28 @@ void rhodium_radio_ctrl_impl::_init_frontend_subtree(
this->set_tx_lo_export_enabled(enabled, RHODIUM_LO2, chan_idx);
})
;
+
+ //LO Distribution Output Ports
+ if (_lo_dist_present) {
+ for (const auto& port : LO_OUTPUT_PORT_NAMES) {
+ subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port / "export")
+ .add_coerced_subscriber([this, chan_idx, port](bool enabled) {
+ this->set_tx_lo_output_enabled(enabled, port, chan_idx);
+ })
+ .set_publisher([this, chan_idx, port]() {
+ return this->get_tx_lo_output_enabled(port, chan_idx);
+ })
+ ;
+ subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port / "export")
+ .add_coerced_subscriber([this, chan_idx, port](bool enabled) {
+ this->set_tx_lo_output_enabled(enabled, port, chan_idx);
+ })
+ .set_publisher([this, chan_idx, port]() {
+ return this->get_tx_lo_output_enabled(port, chan_idx);
+ })
+ ;
+ }
+ }
}
void rhodium_radio_ctrl_impl::_init_prop_tree()
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
index 49eb854d5..a2789bc9a 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
@@ -307,6 +307,10 @@ void rhodium_radio_ctrl_impl::_set_lo1_export_enabled(
) {
auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
_lo_ctrl->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_B, enabled);
+ if (_lo_dist_present) {
+ const auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
+ _rpcc->notify_with_token(_rpc_prefix + "enable_lo_export", direction, enabled);
+ }
}
void rhodium_radio_ctrl_impl::set_tx_lo_export_enabled(
@@ -372,6 +376,95 @@ bool rhodium_radio_ctrl_impl::get_rx_lo_export_enabled(
}
/******************************************************************************
+ * LO Distribution Control
+ *****************************************************************************/
+
+void rhodium_radio_ctrl_impl::_validate_output_port(const std::string& port_name, const std::string& function_name)
+{
+ if (!_lo_dist_present) {
+ throw uhd::runtime_error(str(boost::format(
+ "%s can only be called if the LO distribution board was detected") % function_name));
+ }
+
+ if (!uhd::has(LO_OUTPUT_PORT_NAMES, port_name)) {
+ throw uhd::value_error(str(boost::format(
+ "%s was called with an invalid LO output port: %s Valid ports are [LO_OUT_0, LO_OUT_1, LO_OUT_2, LO_OUT_3]")
+ % function_name % port_name));
+ }
+}
+
+void rhodium_radio_ctrl_impl::_set_lo_output_enabled(
+ const bool enabled,
+ const std::string& port_name,
+ const direction_t dir
+) {
+ auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
+ auto name_iter = std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
+ auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
+
+ _rpcc->notify_with_token(_rpc_prefix + "enable_lo_output", direction, index, enabled);
+ auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled : _lo_dist_tx_out_enabled;
+ out_enabled[index] = enabled;
+}
+
+void rhodium_radio_ctrl_impl::set_tx_lo_output_enabled(
+ const bool enabled,
+ const std::string& port_name,
+ const size_t chan
+) {
+ UHD_LOG_TRACE(unique_id(), "set_tx_lo_output_enabled(enabled=" << enabled << ", port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "set_tx_lo_output_enabled");
+
+ _set_lo_output_enabled(enabled, port_name, TX_DIRECTION);
+}
+
+void rhodium_radio_ctrl_impl::set_rx_lo_output_enabled(
+ const bool enabled,
+ const std::string& port_name,
+ const size_t chan
+) {
+ UHD_LOG_TRACE(unique_id(), "set_rx_lo_output_enabled(enabled=" << enabled << ", port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "set_rx_lo_output_enabled");
+
+ _set_lo_output_enabled(enabled, port_name, RX_DIRECTION);
+}
+
+bool rhodium_radio_ctrl_impl::_get_lo_output_enabled(
+ const std::string& port_name,
+ const direction_t dir
+) {
+ auto name_iter = std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
+ auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
+
+ auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled : _lo_dist_tx_out_enabled;
+ return out_enabled[index];
+}
+
+bool rhodium_radio_ctrl_impl::get_tx_lo_output_enabled(
+ const std::string& port_name,
+ const size_t chan
+) {
+ UHD_LOG_TRACE(unique_id(), "get_tx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "get_tx_lo_output_enabled");
+
+ return _get_lo_output_enabled(port_name, TX_DIRECTION);
+}
+
+bool rhodium_radio_ctrl_impl::get_rx_lo_output_enabled(
+ const std::string& port_name,
+ const size_t chan
+) {
+ UHD_LOG_TRACE(unique_id(), "get_rx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "get_rx_lo_output_enabled");
+
+ return _get_lo_output_enabled(port_name, RX_DIRECTION);
+}
+
+/******************************************************************************
* Gain Control
*****************************************************************************/
diff --git a/mpm/python/usrp_mpm/dboard_manager/rh_periphs.py b/mpm/python/usrp_mpm/dboard_manager/rh_periphs.py
index e9b171c5e..2d631e509 100644
--- a/mpm/python/usrp_mpm/dboard_manager/rh_periphs.py
+++ b/mpm/python/usrp_mpm/dboard_manager/rh_periphs.py
@@ -26,8 +26,7 @@ class TCA6408(object):
)
def __init__(self, i2c_dev):
- if i2c_dev is None:
- raise RuntimeError("Need to specify i2c device to use the TCA6408")
+ assert i2c_dev is not None
self._gpios = SysFSGPIO({'label': 'tca6408'}, 0x3F, 0x00, 0x00, i2c_dev)
def set(self, name, value=None):
@@ -50,6 +49,77 @@ class TCA6408(object):
assert name in self.pins
return self._gpios.get(self.pins.index(name))
+class FPGAtoLoDist(object):
+ """
+ Abstraction layer for the port/gpio expander on the LO Distribution board
+ """
+ EXPECTED_BOARD_REV = 0
+ POWER_ON_TIMEOUT = 20 #ms
+ POWER_ON_POLL_INTERVAL = 1 #ms
+
+ pins = (
+ 'BD_REV_0', #Board revision bit 0
+ 'BD_REV_1', #Board revision bit 1
+ 'BD_REV_2', #Board revision bit 2
+ 'P6_8V_PG', #6.8V Power good
+ '4', #No connect
+ '5', #No connect
+ '6', #No connect
+ '7', #No connect
+ 'RX_OUT0_CTRL', #RX Out0 Port Termination Switch
+ 'RX_OUT1_CTRL', #RX Out1 Port Termination Switch
+ 'RX_OUT2_CTRL', #RX Out2 Port Termination Switch
+ 'RX_OUT3_CTRL', #RX Out3 Port Termination Switch
+ 'RX_INSWITCH_CTRL', #RX 1:4 splitter input select
+ 'TX_OUT0_CTRL', #TX Out0 Port Termination Switch
+ 'TX_OUT1_CTRL', #TX Out1 Port Termination Switch
+ 'TX_OUT2_CTRL', #TX Out2 Port Termination Switch
+ 'TX_OUT3_CTRL', #TX Out3 Port Termination Switch
+ 'TX_INSWITCH_CTRL', #TX 1:4 splitter input select
+ 'P6_8V_EN', #6.8V supply enable
+ 'P6_5V_LDO_EN', #6.5V LDO enable
+ 'P3_3V_RF_EN' #3.3V LDO for RF enable
+ )
+
+ def __init__(self, i2c_dev):
+ assert i2c_dev is not None
+ self._gpios = SysFSGPIO({'label': 'tca6424', 'device/of_node/name': 'rhodium-lodist-gpio'}, 0x1FFF0F, 0x1FFF00, 0x00A500, i2c_dev)
+ board_rev = self._gpios.get(self.pins.index('BD_REV_0')) + \
+ self._gpios.get(self.pins.index('BD_REV_1')) << 1 + \
+ self._gpios.get(self.pins.index('BD_REV_2')) << 2
+ if board_rev != self.EXPECTED_BOARD_REV:
+ raise RuntimeError('LO distribution board revision did not match: Expected: {0} Actual: {1}'.format(self.EXPECTED_BOARD_REV, board_rev))
+ self._gpios.set(self.pins.index('P6_8V_EN'), 1)
+ if not poll_with_timeout(
+ lambda: bool(self._gpios.get(self.pins.index('P6_8V_PG'))),
+ self.POWER_ON_TIMEOUT,
+ self.POWER_ON_POLL_INTERVAL):
+ self._gpios.set(self.pins.index('P6_8V_EN'), 0)
+ raise RuntimeError('Power on failure for LO Distribution board')
+ self._gpios.set(self.pins.index('P6_5V_LDO_EN'), 1)
+ self._gpios.set(self.pins.index('P3_3V_RF_EN'), 1)
+
+ def set(self, name, value=None):
+ """
+ Assert a pin by name
+ """
+ assert name in self.pins
+ self._gpios.set(self.pins.index(name), value=value)
+
+ def reset(self, name):
+ """
+ Deassert a pin by name
+ """
+ assert name in self.pins
+ self.set(name, value=0)
+
+ def get(self, name):
+ """
+ Read back a pin by name
+ """
+ assert name in self.pins
+ return self._gpios.get(self.pins.index(name))
+
class FPGAtoDbGPIO(GPIOBank):
"""
diff --git a/mpm/python/usrp_mpm/dboard_manager/rhodium.py b/mpm/python/usrp_mpm/dboard_manager/rhodium.py
index 81ca221a7..81c67ffa3 100644
--- a/mpm/python/usrp_mpm/dboard_manager/rhodium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/rhodium.py
@@ -13,7 +13,7 @@ import threading
from six import iterkeys, iteritems
from usrp_mpm import lib # Pulls in everything from C++-land
from usrp_mpm.dboard_manager import DboardManagerBase
-from usrp_mpm.dboard_manager.rh_periphs import TCA6408, FPGAtoDbGPIO
+from usrp_mpm.dboard_manager.rh_periphs import TCA6408, FPGAtoDbGPIO, FPGAtoLoDist
from usrp_mpm.dboard_manager.rh_init import RhodiumInitManager
from usrp_mpm.dboard_manager.rh_periphs import RhCPLD
from usrp_mpm.dboard_manager.rh_periphs import DboardClockControl
@@ -24,6 +24,7 @@ from usrp_mpm.mpmlog import get_logger
from usrp_mpm.sys_utils.uio import UIO
from usrp_mpm.sys_utils.udev import get_eeprom_paths
from usrp_mpm.bfrfs import BufferFS
+from usrp_mpm.sys_utils.dtoverlay import apply_overlay_safe, rm_overlay_safe
###############################################################################
@@ -174,6 +175,25 @@ class Rhodium(DboardManagerBase):
default_time_source = 'internal'
default_current_jesd_rate = 4915.2e6
+ # Provide a mapping of direction and pin number to
+ # pin name and active state (0 = active-low) for
+ # LO out ports
+ lo_out_pin_map = {
+ 'RX' : [('RX_OUT0_CTRL', 0),
+ ('RX_OUT1_CTRL', 1),
+ ('RX_OUT2_CTRL', 0),
+ ('RX_OUT3_CTRL', 1)],
+ 'TX' : [('TX_OUT0_CTRL', 0),
+ ('TX_OUT1_CTRL', 1),
+ ('TX_OUT2_CTRL', 0),
+ ('TX_OUT3_CTRL', 1)]}
+
+ # Provide mapping of direction to pin name for LO
+ # in port
+ lo_in_pin_map = {
+ 'RX' : 'RX_INSWITCH_CTRL',
+ 'TX' : 'TX_INSWITCH_CTRL'}
+
def __init__(self, slot_idx, **kwargs):
super(Rhodium, self).__init__(slot_idx, **kwargs)
self.log = get_logger("Rhodium-{}".format(slot_idx))
@@ -191,6 +211,7 @@ class Rhodium(DboardManagerBase):
# Predeclare some attributes to make linter happy:
self.lmk = None
self._port_expander = None
+ self._lo_dist = None
self.cpld = None
# If _init_args is None, it means that init() hasn't yet been called.
self._init_args = None
@@ -235,6 +256,14 @@ class Rhodium(DboardManagerBase):
)
self._port_expander = TCA6408(_get_i2c_dev())
self._daughterboard_gpio = FPGAtoDbGPIO(self.slot_idx)
+ # TODO: applying the overlay without checking for the presence of the
+ # LO dist board will create a kernel error. Fix this when the I2C API
+ # is implemented by checking if the board is present before applying.
+ try:
+ apply_overlay_safe('n321')
+ self._lo_dist = FPGAtoLoDist(_get_i2c_dev())
+ except RuntimeError:
+ self._lo_dist = None
self.log.debug("Turning on Module and RF power supplies")
self._power_on()
self._spi_ifaces = _init_spi_devices()
@@ -411,6 +440,48 @@ class Rhodium(DboardManagerBase):
# This does not stop anyone from killing this process (and the thread)
# while the EEPROM write is happening, though.
+ def enable_lo_export(self, direction, enable):
+ """
+ For N321 devices. If enable is true, connect the RX 1:4 splitter to the
+ daughterboard LO export. If enable is false, connect the splitter to
+ LO input port 1 instead.
+
+ Asserts if there is no LO distribution board attached (e.g. device is
+ not an N321, or this is the daughterboard in slot B)
+ """
+
+ assert self._lo_dist is not None
+ assert direction in ('RX', 'TX')
+ pin = self.lo_in_pin_map[direction]
+ pin_val = 0 if enable else 1
+ self.log.debug("LO Distribution: 1:4 splitter connected to {0} {1}".format(
+ direction, {True: "DB export", False: "Input 0"}[enable]))
+ self.log.trace("Net name: {0}, Pin value: {1}".format(pin, pin_val))
+ self._lo_dist.set(pin, pin_val)
+
+ def enable_lo_output(self, direction, port_number, enable):
+ """
+ For N321 devices. If enable is true, connect the RX 1:4 splitter to the
+ daughterboard LO export. If enable is false, connect the splitter to
+ LO input port 1 instead.
+
+ Asserts if there is no LO distribution board attached (e.g. device is
+ not an N321, or this is the daughterboard in slot B)
+ """
+
+ assert self._lo_dist is not None
+ assert direction in ('RX', 'TX')
+ assert port_number in (0, 1, 2, 3)
+ pin_info = self.lo_out_pin_map[direction][port_number]
+ # enable XNOR active_high = desired pinout value
+ pin_val = 1 if not (enable ^ pin_info[1]) else 0
+ self.log.debug("LO Distribution: {0} Out{1} is {2}".format(
+ direction, port_number, {True: "active", False: "terminated"}[enable]))
+ self.log.trace("Net name: {0}, Pin value: {1}".format(pin_info[0], pin_val))
+ self._lo_dist.set(pin_info[0], pin_val)
+
+ def is_lo_dist_present(self):
+ return self._lo_dist is not None
##########################################################################
# Clocking control APIs
diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
index 775431050..5622285f6 100644
--- a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
@@ -99,7 +99,7 @@ class TCA6424(object):
self.pins = self.pins_list[1]
default_val = 0x860101 if rev == 2 else 0x860780
- self._gpios = SysFSGPIO({'label': 'tca6424'}, 0xFFF7FF, 0x86F7FF, default_val)
+ self._gpios = SysFSGPIO({'label': 'tca6424', 'device/of_node/name': 'gpio'}, 0xFFF7FF, 0x86F7FF, default_val)
def set(self, name, value=None):
"""