diff options
| author | Mark Meserve <mark.meserve@ni.com> | 2018-10-02 14:26:49 -0500 | 
|---|---|---|
| committer | Brent Stapleton <bstapleton@g.hmc.edu> | 2018-11-05 13:43:07 -0800 | 
| commit | 728d9abfe7fd9adf87ba4f87627829e09ddcc8cf (patch) | |
| tree | 757accf69ea77d5f04ce147c0188be20b742a9f0 | |
| parent | 44eb4b4b43a58324854ce50ef983331c98125eeb (diff) | |
| download | uhd-728d9abfe7fd9adf87ba4f87627829e09ddcc8cf.tar.gz uhd-728d9abfe7fd9adf87ba4f87627829e09ddcc8cf.tar.bz2 uhd-728d9abfe7fd9adf87ba4f87627829e09ddcc8cf.zip  | |
rh: add lo distribution support
- This is a combination of 5 commits.
- rh: add lo distribution board gpio expander
- rh: add lo distribution mpm functions
- rh: add code to conditionally initialize lo distribution
- rh: change empty i2c device from exception to assertion
- rh: add lo distribution board control
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):          """  | 
