diff options
Diffstat (limited to 'host/lib/usrp/dboard/e3xx')
15 files changed, 1672 insertions, 1630 deletions
| diff --git a/host/lib/usrp/dboard/e3xx/CMakeLists.txt b/host/lib/usrp/dboard/e3xx/CMakeLists.txt index 5d452fb53..6a14c0766 100644 --- a/host/lib/usrp/dboard/e3xx/CMakeLists.txt +++ b/host/lib/usrp/dboard/e3xx/CMakeLists.txt @@ -1,13 +1,14 @@  #  # Copyright 2018 Ettus Research, a National Instruments Company +# Copyright 2019 Ettus Research, a National Instruments Brand  #  # SPDX-License-Identifier: GPL-3.0-or-later  #  IF(ENABLE_E300 OR ENABLE_E320)          LIST(APPEND E3XX_SOURCES -        ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_impl.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_init.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_control_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_control_init.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_ad9361_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_bands.cpp      ) @@ -16,14 +17,14 @@ ENDIF(ENABLE_E300 OR ENABLE_E320)  IF(ENABLE_E300)          LIST(APPEND E300_SOURCES -        ${CMAKE_CURRENT_SOURCE_DIR}/e31x_radio_ctrl_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/e31x_radio_control_impl.cpp      )      LIBUHD_APPEND_SOURCES(${E300_SOURCES})  ENDIF(ENABLE_E300)  IF(ENABLE_E320)          LIST(APPEND E320_SOURCES -        ${CMAKE_CURRENT_SOURCE_DIR}/e320_radio_ctrl_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/e320_radio_control_impl.cpp      )      LIBUHD_APPEND_SOURCES(${E320_SOURCES})  ENDIF(ENABLE_E320) diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp new file mode 100644 index 000000000..b7524e04c --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp @@ -0,0 +1,212 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "e31x_radio_control_impl.hpp" +#include "e31x_regs.hpp" +#include <uhd/rfnoc/registry.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::rfnoc; + +e31x_radio_control_impl::e31x_radio_control_impl(make_args_ptr make_args) +    : e3xx_radio_control_impl(std::move(make_args)) +{ +    // Swap front ends for E310 +    _fe_swap = true; +    _init_mpm(); +} + +e31x_radio_control_impl::~e31x_radio_control_impl() +{ +    RFNOC_LOG_TRACE("e31x_radio_control_impl::dtor()"); +} + +/****************************************************************************** + * API Calls + *****************************************************************************/ +uint32_t e31x_radio_control_impl::get_tx_switches( +    const size_t chan, +    const double freq +) { +    RFNOC_LOG_TRACE( +        "Update all TX freq related switches. f=" << freq << " Hz, " +    ); + +    size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan; + +    auto tx_sw1 = TX_SW1_LB_2750;  // SW1 = 0 +    auto vctxrx_sw = VCTXRX_SW_OFF; +    auto tx_bias = (fe_chan == 0) ? TX1_BIAS_LB_ON: TX2_BIAS_LB_ON; + +    const auto band = e3xx_radio_control_impl::map_freq_to_tx_band(freq); +    switch(band) { +    case tx_band::LB_80: +        tx_sw1 = TX_SW1_LB_80; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_160: +        tx_sw1 = TX_SW1_LB_160; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_225: +        tx_sw1 = TX_SW1_LB_225; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_400: +        tx_sw1 = TX_SW1_LB_400; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_575: +        tx_sw1 = TX_SW1_LB_575; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_1000: +        tx_sw1 = TX_SW1_LB_1000; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_1700: +        tx_sw1 = TX_SW1_LB_1700; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::LB_2750: +        tx_sw1 = TX_SW1_LB_2750; +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; +        break; +    case tx_band::HB: +        tx_sw1 = TX_SW1_LB_80; +        vctxrx_sw = VCTXRX_SW_TX_HB; +        tx_bias = (fe_chan == 0) ? TX1_BIAS_HB_ON: TX2_BIAS_HB_ON; +        break; +    case tx_band::INVALID_BAND: +        RFNOC_LOG_ERROR( +            "Cannot map TX frequency to band: " << freq); +        UHD_THROW_INVALID_CODE_PATH(); +        break; +    } +    auto tx_regs = 0 | +             vctxrx_sw << VCTXRX_SW_SHIFT | +             tx_bias << TX_BIAS_SHIFT | +             tx_sw1 << TX_SW1_SHIFT; +    return tx_regs; +} + +uint32_t e31x_radio_control_impl::get_rx_switches( +    const size_t chan, +    const double freq, +    const std::string &ant +){ +    RFNOC_LOG_TRACE( +        "Update all RX freq related switches. f=" << freq << " Hz, " +    ); + +    size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan; + +    // Default to OFF +    auto rx_sw1 = RX_SW1_OFF; +    auto rx_swc = RX_SWC_OFF; +    auto rx_swb = RX_SWB_OFF; +    auto vctxrx_sw = VCTXRX_SW_OFF; +    auto vcrx_sw = VCRX_SW_LB; +    if (ant == "TX/RX") { +        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_RX: VCTXRX2_SW_RX; +    } + +    RFNOC_LOG_INFO("RX freq = " << freq); +    const auto band = e3xx_radio_control_impl::map_freq_to_rx_band(freq); +    RFNOC_LOG_INFO("RX band = " << int(band)); + +    switch(band) { +    case rx_band::LB_B2: +        rx_sw1 = RX_SW1_LB_B2; +        rx_swc = RX_SWC_LB_B2; +        rx_swb = RX_SWB_OFF; +        break; +    case rx_band::LB_B3: +        rx_sw1 = RX_SW1_LB_B3; +        rx_swc = RX_SWC_LB_B3; +        rx_swb = RX_SWB_OFF; +        break; +    case rx_band::LB_B4: +        rx_sw1 = RX_SW1_LB_B4; +        rx_swc = RX_SWC_LB_B4; +        rx_swb = RX_SWB_OFF; +        break; +    case rx_band::LB_B5: +        rx_sw1 = RX_SW1_LB_B5; +        rx_swc = RX_SWC_OFF; +        rx_swb = RX_SWB_LB_B5; +        break; +    case rx_band::LB_B6: +        rx_sw1 = RX_SW1_LB_B6; +        rx_swc = RX_SWC_OFF; +        rx_swb = RX_SWB_LB_B6; +        break; +    case rx_band::LB_B7: +        rx_sw1 = RX_SW1_LB_B7; +        rx_swc = RX_SWC_OFF; +        rx_swb = RX_SWB_LB_B7; +        break; +    case rx_band::HB: +        rx_sw1 = RX_SW1_OFF; +        rx_swc = RX_SWC_OFF; +        rx_swb = RX_SWB_OFF; +        vcrx_sw = VCRX_SW_HB; +        break; +    case rx_band::INVALID_BAND: +        RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq); +        UHD_THROW_INVALID_CODE_PATH(); +        break; +    } +    RFNOC_LOG_INFO("RX SW1 = " << rx_sw1); +    RFNOC_LOG_INFO("RX SWC = " << rx_swc); +    RFNOC_LOG_INFO("RX SWB = " << rx_swb); +    RFNOC_LOG_INFO("RX VCRX_SW = " << vcrx_sw); +    RFNOC_LOG_INFO("RX VCTXRX_SW = " << vctxrx_sw); + +    auto rx_regs = 0 | +             vcrx_sw << VCRX_SW_SHIFT | +             vctxrx_sw << VCTXRX_SW_SHIFT | +             rx_swc << RX_SWC_SHIFT | +             rx_swb << RX_SWB_SHIFT | +             rx_sw1 << RX_SW1_SHIFT; +    return rx_regs; +} + +uint32_t e31x_radio_control_impl::get_idle_switches() +{ +    uint32_t idle_regs = VCRX_SW_OFF << VCRX_SW_SHIFT | +                        VCTXRX_SW_OFF << VCTXRX_SW_SHIFT | +                        TX_BIAS_OFF << TX_BIAS_SHIFT | +                        RX_SWC_OFF << RX_SWC_SHIFT | +                        RX_SWB_OFF << RX_SWB_SHIFT | +                        RX_SW1_OFF << RX_SW1_SHIFT | +                        TX_SW1_LB_2750 << TX_SW1_SHIFT; +    return idle_regs; +} + +uint32_t e31x_radio_control_impl::get_idle_led() +{ +    return 0; +} + +uint32_t e31x_radio_control_impl::get_rx_led() +{ +    return 1 << LED_RX_RX_SHIFT; +} + +uint32_t e31x_radio_control_impl::get_tx_led() +{ +    return 1 << LED_TXRX_TX_SHIFT; +} + +uint32_t e31x_radio_control_impl::get_txrx_led() +{ +    return 1 << LED_TXRX_RX_SHIFT; +} + +UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT( +    e31x_radio_control, RADIO_BLOCK, E310, "Radio", true, "radio_clk", "bus_clk") diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.hpp index 581a90c8e..c51d74203 100644 --- a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.hpp @@ -1,5 +1,6 @@  //  // Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand  //  // SPDX-License-Identifier: GPL-3.0-or-later  // @@ -8,30 +9,37 @@  #define INCLUDED_LIBUHD_RFNOC_E31X_RADIO_CTRL_IMPL_HPP  #include "e3xx_constants.hpp" -#include "e3xx_radio_ctrl_impl.hpp" +#include "e3xx_radio_control_impl.hpp" -namespace uhd { -    namespace rfnoc { +namespace { +static constexpr char E31x_GPIO_BANK[] = "INT0"; +} -/*! \brief Provide access to an E31X radio. +namespace uhd { namespace rfnoc { + +/*! Provide access to an E31X radio. + * + * This class only contains hardware-specific things that are different between + * E31X and E320.   */ -class e31x_radio_ctrl_impl : public e3xx_radio_ctrl_impl +class e31x_radio_control_impl : public e3xx_radio_control_impl  {  public:      /************************************************************************ -     * Structors +     * Structors and deinit       ***********************************************************************/ -    e31x_radio_ctrl_impl( -        const make_args_t &make_args -    ); -    virtual ~e31x_radio_ctrl_impl(); +    e31x_radio_control_impl(make_args_ptr make_args); +    virtual ~e31x_radio_control_impl(); -protected: +    std::vector<std::string> get_gpio_banks() const +    { +        return {E31x_GPIO_BANK}; +    } +private:      /**************************************************************************       * ATR/ Switches Types       *************************************************************************/ -      enum tx_sw1_t {          TX_SW1_LB_80 = 7,          TX_SW1_LB_160 = 6, @@ -93,32 +101,13 @@ protected:      };      /************************************************************************ -     * API calls +     * E3XX API calls       ***********************************************************************/ -    virtual bool check_radio_config(); -      const std::string get_default_timing_mode()      {          return TIMING_MODE_1R1T;      }; -    /*! Run a loopback self test. -     * -     * This will write data to the AD936x and read it back again. -     * If this test fails, it generally means the interface is broken, -     * so we assume it passes and throw otherwise. Running this requires -     * a core that we can peek and poke the loopback values into. -     * -     * \param iface An interface to the associated radio control core -     * \param iface The radio control core's address to write the loopback value -     * \param iface The radio control core's readback address to read back the returned -     * value -     * -     * \throws a uhd::runtime_error if the loopback value didn't match. -     */ -    void loopback_self_test(std::function<void(uint32_t)> poker_functor, -        std::function<uint64_t()> peeker_functor); -      uint32_t get_rx_switches(          const size_t chan,          const double freq, @@ -136,7 +125,7 @@ protected:      uint32_t get_rx_led();      uint32_t get_txrx_led();      uint32_t get_idle_led(); -}; /* class radio_ctrl_impl */ +};  }} /* namespace uhd::rfnoc */ diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp deleted file mode 100644 index 60df247a2..000000000 --- a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp +++ /dev/null @@ -1,300 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e31x_radio_ctrl_impl.hpp" -#include "e31x_regs.hpp" - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::rfnoc; - -e31x_radio_ctrl_impl::e31x_radio_ctrl_impl( -    const make_args_t &make_args -): block_ctrl_base(make_args) -{ -    // Swap front ends for E310 -    _fe_swap = true; -} - -e31x_radio_ctrl_impl::~e31x_radio_ctrl_impl() -{ -    UHD_LOG_TRACE(unique_id(), "e31x_radio_ctrl_impl::dtor() "); -} - -/****************************************************************************** - * API Calls - *****************************************************************************/ -bool e31x_radio_ctrl_impl::check_radio_config() -{ -    // mapping of frontend to radio perif index -    static const size_t FE0 = 1; -    static const size_t FE1 = 0; -    const size_t num_rx = _is_streamer_active(RX_DIRECTION, FE0) + _is_streamer_active(RX_DIRECTION, FE1); -    const size_t num_tx = _is_streamer_active(TX_DIRECTION, FE0) + _is_streamer_active(TX_DIRECTION, FE1); - -    //setup the active chains in the codec -    if ((num_rx + num_tx) == 0) { -        // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock -        this->set_streaming_mode(true, false, true, false); -    } else { -        this->set_streaming_mode( -                _is_streamer_active(TX_DIRECTION, FE0), -                _is_streamer_active(TX_DIRECTION, FE1), -                _is_streamer_active(RX_DIRECTION, FE0), -                _is_streamer_active(RX_DIRECTION, FE1) -        ); -    } -    return true; -} - -/*  loopback_self_test checks the integrity of the FPGA->AD936x->FPGA sample interface. -    The AD936x is put in loopback mode that sends the TX data unchanged to the RX side. -    A test value is written to the codec_idle register in the TX side of the radio. -    The readback register is then used to capture the values on the TX and RX sides -    simultaneously for comparison. It is a reasonably effective test for AC timing -    since I/Q Ch0/Ch1 alternate over the same wires. Note, however, that it uses -    whatever timing is configured at the time the test is called rather than select -    worst case conditions to stress the interface. -    Note: This currently only tests 2R2T mode -*/ -void e31x_radio_ctrl_impl::loopback_self_test( -    std::function<void(uint32_t)> poker_functor, std::function<uint64_t()> peeker_functor) -{ -    // Save current rate before running this test -    const double current_rate = this->get_rate(); -    // Set 2R2T mode, stream on all channels -    this->set_streaming_mode(true, false, true, false); -    // Set maximum rate for 2R2T mode -    this->set_rate(30.72e6); -    // Put AD936x in loopback mode -    _ad9361->data_port_loopback(true); -    UHD_LOG_INFO(unique_id(), "Performing CODEC loopback test... "); -    size_t hash                     = size_t(time(NULL)); -    constexpr size_t loopback_count = 100; - -    // Allow some time for AD936x to enter loopback mode. -    // There is no clear statement in the documentation of how long it takes, -    // but UG-570 does say to "allow six ADC_CLK/64 clock cycles of flush time" -    // when leaving the TX or RX states.  That works out to ~75us at the -    // minimum clock rate of 5 MHz, which lines up with test results. -    // Sleeping 1ms is far more than enough. -    std::this_thread::sleep_for(std::chrono::milliseconds(1)); - -    for (size_t i = 0; i < loopback_count; i++) { -        // Create test word -        boost::hash_combine(hash, i); -        const uint32_t word32 = uint32_t(hash) & 0xfff0fff0; -        // const uint32_t word32 = 0xCA00C100; -        // Write test word to codec_idle idle register (on TX side) -        poker_functor(word32); - -        // Read back values - TX is lower 32-bits and RX is upper 32-bits -        const uint64_t rb_word64 = peeker_functor(); -        const uint32_t rb_tx     = uint32_t(rb_word64 >> 32); -        const uint32_t rb_rx     = uint32_t(rb_word64 & 0xffffffff); - -        // Compare TX and RX values to test word -        bool test_fail = word32 != rb_tx or word32 != rb_rx; -        if (test_fail) { -            UHD_LOG_WARNING(unique_id(), -                "CODEC loopback test failed! " -                    << boost::format("Expected: 0x%08X Received (TX/RX): 0x%08X/0x%08X") -                           % word32 % rb_tx % rb_rx); -            throw uhd::runtime_error("CODEC loopback test failed."); -        } -    } -    UHD_LOG_INFO(unique_id(), "CODEC loopback test passed"); - -    // Zero out the idle data. -    poker_functor(0); - -    // Take AD936x out of loopback mode -    _ad9361->data_port_loopback(false); -    this->set_streaming_mode(true, false, true, false); -    // Switch back to current rate -    this->set_rate(current_rate); -} - - -uint32_t e31x_radio_ctrl_impl::get_tx_switches( -    const size_t chan, -    const double freq -) { -    UHD_LOG_TRACE(unique_id(), -        "Update all TX freq related switches. f=" << freq << " Hz, " -    ); - -    size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan; - -    auto tx_sw1 = TX_SW1_LB_2750;  // SW1 = 0 -    auto vctxrx_sw = VCTXRX_SW_OFF; -    auto tx_bias = (fe_chan == 0) ? TX1_BIAS_LB_ON: TX2_BIAS_LB_ON; - -    const auto band = e3xx_radio_ctrl_impl::map_freq_to_tx_band(freq); -    switch(band) { -    case tx_band::LB_80: -        tx_sw1 = TX_SW1_LB_80; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_160: -        tx_sw1 = TX_SW1_LB_160; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_225: -        tx_sw1 = TX_SW1_LB_225; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_400: -        tx_sw1 = TX_SW1_LB_400; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_575: -        tx_sw1 = TX_SW1_LB_575; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_1000: -        tx_sw1 = TX_SW1_LB_1000; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_1700: -        tx_sw1 = TX_SW1_LB_1700; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::LB_2750: -        tx_sw1 = TX_SW1_LB_2750; -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB; -        break; -    case tx_band::HB: -        tx_sw1 = TX_SW1_LB_80; -        vctxrx_sw = VCTXRX_SW_TX_HB; -        tx_bias = (fe_chan == 0) ? TX1_BIAS_HB_ON: TX2_BIAS_HB_ON; -        break; -    case tx_band::INVALID_BAND: -        UHD_LOG_ERROR(unique_id(), -            "Cannot map TX frequency to band: " << freq); -        UHD_THROW_INVALID_CODE_PATH(); -        break; -    } -    auto tx_regs = 0 | -             vctxrx_sw << VCTXRX_SW_SHIFT | -             tx_bias << TX_BIAS_SHIFT | -             tx_sw1 << TX_SW1_SHIFT; -    return tx_regs; -} - -uint32_t e31x_radio_ctrl_impl::get_rx_switches( -    const size_t chan, -    const double freq, -    const std::string &ant -){ -    UHD_LOG_TRACE(unique_id(), -        "Update all RX freq related switches. f=" << freq << " Hz, " -    ); - -    size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan; - -    // Default to OFF -    auto rx_sw1 = RX_SW1_OFF; -    auto rx_swc = RX_SWC_OFF; -    auto rx_swb = RX_SWB_OFF; -    auto vctxrx_sw = VCTXRX_SW_OFF; -    auto vcrx_sw = VCRX_SW_LB; -    if (ant == "TX/RX") { -        vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_RX: VCTXRX2_SW_RX; -    } - -    const auto band = e3xx_radio_ctrl_impl::map_freq_to_rx_band(freq); - -    switch(band) { -    case rx_band::LB_B2: -        rx_sw1 = RX_SW1_LB_B2; -        rx_swc = RX_SWC_LB_B2; -        rx_swb = RX_SWB_OFF; -        break; -    case rx_band::LB_B3: -        rx_sw1 = RX_SW1_LB_B3; -        rx_swc = RX_SWC_LB_B3; -        rx_swb = RX_SWB_OFF; -        break; -    case rx_band::LB_B4: -        rx_sw1 = RX_SW1_LB_B4; -        rx_swc = RX_SWC_LB_B4; -        rx_swb = RX_SWB_OFF; -        break; -    case rx_band::LB_B5: -        rx_sw1 = RX_SW1_LB_B5; -        rx_swc = RX_SWC_OFF; -        rx_swb = RX_SWB_LB_B5; -        break; -    case rx_band::LB_B6: -        rx_sw1 = RX_SW1_LB_B6; -        rx_swc = RX_SWC_OFF; -        rx_swb = RX_SWB_LB_B6; -        break; -    case rx_band::LB_B7: -        rx_sw1 = RX_SW1_LB_B7; -        rx_swc = RX_SWC_OFF; -        rx_swb = RX_SWB_LB_B7; -        break; -    case rx_band::HB: -        rx_sw1 = RX_SW1_OFF; -        rx_swc = RX_SWC_OFF; -        rx_swb = RX_SWB_OFF; -        vcrx_sw = VCRX_SW_HB; -        break; -    case rx_band::INVALID_BAND: -        UHD_LOG_ERROR(unique_id(), -            "Cannot map RX frequency to band: " << freq); -        UHD_THROW_INVALID_CODE_PATH(); -        break; -    } - -    UHD_LOG_TRACE(unique_id(), -        "RX band = " << int(band) << "RX SW1 = " << rx_sw1 << "RX SWC = " << rx_swc -                     << "RX SWB = " << rx_swb << "RX VCRX_SW = " << vcrx_sw -                     << "RX VCTXRX_SW = " << vctxrx_sw); - -    auto rx_regs = 0 | -             vcrx_sw << VCRX_SW_SHIFT | -             vctxrx_sw << VCTXRX_SW_SHIFT | -             rx_swc << RX_SWC_SHIFT | -             rx_swb << RX_SWB_SHIFT | -             rx_sw1 << RX_SW1_SHIFT; -    return rx_regs; -} - -uint32_t e31x_radio_ctrl_impl::get_idle_switches() -{ -    uint32_t idle_regs = VCRX_SW_OFF << VCRX_SW_SHIFT | -                        VCTXRX_SW_OFF << VCTXRX_SW_SHIFT | -                        TX_BIAS_OFF << TX_BIAS_SHIFT | -                        RX_SWC_OFF << RX_SWC_SHIFT | -                        RX_SWB_OFF << RX_SWB_SHIFT | -                        RX_SW1_OFF << RX_SW1_SHIFT | -                        TX_SW1_LB_2750 << TX_SW1_SHIFT; -    return idle_regs; -} - -uint32_t e31x_radio_ctrl_impl::get_idle_led() -{ -    return 0; -} - -uint32_t e31x_radio_ctrl_impl::get_rx_led() -{ -    return 1 << LED_RX_RX_SHIFT; -} - -uint32_t e31x_radio_ctrl_impl::get_tx_led() -{ -    return 1 << LED_TXRX_TX_SHIFT; -} - -uint32_t e31x_radio_ctrl_impl::get_txrx_led() -{ -    return 1 << LED_TXRX_RX_SHIFT; -} -UHD_RFNOC_BLOCK_REGISTER(e31x_radio_ctrl, "E31XRadio"); diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp new file mode 100644 index 000000000..df325bb75 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp @@ -0,0 +1,183 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "e320_radio_control_impl.hpp" +#include "e320_regs.hpp" +#include <uhd/rfnoc/registry.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::rfnoc; + +e320_radio_control_impl::e320_radio_control_impl(make_args_ptr make_args) +    : e3xx_radio_control_impl(std::move(make_args)) +{ +    RFNOC_LOG_TRACE("e320_radio_control_impl::ctor()"); +    // Don't swap front ends for E320 +    _fe_swap = false; +    _init_mpm(); +} + +e320_radio_control_impl::~e320_radio_control_impl() +{ +    RFNOC_LOG_TRACE("e320_radio_control_impl::dtor() "); +} + +/****************************************************************************** + * E320 API Calls + *****************************************************************************/ +uint32_t e320_radio_control_impl::get_tx_switches(const size_t chan, const double freq) +{ +    RFNOC_LOG_TRACE("Update all TX freq related switches. f=" << freq << " Hz, "); +    auto tx_sw1 = TX_SW1_LB_160; +    auto tx_sw2 = TX_SW2_LB_160; +    auto trx_sw = (chan == 0) ? TRX1_SW_TX_LB : TRX2_SW_TX_LB; +    auto tx_amp = TX_AMP_LF_ON; + +    const auto band = e3xx_radio_control_impl::map_freq_to_tx_band(freq); +    switch (band) { +        case tx_band::LB_80: +            tx_sw1 = TX_SW1_LB_80; +            tx_sw2 = TX_SW2_LB_80; +            break; +        case tx_band::LB_160: +            tx_sw1 = TX_SW1_LB_160; +            tx_sw2 = TX_SW2_LB_160; +            break; +        case tx_band::LB_225: +            tx_sw1 = TX_SW1_LB_225; +            tx_sw2 = TX_SW2_LB_225; +            break; +        case tx_band::LB_400: +            tx_sw1 = TX_SW1_LB_400; +            tx_sw2 = TX_SW2_LB_400; +            break; +        case tx_band::LB_575: +            tx_sw1 = TX_SW1_LB_575; +            tx_sw2 = TX_SW2_LB_575; +            break; +        case tx_band::LB_1000: +            tx_sw1 = TX_SW1_LB_1000; +            tx_sw2 = TX_SW2_LB_1000; +            break; +        case tx_band::LB_1700: +            tx_sw1 = TX_SW1_LB_1700; +            tx_sw2 = TX_SW2_LB_1700; +            break; +        case tx_band::LB_2750: +            tx_sw1 = TX_SW1_LB_2750; +            tx_sw2 = TX_SW2_LB_2750; +            break; +        case tx_band::HB: +            trx_sw = (chan == 0) ? TRX1_SW_TX_HB : TRX2_SW_TX_HB; +            tx_amp = TX_AMP_HF_ON; +            break; +        case tx_band::INVALID_BAND: +            RFNOC_LOG_ERROR("Cannot map TX frequency to band: " << freq); +            UHD_THROW_INVALID_CODE_PATH(); +            break; +    } + +    auto tx_regs = tx_amp << TX_AMP_SHIFT | trx_sw << TRX_SW_SHIFT +                   | tx_sw2 << TX_SW2_SHIFT | tx_sw1 << TX_SW1_SHIFT; +    return tx_regs; +} + +uint32_t e320_radio_control_impl::get_rx_switches( +    const size_t chan, const double freq, const std::string& ant) +{ +    RFNOC_LOG_TRACE("Update all RX freq related switches. f=" << (freq / 1e6) << " MHz"); +    // Default to OFF +    auto rx_sw1 = RX_SW1_OFF; +    auto rx_sw2 = RX_SW2_OFF; +    auto rx_sw3 = RX_SW3_OFF; +    auto trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX; +    if (ant == "TX/RX") { +        rx_sw3 = RX_SW3_HBRX_LBTRX; +        trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX; +    } else if (ant == "RX2") { +        rx_sw3 = RX_SW3_HBTRX_LBRX; +        // Set TRX switch to TX when receiving on RX2 +        trx_sw = TRX1_SW_TX_HB; +    } + +    const auto band = e3xx_radio_control_impl::map_freq_to_rx_band(freq); +    switch (band) { +        case rx_band::LB_B2: +            rx_sw1 = RX_SW1_LB_B2; +            rx_sw2 = RX_SW2_LB_B2; +            break; +        case rx_band::LB_B3: +            rx_sw1 = RX_SW1_LB_B3; +            rx_sw2 = RX_SW2_LB_B3; +            break; +        case rx_band::LB_B4: +            rx_sw1 = RX_SW1_LB_B4; +            rx_sw2 = RX_SW2_LB_B4; +            break; +        case rx_band::LB_B5: +            rx_sw1 = RX_SW1_LB_B5; +            rx_sw2 = RX_SW2_LB_B5; +            break; +        case rx_band::LB_B6: +            rx_sw1 = RX_SW1_LB_B6; +            rx_sw2 = RX_SW2_LB_B6; +            break; +        case rx_band::LB_B7: +            rx_sw1 = RX_SW1_LB_B7; +            rx_sw2 = RX_SW2_LB_B7; +            break; +        case rx_band::HB: +            rx_sw1 = RX_SW1_OFF; +            rx_sw2 = RX_SW2_OFF; +            if (ant == "TX/RX") { +                rx_sw3 = RX_SW3_HBTRX_LBRX; +            } else if (ant == "RX2") { +                rx_sw3 = RX_SW3_HBRX_LBTRX; +            } +            break; +        case rx_band::INVALID_BAND: +            RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq); +            UHD_THROW_INVALID_CODE_PATH(); +            break; +    } + +    auto rx_regs = trx_sw << TRX_SW_SHIFT | rx_sw3 << RX_SW3_SHIFT +                   | rx_sw2 << RX_SW2_SHIFT | rx_sw1 << RX_SW1_SHIFT; +    return rx_regs; +} + +uint32_t e320_radio_control_impl::get_idle_switches() +{ +    uint32_t idle_regs = TX_AMP_OFF << TX_AMP_SHIFT | TRX1_SW_TX_HB << TRX_SW_SHIFT +                         | TX_SW2_LB_80 << TX_SW2_SHIFT | TX_SW1_LB_80 << TX_SW1_SHIFT +                         | RX_SW3_OFF << RX_SW3_SHIFT | RX_SW2_OFF << RX_SW2_SHIFT +                         | RX_SW1_OFF << RX_SW1_SHIFT; +    return idle_regs; +} + +uint32_t e320_radio_control_impl::get_idle_led() +{ +    return 0; +} + +uint32_t e320_radio_control_impl::get_rx_led() +{ +    return 1 << TRX_LED_GRN_SHIFT; +} + +uint32_t e320_radio_control_impl::get_tx_led() +{ +    return 1 << TX_LED_RED_SHIFT; +} + +uint32_t e320_radio_control_impl::get_txrx_led() +{ +    return 1 << RX_LED_GRN_SHIFT; +} + +UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT( +    e320_radio_control, RADIO_BLOCK, E320, "Radio", true, "radio_clk", "bus_clk") diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.hpp index 7f75cadc7..f781eb49d 100644 --- a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.hpp @@ -8,20 +8,32 @@  #    define INCLUDED_LIBUHD_RFNOC_E320_RADIO_CTRL_IMPL_HPP  #    include "e3xx_constants.hpp" -#    include "e3xx_radio_ctrl_impl.hpp" +#    include "e3xx_radio_control_impl.hpp" + +namespace { +static constexpr char E320_GPIO_BANK[] = "FP0"; +}  namespace uhd { namespace rfnoc {  /*! \brief Provide access to an E320 radio. + * + * This class only contains hardware-specific things that are different between + * E320 and E31X.   */ -class e320_radio_ctrl_impl : public e3xx_radio_ctrl_impl +class e320_radio_control_impl : public e3xx_radio_control_impl  {  public:      /************************************************************************       * Structors       ***********************************************************************/ -    e320_radio_ctrl_impl(const make_args_t& make_args); -    virtual ~e320_radio_ctrl_impl(); +    e320_radio_control_impl(make_args_ptr make_args); +    virtual ~e320_radio_control_impl(); + +    std::vector<std::string> get_gpio_banks() const +    { +        return {E320_GPIO_BANK}; +    }  protected:      /************************************************************************** @@ -89,32 +101,13 @@ protected:      enum tx_amp_t { TX_AMP_HF_ON = 2, TX_AMP_LF_ON = 1, TX_AMP_OFF = 3 };      /************************************************************************ -     * API calls +     * E3XX API calls       ***********************************************************************/ -    virtual bool check_radio_config(); -      const std::string get_default_timing_mode()      {          return TIMING_MODE_2R2T;      }; -    /*! Run a loopback self test. -     * -     * This will write data to the AD936x and read it back again. -     * If this test fails, it generally means the interface is broken, -     * so we assume it passes and throw otherwise. Running this requires -     * a core that we can peek and poke the loopback values into. -     * -     * \param iface An interface to the associated radio control core -     * \param iface The radio control core's address to write the loopback value -     * \param iface The radio control core's readback address to read back the returned -     * value -     * -     * \throws a uhd::runtime_error if the loopback value didn't match. -     */ -    void loopback_self_test(std::function<void(uint32_t)> poker_functor, -        std::function<uint64_t()> peeker_functor); -      uint32_t get_rx_switches(          const size_t chan, const double freq, const std::string& ant); @@ -126,7 +119,7 @@ protected:      uint32_t get_rx_led();      uint32_t get_txrx_led();      uint32_t get_idle_led(); -}; /* class radio_ctrl_impl */ +};  }} /* namespace uhd::rfnoc */ diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp deleted file mode 100644 index c48cabc9c..000000000 --- a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp +++ /dev/null @@ -1,272 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e320_radio_ctrl_impl.hpp" -#include "e320_regs.hpp" - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::rfnoc; - -e320_radio_ctrl_impl::e320_radio_ctrl_impl(const make_args_t& make_args) -    : block_ctrl_base(make_args) -{ -    UHD_LOG_TRACE(unique_id(), "Entering e320_radio_ctrl_impl ctor..."); -    // Don't swap front ends for E320 -    _fe_swap = false; -} - -e320_radio_ctrl_impl::~e320_radio_ctrl_impl() -{ -    UHD_LOG_TRACE(unique_id(), "e320_radio_ctrl_impl::dtor() "); -} - -/****************************************************************************** - * API Calls - *****************************************************************************/ -bool e320_radio_ctrl_impl::check_radio_config() -{ -    // mapping of frontend to radio perif index -    static const size_t FE0 = 0; -    static const size_t FE1 = 1; -    const size_t num_rx = -        _is_streamer_active(RX_DIRECTION, FE0) + _is_streamer_active(RX_DIRECTION, FE1); -    const size_t num_tx = -        _is_streamer_active(TX_DIRECTION, FE0) + _is_streamer_active(TX_DIRECTION, FE1); - -    // setup the active chains in the codec -    if ((num_rx + num_tx) == 0) { -        // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock -        this->set_streaming_mode(true, false, true, false); -    } else { -        this->set_streaming_mode(_is_streamer_active(TX_DIRECTION, FE0), -            _is_streamer_active(TX_DIRECTION, FE1), -            _is_streamer_active(RX_DIRECTION, FE0), -            _is_streamer_active(RX_DIRECTION, FE1)); -    } -    return true; -} - -/*  loopback_self_test checks the integrity of the FPGA->AD936x->FPGA sample interface. -    The AD936x is put in loopback mode that sends the TX data unchanged to the RX side. -    A test value is written to the codec_idle register in the TX side of the radio. -    The readback register is then used to capture the values on the TX and RX sides -    simultaneously for comparison. It is a reasonably effective test for AC timing -    since I/Q Ch0/Ch1 alternate over the same wires. Note, however, that it uses -    whatever timing is configured at the time the test is called rather than select -    worst case conditions to stress the interface. -    Note: This currently only tests 2R2T mode -*/ -void e320_radio_ctrl_impl::loopback_self_test( -    std::function<void(uint32_t)> poker_functor, std::function<uint64_t()> peeker_functor) -{ -    // Save current rate before running this test -    const double current_rate = this->get_rate(); -    // Set 2R2T mode, stream on all channels -    this->set_streaming_mode(true, true, true, true); -    // Set maximum rate for 2R2T mode -    this->set_rate(30.72e6); -    // Put AD936x in loopback mode -    _ad9361->data_port_loopback(true); -    UHD_LOG_INFO(unique_id(), "Performing CODEC loopback test... "); -    size_t hash                     = size_t(time(NULL)); -    constexpr size_t loopback_count = 100; - -    // Allow some time for AD936x to enter loopback mode. -    // There is no clear statement in the documentation of how long it takes, -    // but UG-570 does say to "allow six ADC_CLK/64 clock cycles of flush time" -    // when leaving the TX or RX states.  That works out to ~75us at the -    // minimum clock rate of 5 MHz, which lines up with test results. -    // Sleeping 1ms is far more than enough. -    std::this_thread::sleep_for(std::chrono::milliseconds(1)); - -    for (size_t i = 0; i < loopback_count; i++) { -        // Create test word -        boost::hash_combine(hash, i); -        const uint32_t word32 = uint32_t(hash) & 0xfff0fff0; -        // const uint32_t word32 = 0xCA00C100; -        // Write test word to codec_idle idle register (on TX side) -        poker_functor(word32); - -        // Read back values - TX is lower 32-bits and RX is upper 32-bits -        const uint64_t rb_word64 = peeker_functor(); -        const uint32_t rb_tx     = uint32_t(rb_word64 >> 32); -        const uint32_t rb_rx     = uint32_t(rb_word64 & 0xffffffff); - -        // Compare TX and RX values to test word -        bool test_fail = word32 != rb_tx or word32 != rb_rx; -        if (test_fail) { -            UHD_LOG_WARNING(unique_id(), -                "CODEC loopback test failed! " -                    << boost::format("Expected: 0x%08X Received (TX/RX): 0x%08X/0x%08X") -                           % word32 % rb_tx % rb_rx); -            throw uhd::runtime_error("CODEC loopback test failed."); -        } -    } -    UHD_LOG_INFO(unique_id(), "CODEC loopback test passed"); - -    // Zero out the idle data. -    poker_functor(0); - -    // Take AD936x out of loopback mode -    _ad9361->data_port_loopback(false); -    this->set_streaming_mode(true, false, true, false); -    // Switch back to current rate -    this->set_rate(current_rate); -} - -uint32_t e320_radio_ctrl_impl::get_tx_switches(const size_t chan, const double freq) -{ -    UHD_LOG_TRACE( -        unique_id(), "Update all TX freq related switches. f=" << freq << " Hz, "); -    auto tx_sw1 = TX_SW1_LB_160; -    auto tx_sw2 = TX_SW2_LB_160; -    auto trx_sw = (chan == 0) ? TRX1_SW_TX_LB : TRX2_SW_TX_LB; -    auto tx_amp = TX_AMP_LF_ON; - -    const auto band = e3xx_radio_ctrl_impl::map_freq_to_tx_band(freq); -    switch (band) { -        case tx_band::LB_80: -            tx_sw1 = TX_SW1_LB_80; -            tx_sw2 = TX_SW2_LB_80; -            break; -        case tx_band::LB_160: -            tx_sw1 = TX_SW1_LB_160; -            tx_sw2 = TX_SW2_LB_160; -            break; -        case tx_band::LB_225: -            tx_sw1 = TX_SW1_LB_225; -            tx_sw2 = TX_SW2_LB_225; -            break; -        case tx_band::LB_400: -            tx_sw1 = TX_SW1_LB_400; -            tx_sw2 = TX_SW2_LB_400; -            break; -        case tx_band::LB_575: -            tx_sw1 = TX_SW1_LB_575; -            tx_sw2 = TX_SW2_LB_575; -            break; -        case tx_band::LB_1000: -            tx_sw1 = TX_SW1_LB_1000; -            tx_sw2 = TX_SW2_LB_1000; -            break; -        case tx_band::LB_1700: -            tx_sw1 = TX_SW1_LB_1700; -            tx_sw2 = TX_SW2_LB_1700; -            break; -        case tx_band::LB_2750: -            tx_sw1 = TX_SW1_LB_2750; -            tx_sw2 = TX_SW2_LB_2750; -            break; -        case tx_band::HB: -            trx_sw = (chan == 0) ? TRX1_SW_TX_HB : TRX2_SW_TX_HB; -            tx_amp = TX_AMP_HF_ON; -            break; -        case tx_band::INVALID_BAND: -            UHD_LOG_ERROR(unique_id(), "Cannot map TX frequency to band: " << freq); -            UHD_THROW_INVALID_CODE_PATH(); -            break; -    } - -    auto tx_regs = tx_amp << TX_AMP_SHIFT | trx_sw << TRX_SW_SHIFT -                   | tx_sw2 << TX_SW2_SHIFT | tx_sw1 << TX_SW1_SHIFT; -    return tx_regs; -} - -uint32_t e320_radio_ctrl_impl::get_rx_switches( -    const size_t chan, const double freq, const std::string& ant) -{ -    UHD_LOG_TRACE( -        unique_id(), "Update all RX freq related switches. f=" << freq << " Hz, "); -    // Default to OFF -    auto rx_sw1 = RX_SW1_OFF; -    auto rx_sw2 = RX_SW2_OFF; -    auto rx_sw3 = RX_SW3_OFF; -    auto trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX; -    if (ant == "TX/RX") { -        rx_sw3 = RX_SW3_HBRX_LBTRX; -        trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX; -    } else if (ant == "RX2") { -        rx_sw3 = RX_SW3_HBTRX_LBRX; -        // Set TRX switch to TX when receiving on RX2 -        trx_sw = TRX1_SW_TX_HB; -    } - -    const auto band = e3xx_radio_ctrl_impl::map_freq_to_rx_band(freq); -    switch (band) { -        case rx_band::LB_B2: -            rx_sw1 = RX_SW1_LB_B2; -            rx_sw2 = RX_SW2_LB_B2; -            break; -        case rx_band::LB_B3: -            rx_sw1 = RX_SW1_LB_B3; -            rx_sw2 = RX_SW2_LB_B3; -            break; -        case rx_band::LB_B4: -            rx_sw1 = RX_SW1_LB_B4; -            rx_sw2 = RX_SW2_LB_B4; -            break; -        case rx_band::LB_B5: -            rx_sw1 = RX_SW1_LB_B5; -            rx_sw2 = RX_SW2_LB_B5; -            break; -        case rx_band::LB_B6: -            rx_sw1 = RX_SW1_LB_B6; -            rx_sw2 = RX_SW2_LB_B6; -            break; -        case rx_band::LB_B7: -            rx_sw1 = RX_SW1_LB_B7; -            rx_sw2 = RX_SW2_LB_B7; -            break; -        case rx_band::HB: -            rx_sw1 = RX_SW1_OFF; -            rx_sw2 = RX_SW2_OFF; -            if (ant == "TX/RX") { -                rx_sw3 = RX_SW3_HBTRX_LBRX; -            } else if (ant == "RX2") { -                rx_sw3 = RX_SW3_HBRX_LBTRX; -            } -            break; -        case rx_band::INVALID_BAND: -            UHD_LOG_ERROR(unique_id(), "Cannot map RX frequency to band: " << freq); -            UHD_THROW_INVALID_CODE_PATH(); -            break; -    } - -    auto rx_regs = trx_sw << TRX_SW_SHIFT | rx_sw3 << RX_SW3_SHIFT -                   | rx_sw2 << RX_SW2_SHIFT | rx_sw1 << RX_SW1_SHIFT; -    return rx_regs; -} - -uint32_t e320_radio_ctrl_impl::get_idle_switches() -{ -    uint32_t idle_regs = TX_AMP_OFF << TX_AMP_SHIFT | TRX1_SW_TX_HB << TRX_SW_SHIFT -                         | TX_SW2_LB_80 << TX_SW2_SHIFT | TX_SW1_LB_80 << TX_SW1_SHIFT -                         | RX_SW3_OFF << RX_SW3_SHIFT | RX_SW2_OFF << RX_SW2_SHIFT -                         | RX_SW1_OFF << RX_SW1_SHIFT; -    return idle_regs; -} - -uint32_t e320_radio_ctrl_impl::get_idle_led() -{ -    return 0; -} - -uint32_t e320_radio_ctrl_impl::get_rx_led() -{ -    return 1 << TRX_LED_GRN_SHIFT; -} - -uint32_t e320_radio_ctrl_impl::get_tx_led() -{ -    return 1 << TX_LED_RED_SHIFT; -} - -uint32_t e320_radio_ctrl_impl::get_txrx_led() -{ -    return 1 << RX_LED_GRN_SHIFT; -} -UHD_RFNOC_BLOCK_REGISTER(e320_radio_ctrl, "NeonRadio"); diff --git a/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp b/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp index 001cf5d1b..83e96b3ec 100644 --- a/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp @@ -5,7 +5,7 @@  //  #include "e3xx_constants.hpp" -#include "e3xx_radio_ctrl_impl.hpp" +#include "e3xx_radio_control_impl.hpp"  #include <uhd/utils/math.hpp>  /* @@ -131,9 +131,9 @@ constexpr double E3XX_TX_LB_2750_MIN_FREQ = 1842.6e6;  constexpr double E3XX_TX_HB_MIN_FREQ      = 2940.0e6;  } // namespace -e3xx_radio_ctrl_impl::rx_band e3xx_radio_ctrl_impl::map_freq_to_rx_band(const double freq) +e3xx_radio_control_impl::rx_band e3xx_radio_control_impl::map_freq_to_rx_band(const double freq)  { -    e3xx_radio_ctrl_impl::rx_band band; +    e3xx_radio_control_impl::rx_band band;      if (fp_compare_epsilon<double>(freq) < AD9361_RX_MIN_FREQ) {          band = rx_band::INVALID_BAND; @@ -158,9 +158,9 @@ e3xx_radio_ctrl_impl::rx_band e3xx_radio_ctrl_impl::map_freq_to_rx_band(const do      return band;  } -e3xx_radio_ctrl_impl::tx_band e3xx_radio_ctrl_impl::map_freq_to_tx_band(const double freq) +e3xx_radio_control_impl::tx_band e3xx_radio_control_impl::map_freq_to_tx_band(const double freq)  { -    e3xx_radio_ctrl_impl::tx_band band; +    e3xx_radio_control_impl::tx_band band;      if (fp_compare_epsilon<double>(freq) < AD9361_TX_MIN_FREQ) {          band = tx_band::INVALID_BAND; diff --git a/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp b/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp index 53f64d837..f883a7d72 100644 --- a/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp @@ -12,12 +12,11 @@  #include <vector>  static constexpr size_t FPGPIO_MASTER_RADIO     = 0; -static constexpr size_t TOTAL_RADIO_PORTS       = 2; -static constexpr double AD9361_RX_MIN_BANDWIDTH = 20.0e6; // HZ -static constexpr double AD9361_RX_MAX_BANDWIDTH = 40.0e6; // HZ +static constexpr double AD9361_RX_MIN_BANDWIDTH = 20.0e6; // Hz +static constexpr double AD9361_RX_MAX_BANDWIDTH = 40.0e6; // Hz -static constexpr double AD9361_TX_MIN_BANDWIDTH = 20.0e6; // HZ -static constexpr double AD9361_TX_MAX_BANDWIDTH = 40.0e6; // HZ +static constexpr double AD9361_TX_MIN_BANDWIDTH = 20.0e6; // Hz +static constexpr double AD9361_TX_MAX_BANDWIDTH = 40.0e6; // Hz  static constexpr double AD9361_TX_MIN_FREQ = 47.0e6; // Hz  static constexpr double AD9361_TX_MAX_FREQ = 6.0e9; // Hz @@ -44,7 +43,10 @@ static constexpr double E3XX_DEFAULT_BANDWIDTH  = 40e6; // Hz  static constexpr char E3XX_DEFAULT_RX_ANTENNA[] = "RX2";  static constexpr char E3XX_DEFAULT_TX_ANTENNA[] = "TX/RX"; -static const std::vector<std::string> E3XX_RX_ANTENNAS = {"RX2", "TX/RX"}; +static const std::vector<std::string> E3XX_RX_ANTENNAS = { +    E3XX_DEFAULT_RX_ANTENNA, E3XX_DEFAULT_TX_ANTENNA}; + +static constexpr char E3XX_GPIO_BANK[] = "INT0";  static constexpr size_t E3XX_NUM_CHANS = 2; diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp new file mode 100644 index 000000000..29381a53c --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp @@ -0,0 +1,621 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "e3xx_radio_control_impl.hpp" +#include "e3xx_constants.hpp" +#include <uhd/transport/chdr.hpp> +#include <uhd/types/direction.hpp> +#include <uhd/types/eeprom.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/math.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/format.hpp> +#include <cmath> +#include <cstdlib> +#include <sstream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::rfnoc; +using namespace uhd::math::fp_compare; + +/****************************************************************************** + * Structors + *****************************************************************************/ +e3xx_radio_control_impl::e3xx_radio_control_impl(make_args_ptr make_args) +    : radio_control_impl(std::move(make_args)) +{ +    RFNOC_LOG_TRACE("Entering e3xx_radio_control_impl ctor..."); +    UHD_ASSERT_THROW(get_block_id().get_block_count() == 0); +    UHD_ASSERT_THROW( +        std::max(get_num_output_ports(), get_num_input_ports()) == E3XX_NUM_CHANS); +    UHD_ASSERT_THROW(get_mb_controller()); +    _e3xx_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller()); +    UHD_ASSERT_THROW(_e3xx_mb_control); +    _e3xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>( +        _e3xx_mb_control->get_timekeeper(0)); +    UHD_ASSERT_THROW(_e3xx_timekeeper); +    _rpcc = _e3xx_mb_control->get_rpc_client(); +    UHD_ASSERT_THROW(_rpcc); +    RFNOC_LOG_TRACE("Instantiating AD9361 control object..."); +    _ad9361 = make_rpc(_rpcc); + +    _init_defaults(); +    _init_peripherals(); +    _init_prop_tree(); + +    // Properties +    for (auto& samp_rate_prop : _samp_rate_in) { +        samp_rate_prop.set(_master_clock_rate); +    } +    for (auto& samp_rate_prop : _samp_rate_out) { +        samp_rate_prop.set(_master_clock_rate); +    } +} + +e3xx_radio_control_impl::~e3xx_radio_control_impl() +{ +    RFNOC_LOG_TRACE("e3xx_radio_control_impl::dtor() "); +} + +void e3xx_radio_control_impl::deinit() +{ +    _db_gpio.clear(); +    _leds_gpio.clear(); +    _fp_gpio.reset(); +    _wb_ifaces.clear(); +} + + +/****************************************************************************** + * API Calls + *****************************************************************************/ +bool e3xx_radio_control_impl::check_topology(const std::vector<size_t>& connected_inputs, +        const std::vector<size_t>& connected_outputs) +{ +    if (!node_t::check_topology(connected_inputs, connected_outputs)) { +        return false; +    } +    // Now we know that the connected ports are either 0 or 1 + +    // Check if we're running a 2x1 or 1x2 configuration -- the device does not +    // support this! +    if ((connected_outputs.size() == 1 && connected_inputs.size() == 2) +        || (connected_outputs.size() == 2 && connected_inputs.size() == 1)) { +        const std::string err_msg("Invalid channel configuration: This device does not " +                                  "support 1 TX x 2 RX or 2 TX x 1 RX configurations!"); +        RFNOC_LOG_ERROR(err_msg); +        throw uhd::runtime_error(err_msg); +    } +    // mapping of frontend to radio perif index +    const size_t FE0 = _fe_swap ? 1 : 0; +    const size_t FE1 = _fe_swap ? 0 : 1; + +    const bool tx_fe0_active = std::any_of(connected_inputs.begin(), +        connected_inputs.end(), +        [FE0](const size_t port) { return port == FE0; }); +    const bool tx_fe1_active = std::any_of(connected_inputs.begin(), +        connected_inputs.end(), +        [FE1](const size_t port) { return port == FE1; }); +    const bool rx_fe0_active = std::any_of(connected_outputs.begin(), +        connected_outputs.end(), +        [FE0](const size_t port) { return port == FE0; }); +    const bool rx_fe1_active = std::any_of(connected_outputs.begin(), +        connected_outputs.end(), +        [FE1](const size_t port) { return port == FE1; }); +    RFNOC_LOG_TRACE("TX FE0 Active: " << tx_fe0_active); +    RFNOC_LOG_TRACE("TX FE1 Active: " << tx_fe1_active); +    RFNOC_LOG_TRACE("RX FE0 Active: " << rx_fe0_active); +    RFNOC_LOG_TRACE("RX FE1 Active: " << rx_fe1_active); + +    //setup the active chains in the codec +    if (connected_inputs.size() + connected_outputs.size() == 0) { +        // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock +        this->set_streaming_mode(true, false, true, false); +    } else { +        this->set_streaming_mode( +            tx_fe0_active, tx_fe1_active, rx_fe0_active, rx_fe1_active); +    } +    return true; +} + + +void e3xx_radio_control_impl::set_streaming_mode( +    const bool tx1, const bool tx2, const bool rx1, const bool rx2) +{ +    RFNOC_LOG_TRACE("Setting streaming mode...") +    const size_t num_rx = rx1 + rx2; +    const size_t num_tx = tx1 + tx2; + +    // setup the active chains in the codec +    if ((num_rx + num_tx) == 0) { +        // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock +        _ad9361->set_active_chains(true, false, true, false); +    } else { +        // setup the active chains in the codec +        _ad9361->set_active_chains(tx1, tx2, rx1, rx2); +    } + +    // setup 1R1T/2R2T mode in catalina and fpga +    // The Catalina interface in the fpga needs to know which TX channel to use for +    // the data on the LVDS lines. +    if ((num_rx == 2) or (num_tx == 2)) { +        // AD9361 is in 1R1T mode +        _ad9361->set_timing_mode(this->get_default_timing_mode()); +        this->set_channel_mode(MIMO); +    } else { +        // AD9361 is in 1R1T mode +        _ad9361->set_timing_mode(TIMING_MODE_1R1T); + +        // Set to SIS0_TX1 if we're using the second TX antenna, otherwise +        // default to SISO_TX0 +        this->set_channel_mode(tx2 ? SISO_TX1 : SISO_TX0); +    } +} + +void e3xx_radio_control_impl::set_channel_mode(const std::string& channel_mode) +{ +    // MIMO for 2R2T mode for 2 channels +    // SISO_TX1 for 1R1T mode for 1 channel - TX1 +    // SISO_TX0 for 1R1T mode for 1 channel - TX0 +    _rpcc->request_with_token<void>("set_channel_mode", channel_mode); +} + +double e3xx_radio_control_impl::set_rate(const double rate) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    RFNOC_LOG_DEBUG("Asking for clock rate " << rate / 1e6 << " MHz\n"); +    // On E3XX, tick rate and samp rate are always the same +    double actual_tick_rate = _ad9361->set_clock_rate(rate); +    RFNOC_LOG_DEBUG("Actual clock rate " << actual_tick_rate / 1e6 << " MHz\n"); +    set_tick_rate(actual_tick_rate); +    radio_control_impl::set_rate(actual_tick_rate); +    _e3xx_timekeeper->update_tick_rate(rate); +    return rate; +} + +uhd::meta_range_t e3xx_radio_control_impl::get_rate_range() const +{ +    return _ad9361->get_clock_rate_range(); +} + +/****************************************************************************** + * RF API calls + *****************************************************************************/ +void e3xx_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan) +{ +    if (ant != get_tx_antenna(chan)) { +        throw uhd::value_error( +            str(boost::format("[%s] Requesting invalid TX antenna value: %s") +                % get_unique_id() % ant)); +    } +    radio_control_impl::set_tx_antenna(ant, chan); +    // We can't actually set the TX antenna, so let's stop here. +} + +void e3xx_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan) +{ +    UHD_ASSERT_THROW(chan <= E3XX_NUM_CHANS); +    if (std::find(E3XX_RX_ANTENNAS.begin(), E3XX_RX_ANTENNAS.end(), ant) +        == E3XX_RX_ANTENNAS.end()) { +        throw uhd::value_error( +            str(boost::format("[%s] Requesting invalid RX antenna value: %s") +                % get_unique_id() % ant)); +    } +    RFNOC_LOG_TRACE("Setting RX antenna to " << ant << " for chan " << chan); + +    radio_control_impl::set_rx_antenna(ant, chan); +    _set_atr_bits(chan); +} + +double e3xx_radio_control_impl::set_tx_frequency(const double freq, const size_t chan) +{ +    RFNOC_LOG_TRACE("set_tx_frequency(f=" << freq << ", chan=" << chan << ")"); +    std::lock_guard<std::mutex> l(_set_lock); + +    double clipped_freq = uhd::clip(freq, AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ); + +    double coerced_freq = +        _ad9361->tune(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clipped_freq); +    radio_control_impl::set_tx_frequency(coerced_freq, chan); +    // Front-end switching +    _set_atr_bits(chan); + +    return coerced_freq; +} + +double e3xx_radio_control_impl::set_rx_frequency(const double freq, const size_t chan) +{ +    RFNOC_LOG_TRACE("set_rx_frequency(f=" << freq << ", chan=" << chan << ")"); +    std::lock_guard<std::mutex> l(_set_lock); + +    double clipped_freq = uhd::clip(freq, AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ); + +    double coerced_freq = +        _ad9361->tune(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clipped_freq); +    radio_control_impl::set_rx_frequency(coerced_freq, chan); +    // Front-end switching +    _set_atr_bits(chan); + +    return coerced_freq; +} + +void e3xx_radio_control_impl::set_rx_agc(const bool enb, const size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    RFNOC_LOG_TRACE("set_rx_agc(enb=" << enb << ", chan=" << chan << ")"); +    const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan); +    _ad9361->set_agc(rx_fe, enb); +} + +double e3xx_radio_control_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    double clipped_bw = +        _ad9361->set_bw_filter(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), bandwidth); +    return radio_control_impl::set_rx_bandwidth(clipped_bw, chan); +} + +double e3xx_radio_control_impl::set_tx_bandwidth(const double bandwidth, const size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    double clipped_bw = +        _ad9361->set_bw_filter(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), bandwidth); +    return radio_control_impl::set_tx_bandwidth(clipped_bw, chan); +} + +double e3xx_radio_control_impl::set_tx_gain(const double gain, const size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    RFNOC_LOG_TRACE("set_tx_gain(gain=" << gain << ", chan=" << chan << ")"); +    double clip_gain = uhd::clip(gain, AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN); +    _ad9361->set_gain(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clip_gain); +    radio_control_impl::set_tx_gain(clip_gain, chan); +    return clip_gain; +} + +double e3xx_radio_control_impl::set_rx_gain(const double gain, const size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    UHD_ASSERT_THROW(chan < get_num_output_ports()); +    RFNOC_LOG_TRACE("set_rx_gain(gain=" << gain << ", chan=" << chan << ")"); +    double clip_gain = uhd::clip(gain, AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN); +    _ad9361->set_gain(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clip_gain); +    radio_control_impl::set_rx_gain(clip_gain, chan); +    return clip_gain; +} + +std::vector<std::string> e3xx_radio_control_impl::get_tx_antennas(const size_t) const +{ +    return {E3XX_DEFAULT_TX_ANTENNA}; +} + +std::vector<std::string> e3xx_radio_control_impl::get_rx_antennas(const size_t) const +{ +    return E3XX_RX_ANTENNAS; +} + +freq_range_t e3xx_radio_control_impl::get_tx_frequency_range(const size_t) const +{ +    return freq_range_t(AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ, 1.0); +} + +freq_range_t e3xx_radio_control_impl::get_rx_frequency_range(const size_t) const +{ +    return freq_range_t(AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ, 1.0); +} + +uhd::gain_range_t e3xx_radio_control_impl::get_tx_gain_range(const size_t) const +{ +    return meta_range_t(AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN, AD9361_TX_GAIN_STEP); +} + +uhd::gain_range_t e3xx_radio_control_impl::get_rx_gain_range(const size_t) const +{ +    return meta_range_t(AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN, AD9361_RX_GAIN_STEP); +} + +meta_range_t e3xx_radio_control_impl::get_tx_bandwidth_range(size_t) const +{ +    return meta_range_t(AD9361_TX_MIN_BANDWIDTH, AD9361_TX_MAX_BANDWIDTH); +} + +meta_range_t e3xx_radio_control_impl::get_rx_bandwidth_range(size_t) const +{ +    return meta_range_t(AD9361_RX_MIN_BANDWIDTH, AD9361_RX_MAX_BANDWIDTH); +} + +/************************************************************************** + * Calibration-Related API Calls + *************************************************************************/ +void e3xx_radio_control_impl::set_rx_dc_offset(const bool enb, size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    RFNOC_LOG_TRACE("set_rx_dc_offset(enb=" << enb << ", chan=" << chan << ")"); +    const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan); +    _ad9361->set_dc_offset_auto(rx_fe, enb); +} + +void e3xx_radio_control_impl::set_rx_iq_balance(const bool enb, size_t chan) +{ +    std::lock_guard<std::mutex> l(_set_lock); +    RFNOC_LOG_TRACE("set_rx_iq_balance(enb=" << enb << ", chan=" << chan << ")"); +    const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan); +    _ad9361->set_iq_balance_auto(rx_fe, enb); +} + +/************************************************************************** + * GPIO Controls + *************************************************************************/ +void e3xx_radio_control_impl::set_gpio_attr( +    const std::string& bank, const std::string& attr, const uint32_t value) +{ +    if (bank != get_gpio_banks().front()) { +        RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank); +        throw uhd::key_error("Invalid GPIO bank!"); +    } +    if (!gpio_atr::gpio_attr_rev_map.count(attr)) { +        RFNOC_LOG_ERROR("Invalid GPIO attr: " << attr); +        throw uhd::key_error("Invalid GPIO attr!"); +    } + +    const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr); + +    if (gpio_attr == gpio_atr::GPIO_READBACK) { +        RFNOC_LOG_WARNING("Cannot set READBACK attr."); +        return; +    } + +    _fp_gpio->set_gpio_attr(gpio_attr, value); +} + +uint32_t e3xx_radio_control_impl::get_gpio_attr( +    const std::string& bank, const std::string& attr) +{ +    if (bank != get_gpio_banks().front()) { +        RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank); +        throw uhd::key_error("Invalid GPIO bank!"); +    } + +    const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr); +    return _fp_gpio->get_attr_reg(gpio_attr); +} + +/****************************************************************************** + * Sensor API + *****************************************************************************/ +std::vector<std::string> e3xx_radio_control_impl::get_rx_sensor_names(const size_t) const +{ +    return _rx_sensor_names; +} + +uhd::sensor_value_t e3xx_radio_control_impl::get_rx_sensor( +    const std::string& sensor_name, const size_t chan) +{ +    return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>( +        _rpc_prefix + "get_sensor", "RX", sensor_name, chan)); +} + +std::vector<std::string> e3xx_radio_control_impl::get_tx_sensor_names(const size_t) const +{ +    return _tx_sensor_names; +} + +uhd::sensor_value_t e3xx_radio_control_impl::get_tx_sensor( +    const std::string& sensor_name, const size_t chan) +{ +    return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>( +        _rpc_prefix + "get_sensor", "TX", sensor_name, chan)); +} + +/*  loopback_self_test checks the integrity of the FPGA->AD936x->FPGA sample interface. +    The AD936x is put in loopback mode that sends the TX data unchanged to the RX side. +    A test value is written to the codec_idle register in the TX side of the radio. +    The readback register is then used to capture the values on the TX and RX sides +    simultaneously for comparison. It is a reasonably effective test for AC timing +    since I/Q Ch0/Ch1 alternate over the same wires. Note, however, that it uses +    whatever timing is configured at the time the test is called rather than select +    worst case conditions to stress the interface. +    Note: This currently only tests 2R2T mode +*/ +void e3xx_radio_control_impl::loopback_self_test(const size_t chan) +{ +    // Save current rate before running this test +    const double current_rate = this->get_rate(); +    // Set 2R2T mode, stream on all channels +    this->set_streaming_mode(true, true, true, true); +    // This was in there in the E320 code, but the comments didn't make sense: +    //this->set_streaming_mode(true, true, true, true); +    // Set maximum rate for 2R2T mode +    /* FIXME +     * We're directly setting the master clock rate here because we want to +     * avoid property propagation, etc, and we know that we're going to set it +     * back once we're done +     * this->set_rate(30.72e6); +     */ +    _ad9361->set_clock_rate(30.72e6); +    // Put AD936x in loopback mode +    _ad9361->data_port_loopback(true); +    RFNOC_LOG_INFO("Performing CODEC loopback test... "); +    size_t hash                     = size_t(time(NULL)); +    constexpr size_t loopback_count = 100; + +    // Allow some time for AD936x to enter loopback mode. +    // There is no clear statement in the documentation of how long it takes, +    // but UG-570 does say to "allow six ADC_CLK/64 clock cycles of flush time" +    // when leaving the TX or RX states.  That works out to ~75us at the +    // minimum clock rate of 5 MHz, which lines up with test results. +    // Sleeping 1ms is far more than enough. +    std::this_thread::sleep_for(std::chrono::milliseconds(1)); + +    for (size_t i = 0; i < loopback_count; i++) { +        // Create test word +        boost::hash_combine(hash, i); +        const uint32_t word32 = uint32_t(hash) & 0xfff0fff0; +        // Write test word to codec_idle idle register (on TX side) +        regs().poke32( +            regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_TX_IDLE_VALUE, word32); + +        // Read back values - TX is lower 32-bits and RX is upper 32-bits +        const uint32_t rb_tx = +            regs().peek32(regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_TX_IDLE_VALUE); +        const uint32_t rb_rx = +            regs().peek32(regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_RX_DATA); + +        // Compare TX and RX values to test word +        bool test_fail = word32 != rb_tx or word32 != rb_rx; +        if (test_fail) { +            RFNOC_LOG_WARNING( +                "CODEC loopback test failed! " +                    << boost::format("Expected: 0x%08X Received (TX/RX): 0x%08X/0x%08X") +                           % word32 % rb_tx % rb_rx); +            throw uhd::runtime_error("CODEC loopback test failed."); +        } +    } +    RFNOC_LOG_INFO("CODEC loopback test passed"); + +    // Zero out the idle data. +    regs().poke32(regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_TX_IDLE_VALUE, 0); + +    // Take AD936x out of loopback mode +    _ad9361->data_port_loopback(false); +    this->set_streaming_mode(true, false, true, false); +    // Switch back to current rate +    // FIXME along with the other comment above +    // this->set_rate(current_rate); +    _ad9361->set_clock_rate(current_rate); +} + +void e3xx_radio_control_impl::_identify_with_leds(const int identify_duration) +{ +    RFNOC_LOG_INFO( +        "Running LED identification process for " << identify_duration << " seconds."); +    auto end_time = +        std::chrono::steady_clock::now() + std::chrono::seconds(identify_duration); +    bool led_state = true; +    while (std::chrono::steady_clock::now() < end_time) { +        // Add update_leds +        led_state = !led_state; +        std::this_thread::sleep_for(std::chrono::milliseconds(500)); +    } +} + +void e3xx_radio_control_impl::_set_atr_bits(const size_t chan) +{ +    const auto rx_freq       = radio_control_impl::get_rx_frequency(chan); +    const auto tx_freq       = radio_control_impl::get_tx_frequency(chan); +    const auto rx_ant        = radio_control_impl::get_rx_antenna(chan); +    const uint32_t rx_regs   = this->get_rx_switches(chan, rx_freq, rx_ant); +    const uint32_t tx_regs   = this->get_tx_switches(chan, tx_freq); +    const uint32_t idle_regs = this->get_idle_switches(); + +    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_regs); +    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_RX_ONLY, rx_regs); +    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_regs); +    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_regs | tx_regs); + +    // The LED signal names are reversed, but are consistent with the schematic +    const bool is_txrx = rx_ant == "TX/RX"; +    const int idle_led = 0; +    const int rx_led   = this->get_rx_led(); +    const int tx_led   = this->get_tx_led(); +    const int txrx_led = this->get_txrx_led(); + +    _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_led); +    _leds_gpio[chan]->set_atr_reg( +        usrp::gpio_atr::ATR_REG_RX_ONLY, is_txrx ? txrx_led : rx_led); +    _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_led); +    _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_led | tx_led); +} + +void e3xx_radio_control_impl::set_db_eeprom(const eeprom_map_t& db_eeprom) +{ +    _rpcc->notify_with_token("set_db_eeprom", 0, db_eeprom); +} + +eeprom_map_t e3xx_radio_control_impl::get_db_eeprom() +{ +    return _rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", 0); +} + +/************************************************************************** + * Filter API + *************************************************************************/ +std::vector<std::string> e3xx_radio_control_impl::get_rx_filter_names(const size_t) const +{ +    return _rx_filter_names; +} + +uhd::filter_info_base::sptr e3xx_radio_control_impl::get_rx_filter( +    const std::string& name, const size_t chan) +{ +    return _ad9361->get_filter( +        get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), name); +} + +void e3xx_radio_control_impl::set_rx_filter( +    const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan) +{ +    _ad9361->set_filter( +        get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), name, filter); +} + +std::vector<std::string> e3xx_radio_control_impl::get_tx_filter_names(const size_t) const +{ +    return _tx_filter_names; +} + +uhd::filter_info_base::sptr e3xx_radio_control_impl::get_tx_filter( +    const std::string& name, const size_t chan) +{ +    return _ad9361->get_filter( +        get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), name); +} + +void e3xx_radio_control_impl::set_tx_filter( +    const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan) +{ +    _ad9361->set_filter( +        get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), name, filter); +} + + +/************************************************************************** + * Radio Identification API Calls + *************************************************************************/ +size_t e3xx_radio_control_impl::get_chan_from_dboard_fe( +    const std::string& fe, const uhd::direction_t) const +{ +    // A and B are available here for backward compat +    if (fe == "A" || fe == "0") { +        return 0; +    } +    if (fe == "B" || fe == "1") { +        return 1; +    } +    throw uhd::key_error(std::string("[E3xx] Invalid frontend: ") + fe); +} + +std::string e3xx_radio_control_impl::get_dboard_fe_from_chan( +    const size_t chan, const uhd::direction_t) const +{ +    if (chan == 0) { +        return "0"; +    } +    if (chan == 1) { +        return "1"; +    } +    throw uhd::lookup_error( +        std::string("[E3xx] Invalid channel: ") + std::to_string(chan)); +} + +std::string e3xx_radio_control_impl::get_fe_name( +    const size_t, const uhd::direction_t) const +{ +    return "E3xx"; +} diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp new file mode 100644 index 000000000..f49cde64a --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp @@ -0,0 +1,293 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP +#define INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP + +#include "e3xx_ad9361_iface.hpp" +#include <uhd/types/eeprom.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include <uhd/rfnoc/filter_node.hpp> +#include <uhdlib/rfnoc/radio_control_impl.hpp> +#include <uhdlib/usrp/common/mpmd_mb_controller.hpp> +#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> +#include <mutex> + +namespace uhd { namespace rfnoc { + +namespace e3xx_regs { +    constexpr uint32_t PERIPH_BASE            = 0x80000; +    constexpr uint32_t PERIPH_REG_OFFSET      = 8; +    constexpr uint32_t PERIPH_REG_CHAN_OFFSET = 0x800; +    constexpr uint32_t SR_LEDS                = PERIPH_BASE + 176 * PERIPH_REG_OFFSET; +    constexpr uint32_t SR_FP_GPIO             = PERIPH_BASE + 184 * PERIPH_REG_OFFSET; +    constexpr uint32_t SR_DB_GPIO             = PERIPH_BASE + 192 * PERIPH_REG_OFFSET; + +    constexpr uint32_t RB_DB_GPIO = PERIPH_BASE + 19 * PERIPH_REG_OFFSET; +    constexpr uint32_t RB_FP_GPIO = PERIPH_BASE + 20 * PERIPH_REG_OFFSET; + + +} + +/*! \brief Provide access to an E3xx radio. + */ +class e3xx_radio_control_impl : public radio_control_impl, public uhd::rfnoc::detail::filter_node +{ +public: +    //! Frequency bands for RX. Bands are a function of the analog filter banks +    enum class rx_band { INVALID_BAND, LB_B2, LB_B3, LB_B4, LB_B5, LB_B6, LB_B7, HB }; + +    //! Frequency bands for TX. Bands are a function of the analog filter banks +    enum class tx_band { +        INVALID_BAND, +        LB_80, +        LB_160, +        LB_225, +        LB_400, +        LB_575, +        LB_1000, +        LB_1700, +        LB_2750, +        HB +    }; + +    /************************************************************************** +     * ATR/ Switches Types +     *************************************************************************/ +    //! ATR state +    enum atr_state_t { IDLE, RX_ONLY, TX_ONLY, FULL_DUPLEX }; + +    //! Channel select: +    enum chan_sel_t { CHAN1, CHAN2, BOTH }; + +    /************************************************************************ +     * Structors +     ***********************************************************************/ +    e3xx_radio_control_impl(make_args_ptr make_args); +    virtual ~e3xx_radio_control_impl(); + +    /************************************************************************ +     * node_t && noc_block_base API calls +     ***********************************************************************/ +    void deinit(); + +    bool check_topology(const std::vector<size_t>& connected_inputs, +            const std::vector<size_t>& connected_outputs); + +    /************************************************************************ +     * radio_control API calls +     ***********************************************************************/ +    double set_rate(const double rate); +    uhd::meta_range_t get_rate_range() const; + +    // Setters +    void set_tx_antenna(const std::string &ant, const size_t chan); +    void set_rx_antenna(const std::string &ant, const size_t chan); +    double set_tx_frequency(const double freq, const size_t chan); +    double set_rx_frequency(const double freq, const size_t chan); +    double set_tx_gain(const double gain, const size_t chan); +    double set_rx_gain(const double gain, const size_t chan); +    void set_rx_agc(const bool enable, const size_t chan); +    double set_tx_bandwidth(const double bandwidth, const size_t chan); +    double set_rx_bandwidth(const double bandwidth, const size_t chan); + +    // Getters +    std::vector<std::string> get_tx_antennas(const size_t chan) const; +    std::vector<std::string> get_rx_antennas(const size_t chan) const; +    uhd::freq_range_t get_tx_frequency_range(const size_t chan) const; +    uhd::freq_range_t get_rx_frequency_range(const size_t chan) const; +    uhd::gain_range_t get_tx_gain_range(const size_t) const; +    uhd::gain_range_t get_rx_gain_range(const size_t) const; +    meta_range_t get_tx_bandwidth_range(size_t chan) const; +    meta_range_t get_rx_bandwidth_range(size_t chan) const; + +    /************************************************************************** +     * Calibration-Related API Calls +     *************************************************************************/ +    virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS); +    virtual void set_rx_iq_balance(const bool enb, size_t chan); + +    /************************************************************************** +     * GPIO Controls +     *************************************************************************/ +    virtual void set_gpio_attr( +        const std::string& bank, const std::string& attr, const uint32_t value); +    virtual uint32_t get_gpio_attr(const std::string& bank, const std::string& attr); + +    /************************************************************************** +     * Sensor API +     *************************************************************************/ +    std::vector<std::string> get_rx_sensor_names(size_t chan) const; +    uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan); +    std::vector<std::string> get_tx_sensor_names(size_t chan) const; +    uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan); + +    /************************************************************************** +     * Filter API +     *************************************************************************/ +    std::vector<std::string> get_rx_filter_names(const size_t chan) const; +    uhd::filter_info_base::sptr get_rx_filter(const std::string& name, const size_t chan); +    void set_rx_filter( +        const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan); + +    std::vector<std::string> get_tx_filter_names(const size_t chan) const; +    uhd::filter_info_base::sptr get_tx_filter(const std::string& name, const size_t chan); +    void set_tx_filter( +        const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan); + +    /************************************************************************** +     * Radio Identification API Calls +     *************************************************************************/ +    std::string get_slot_name() const { return "A"; } +    virtual size_t get_chan_from_dboard_fe( +        const std::string& fe, const uhd::direction_t direction) const; +    virtual std::string get_dboard_fe_from_chan( +        const size_t chan, const uhd::direction_t direction) const; +    virtual std::string get_fe_name( +        const size_t chan, const uhd::direction_t direction) const; + +protected: +    //! Map a frequency in Hz to an rx_band value. Will return +    //  rx_band::INVALID_BAND if the frequency is out of range. +    rx_band map_freq_to_rx_band(const double freq); +    //! Map a frequency in Hz to an tx_band value. Will return +    //  tx_band::INVALID_BAND if the frequency is out of range. +    tx_band map_freq_to_tx_band(const double freq); + +    virtual const std::string get_default_timing_mode() = 0; + +    /*! Run a loopback self test. +     * +     * This will write data to the AD936x and read it back again. +     * If this test fails, it generally means the interface is broken, +     * so we assume it passes and throw otherwise. Running this requires +     * a core that we can peek and poke the loopback values into. +     * +     * \param iface An interface to the associated radio control core +     * \param iface The radio control core's address to write the loopback value +     * \param iface The radio control core's readback address to read back the returned +     * value +     * +     * \throws a uhd::runtime_error if the loopback value didn't match. +     */ +    void loopback_self_test(const size_t chan); + +    virtual uint32_t get_rx_switches( +        const size_t chan, const double freq, const std::string& ant) = 0; + +    virtual uint32_t get_tx_switches(const size_t chan, const double freq) = 0; + +    virtual uint32_t get_idle_switches() = 0; + +    virtual uint32_t get_tx_led()   = 0; +    virtual uint32_t get_rx_led()   = 0; +    virtual uint32_t get_txrx_led() = 0; +    virtual uint32_t get_idle_led() = 0; + +    //! Reference to the AD9361 controls +    // e3xx_ad9361_iface::uptr _ad9361; +    ad9361_ctrl::sptr _ad9361; + +    //! Swap RFA and RFB for catalina +    bool _fe_swap; + +    //! Init RPC-related items +    void _init_mpm(); + +private: +    /************************************************************************** +     * Helpers +     *************************************************************************/ +    //! Initialize all the peripherals connected to this block +    void _init_peripherals(); + +    //! Set state of this class to sensible defaults +    void _init_defaults(); + +    //! Init a subtree for the RF frontends +    void _init_frontend_subtree(uhd::property_tree::sptr subtree, const size_t chan_idx); + +    //! Initialize Catalina defaults +    void _init_codec(); + +    //! Initialize property tree +    void _init_prop_tree(); + +    //! Set streaming mode - active chains, channel_mode, timing_mode +    void set_streaming_mode( +        const bool tx1, const bool tx2, const bool rx1, const bool rx2); + +    //! Set which channel mode is used +    void set_channel_mode(const std::string& channel_mode); + +    /************************************************************************** +     * Misc Controls +     *************************************************************************/ +    //! Blink the front-panel LEDs for \p identify_duration, +    //  and resume normal operation. +    void _identify_with_leds(const int identify_duration); + +    void _set_atr_bits(const size_t chan); + +    void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom); + +    uhd::eeprom_map_t get_db_eeprom(); + +    /************************************************************************** +     * Private attributes +     *************************************************************************/ +    //! Locks access to setter APIs +    mutable std::mutex _set_lock; + +    //! Prepended for all dboard RPC calls +    std::string _rpc_prefix = "db_0_"; + +    //! Reference to the MB controller +    uhd::rfnoc::mpmd_mb_controller::sptr _e3xx_mb_control; + +    //! Reference to the MB timekeeper +    uhd::rfnoc::mpmd_mb_controller::mpmd_timekeeper::sptr _e3xx_timekeeper; + +    //! Reference to wb_iface adapters +    std::vector<uhd::timed_wb_iface::sptr> _wb_ifaces; + +    //! Reference to the RPC client +    uhd::rpc_client::sptr _rpcc; + +    //! ATR controls. These control the AD9361 gain up/down bits. +    //  Every radio channel gets its own ATR state register. +    std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _db_gpio; + +    // ATR controls for LEDs +    //  Every radio channel gets its own ATR state register. +    std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _leds_gpio; + +    //! Front panel GPIO controller. Note that only one radio block per +    //  module can be the FP-GPIO master. +    usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio; + +    //! Sampling rate +    double _master_clock_rate = 1.0; + +    //! RX sensor names (they get cached) +    std::vector<std::string> _rx_sensor_names; + +    //! TX sensor names (they get cached) +    std::vector<std::string> _tx_sensor_names; + +    //! RX filter names (they get cached) +    std::vector<std::string> _rx_filter_names; + +    //! TX filter names (they get cached) +    std::vector<std::string> _tx_filter_names; +}; + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */ diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp new file mode 100644 index 000000000..f97feeb68 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp @@ -0,0 +1,305 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "e3xx_constants.hpp" +#include "e3xx_radio_control_impl.hpp" +#include <uhd/transport/chdr.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/log.hpp> +#include <uhdlib/rfnoc/reg_iface_adapter.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/case_conv.hpp> +#include <boost/algorithm/string/split.hpp> +#include <string> +#include <vector> + +using namespace uhd; +using namespace uhd::rfnoc; + +void e3xx_radio_control_impl::_init_defaults() +{ +    RFNOC_LOG_TRACE("Initializing defaults..."); +    const size_t num_rx_chans = get_num_output_ports(); +    const size_t num_tx_chans = get_num_input_ports(); + +    RFNOC_LOG_TRACE( +        "Num TX chans: " << num_tx_chans << " Num RX chans: " << num_rx_chans); + + +    // Note: MCR gets set during the init() call (prior to this), which takes +    // in arguments from the device args. So if block_args contains a +    // master_clock_rate key, then it should better be whatever the device is +    // configured to do. +    auto block_args = get_block_args(); +    _master_clock_rate = +        _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate"); +    const double block_args_mcr = +        block_args.cast<double>("master_clock_rate", _master_clock_rate); +    if (block_args_mcr != _master_clock_rate) { +        throw uhd::runtime_error( +            str(boost::format("Master clock rate mismatch. Device returns %f MHz, " +                              "but should have been %f MHz.") +                % (_master_clock_rate / 1e6) % (block_args_mcr / 1e6))); +    } +    RFNOC_LOG_DEBUG("Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz."); +    set_tick_rate(_master_clock_rate); +    _e3xx_timekeeper->update_tick_rate(_master_clock_rate); +    radio_control_impl::set_rate(_master_clock_rate); +    for (size_t chan = 0; chan < num_rx_chans; chan++) { +        radio_control_impl::set_rx_frequency(E3XX_DEFAULT_FREQ, chan); +        radio_control_impl::set_rx_gain(E3XX_DEFAULT_GAIN, chan); +        radio_control_impl::set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan); +        radio_control_impl::set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); +    } + +    for (size_t chan = 0; chan < num_tx_chans; chan++) { +        radio_control_impl::set_tx_frequency(E3XX_DEFAULT_FREQ, chan); +        radio_control_impl::set_tx_gain(E3XX_DEFAULT_GAIN, chan); +        radio_control_impl::set_tx_antenna(E3XX_DEFAULT_TX_ANTENNA, chan); +        radio_control_impl::set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); +    } + +    _rx_sensor_names = _rpcc->request_with_token<std::vector<std::string>>( +        this->_rpc_prefix + "get_sensors", "RX"); +    _tx_sensor_names = _rpcc->request_with_token<std::vector<std::string>>( +        this->_rpc_prefix + "get_sensors", "TX"); + +    // Cache the filter names +    // FIXME: Uncomment this +    //_rx_filter_names = _ad9361->get_filter_names( +    //    get_which_ad9361_chain(RX_DIRECTION, 0, _fe_swap)); +    //_tx_filter_names = _ad9361->get_filter_names( +    //    get_which_ad9361_chain(TX_DIRECTION, 0, _fe_swap)); +} + +void e3xx_radio_control_impl::_init_peripherals() +{ +    RFNOC_LOG_TRACE("Initializing peripherals..."); +    for (size_t radio_idx = 0; radio_idx < E3XX_NUM_CHANS; radio_idx++) { +        _wb_ifaces.push_back(RFNOC_MAKE_WB_IFACE(0, radio_idx)); +    } +    _db_gpio.clear(); // Following the as-if rule, this can get optimized out +    for (size_t radio_idx = 0; radio_idx < E3XX_NUM_CHANS; radio_idx++) { +        RFNOC_LOG_TRACE("Initializing DB GPIOs for channel " << radio_idx); +        // Note: The register offset is baked into the different _wb_iface +        // objects! +        _db_gpio.emplace_back( +            usrp::gpio_atr::gpio_atr_3000::make_write_only(_wb_ifaces.at(radio_idx), +                e3xx_regs::SR_DB_GPIO + (radio_idx * e3xx_regs::PERIPH_REG_CHAN_OFFSET), +                e3xx_regs::PERIPH_REG_OFFSET)); +        _db_gpio[radio_idx]->set_atr_mode( +            usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); +    } +    _leds_gpio.clear(); // Following the as-if rule, this can get optimized out +    for (size_t radio_idx = 0; radio_idx < E3XX_NUM_CHANS; radio_idx++) { +        RFNOC_LOG_TRACE("Initializing LED GPIOs for channel " << radio_idx); +        _leds_gpio.emplace_back( +            usrp::gpio_atr::gpio_atr_3000::make_write_only(_wb_ifaces.at(radio_idx), +                e3xx_regs::SR_LEDS + (radio_idx * e3xx_regs::PERIPH_REG_CHAN_OFFSET), +                e3xx_regs::PERIPH_REG_OFFSET)); +        _leds_gpio[radio_idx]->set_atr_mode( +            usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); +    } +    RFNOC_LOG_TRACE("Initializing front-panel GPIO control...") +    _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make( +        _wb_ifaces.at(0), e3xx_regs::SR_FP_GPIO, e3xx_regs::RB_FP_GPIO, e3xx_regs::PERIPH_REG_OFFSET); + + +    auto block_args = get_block_args(); +    if (block_args.has_key("identify")) { +        const std::string identify_val = block_args.get("identify"); +        int identify_duration          = std::atoi(identify_val.c_str()); +        if (identify_duration == 0) { +            identify_duration = 5; +        } +        _identify_with_leds(identify_duration); +    } +} + +void e3xx_radio_control_impl::_init_frontend_subtree( +    uhd::property_tree::sptr subtree, const size_t chan_idx) +{ +    const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx; +    const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx; +    RFNOC_LOG_TRACE( +        "Adding non-RFNoC block properties for channel " +            << chan_idx << " to prop tree path " << tx_fe_path << " and " << rx_fe_path); +    // TX Standard attributes +    subtree->create<std::string>(tx_fe_path / "name").set("E3xx"); +    subtree->create<std::string>(tx_fe_path / "connection").set("IQ"); +    // RX Standard attributes +    subtree->create<std::string>(rx_fe_path / "name").set("E3xx"); +    subtree->create<std::string>(rx_fe_path / "connection").set("IQ"); +    // TX Antenna +    subtree->create<std::string>(tx_fe_path / "antenna" / "value") +        .add_coerced_subscriber([this, chan_idx](const std::string& ant) { +            this->set_tx_antenna(ant, chan_idx); +        }) +        .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); }); +    subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options") +        .set({E3XX_DEFAULT_TX_ANTENNA}) +        .add_coerced_subscriber([](const std::vector<std::string>&) { +            throw uhd::runtime_error("Attempting to update antenna options!"); +        }); +    // RX Antenna +    subtree->create<std::string>(rx_fe_path / "antenna" / "value") +        .add_coerced_subscriber([this, chan_idx](const std::string& ant) { +            this->set_rx_antenna(ant, chan_idx); +        }) +        .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); }); +    subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options") +        .set(E3XX_RX_ANTENNAS) +        .add_coerced_subscriber([](const std::vector<std::string>&) { +            throw uhd::runtime_error("Attempting to update antenna options!"); +        }); +    // TX frequency +    subtree->create<double>(tx_fe_path / "freq" / "value") +        .set_coercer([this, chan_idx](const double freq) { +            return this->set_tx_frequency(freq, chan_idx); +        }) +        .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); }); +    subtree->create<meta_range_t>(tx_fe_path / "freq" / "range") +        .set_publisher([this]() { return get_tx_frequency_range(0); }) +        .add_coerced_subscriber([](const meta_range_t&) { +            throw uhd::runtime_error("Attempting to update freq range!"); +        }); +    // RX frequency +    subtree->create<double>(rx_fe_path / "freq" / "value") +        .set_coercer([this, chan_idx](const double freq) { +            return this->set_rx_frequency(freq, chan_idx); +        }) +        .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); }); +    subtree->create<meta_range_t>(rx_fe_path / "freq" / "range") +        .set_publisher([this]() { return get_rx_frequency_range(0); }) +        .add_coerced_subscriber([](const meta_range_t&) { +            throw uhd::runtime_error("Attempting to update freq range!"); +        }); +    // TX bandwidth +    subtree->create<double>(tx_fe_path / "bandwidth" / "value") +        .set_publisher([this, chan_idx]() { return get_tx_bandwidth(chan_idx); }) +        .set_coercer([this, chan_idx](const double bw) { +            return this->set_tx_bandwidth(bw, chan_idx); +        }) +        .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); }); +    subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range") +        .set_publisher([this]() { return get_tx_bandwidth_range(0); }) +        .add_coerced_subscriber([](const meta_range_t&) { +            throw uhd::runtime_error("Attempting to update bandwidth range!"); +        }); +    // RX bandwidth +    subtree->create<double>(rx_fe_path / "bandwidth" / "value") +        .set_publisher([this, chan_idx]() { return get_rx_bandwidth(chan_idx); }) +        .set_coercer([this, chan_idx](const double bw) { +            return this->set_rx_bandwidth(bw, chan_idx); +        }); +    subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range") +        .set_publisher([this]() { return get_rx_bandwidth_range(0); }) +        .add_coerced_subscriber([](const meta_range_t&) { +            throw uhd::runtime_error("Attempting to update bandwidth range!"); +        }); + +    // TX gains +    const std::vector<std::string> tx_gain_names = ad9361_ctrl::get_gain_names("TX1"); +    for (auto tx_gain_name : tx_gain_names) { +        subtree->create<double>(tx_fe_path / "gains" / tx_gain_name / "value") +            .set_coercer([this, chan_idx](const double gain) { +                return this->set_tx_gain(gain, chan_idx); +            }) +            .set_publisher( +                [this, chan_idx]() { return radio_control_impl::get_tx_gain(chan_idx); }); +        subtree->create<meta_range_t>(tx_fe_path / "gains" / tx_gain_name / "range") +            .add_coerced_subscriber([](const meta_range_t&) { +                throw uhd::runtime_error("Attempting to update gain range!"); +            }) +            .set_publisher([this]() { +                return meta_range_t( +                    AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN, AD9361_TX_GAIN_STEP); +            }); +    } + +    // RX gains +    const std::vector<std::string> rx_gain_names = ad9361_ctrl::get_gain_names("RX1"); +    for (auto rx_gain_name : rx_gain_names) { +        subtree->create<double>(rx_fe_path / "gains" / rx_gain_name / "value") +            .set_coercer([this, chan_idx](const double gain) { +                return this->set_rx_gain(gain, chan_idx); +            }) +            .set_publisher( +                [this, chan_idx]() { return radio_control_impl::get_rx_gain(chan_idx); }); + +        subtree->create<meta_range_t>(rx_fe_path / "gains" / rx_gain_name / "range") +            .add_coerced_subscriber([](const meta_range_t&) { +                throw uhd::runtime_error("Attempting to update gain range!"); +            }) +            .set_publisher([this]() { +                return meta_range_t( +                    AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN, AD9361_RX_GAIN_STEP); +            }); +    } + +    auto rx_sensor_names = get_rx_sensor_names(chan_idx); +    for (const auto& rx_sensor_name : rx_sensor_names) { +        RFNOC_LOG_TRACE("Adding RX sensor " << rx_sensor_name); +        get_tree()->create<sensor_value_t>(rx_fe_path / "sensors" / rx_sensor_name) +            .add_coerced_subscriber([](const sensor_value_t&) { +                throw uhd::runtime_error("Attempting to write to sensor!"); +            }) +            .set_publisher([this, rx_sensor_name, chan_idx]() { +                return get_rx_sensor(rx_sensor_name, chan_idx); +            }); +    } +    auto tx_sensor_names = get_tx_sensor_names(chan_idx); +    for (const auto& tx_sensor_name : tx_sensor_names) { +        RFNOC_LOG_TRACE("Adding TX sensor " << tx_sensor_name); +        get_tree()->create<sensor_value_t>(tx_fe_path / "sensors" / tx_sensor_name) +            .add_coerced_subscriber([](const sensor_value_t&) { +                throw uhd::runtime_error("Attempting to write to sensor!"); +            }) +            .set_publisher([this, tx_sensor_name, chan_idx]() { +                return get_tx_sensor(tx_sensor_name, chan_idx); +            }); +    } +} + +void e3xx_radio_control_impl::_init_prop_tree() +{ +    for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) { +        this->_init_frontend_subtree(get_tree()->subtree(DB_PATH), chan_idx); +    } +    get_tree()->create<std::string>("rx_codec/name").set("AD9361 Dual ADC"); +    get_tree()->create<std::string>("tx_codec/name").set("AD9361 Dual DAC"); +} + +void e3xx_radio_control_impl::_init_mpm() +{ +    // Initialize catalina +    _init_codec(); + +    // Loopback test +    for (size_t chan = 0; chan < E3XX_NUM_CHANS; chan++) { +        loopback_self_test(chan); +    } +} + +void e3xx_radio_control_impl::_init_codec() +{ +    RFNOC_LOG_TRACE("Setting Catalina Defaults... "); +    for (size_t chan = 0; chan < E3XX_NUM_CHANS; chan++) { +        std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan); +        this->set_rx_gain(E3XX_DEFAULT_GAIN, chan); +        this->set_rx_frequency(E3XX_DEFAULT_FREQ, chan); +        this->set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan); +        this->set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); +        _ad9361->set_dc_offset_auto(rx_fe, E3XX_DEFAULT_AUTO_DC_OFFSET); +        _ad9361->set_iq_balance_auto(rx_fe, E3XX_DEFAULT_AUTO_IQ_BALANCE); +        _ad9361->set_agc(rx_fe, E3XX_DEFAULT_AGC_ENABLE); +        std::string tx_fe = get_which_ad9361_chain(TX_DIRECTION, chan); +        this->set_tx_gain(E3XX_DEFAULT_GAIN, chan); +        this->set_tx_frequency(E3XX_DEFAULT_FREQ, chan); +        this->set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); +    } +} + diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp deleted file mode 100644 index 989b73b82..000000000 --- a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp +++ /dev/null @@ -1,343 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e3xx_radio_ctrl_impl.hpp" -#include "e3xx_constants.hpp" -#include <uhd/rfnoc/node_ctrl_base.hpp> -#include <uhd/transport/chdr.hpp> -#include <uhd/types/direction.hpp> -#include <uhd/types/eeprom.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/math.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/format.hpp> -#include <boost/make_shared.hpp> -#include <cmath> -#include <cstdlib> -#include <sstream> - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::rfnoc; -using namespace uhd::math::fp_compare; - -/****************************************************************************** - * Structors - *****************************************************************************/ -e3xx_radio_ctrl_impl::e3xx_radio_ctrl_impl() -{ -    UHD_LOG_TRACE(unique_id(), "Entering e3xx_radio_ctrl_impl ctor..."); -    const char radio_slot_name[1] = {'A'}; -    _radio_slot                   = radio_slot_name[get_block_id().get_block_count()]; -    UHD_LOG_TRACE(unique_id(), "Radio slot: " << _radio_slot); -    _rpc_prefix = "db_0_"; - -    _init_defaults(); -    _init_peripherals(); -    _init_prop_tree(); -} - -e3xx_radio_ctrl_impl::~e3xx_radio_ctrl_impl() -{ -    UHD_LOG_TRACE(unique_id(), "e3xx_radio_ctrl_impl::dtor() "); -} - - -/****************************************************************************** - * API Calls - *****************************************************************************/ - -void e3xx_radio_ctrl_impl::set_streaming_mode( -    const bool tx1, const bool tx2, const bool rx1, const bool rx2) -{ -    UHD_LOG_TRACE(unique_id(), "Setting up streaming ...") -    const size_t num_rx = rx1 + rx2; -    const size_t num_tx = tx1 + tx2; - -    // setup the active chains in the codec -    if ((num_rx + num_tx) == 0) { -        // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock -        _ad9361->set_active_chains(true, false, true, false); -    } else { -        // setup the active chains in the codec -        _ad9361->set_active_chains(tx1, tx2, rx1, rx2); -    } - -    // setup 1R1T/2R2T mode in catalina and fpga -    // The Catalina interface in the fpga needs to know which TX channel to use for -    // the data on the LVDS lines. -    if ((num_rx == 2) or (num_tx == 2)) { -        // AD9361 is in 1R1T mode -        _ad9361->set_timing_mode(this->get_default_timing_mode()); -        this->set_channel_mode(MIMO); -    } else { -        // AD9361 is in 1R1T mode -        _ad9361->set_timing_mode(TIMING_MODE_1R1T); - -        // Set to SIS0_TX1 if we're using the second TX antenna, otherwise -        // default to SISO_TX0 -        this->set_channel_mode(tx2 ? SISO_TX1 : SISO_TX0); -    } -} - -void e3xx_radio_ctrl_impl::set_channel_mode(const std::string& channel_mode) -{ -    // MIMO for 2R2T mode for 2 channels -    // SISO_TX1 for 1R1T mode for 1 channel - TX1 -    // SISO_TX0 for 1R1T mode for 1 channel - TX0 - -    _rpcc->request_with_token<void>("set_channel_mode", channel_mode); -} - -double e3xx_radio_ctrl_impl::set_rate(const double rate) -{ -    std::lock_guard<std::mutex> l(_set_lock); -    UHD_LOG_DEBUG(unique_id(), "Asking for clock rate " << rate / 1e6 << " MHz\n"); -    double actual_tick_rate = _ad9361->set_clock_rate(rate); -    UHD_LOG_DEBUG( -        unique_id(), "Actual clock rate " << actual_tick_rate / 1e6 << " MHz\n"); - -    radio_ctrl_impl::set_rate(rate); -    return rate; -} - -void e3xx_radio_ctrl_impl::set_tx_antenna(const std::string& ant, const size_t chan) -{ -    if (ant != get_tx_antenna(chan)) { -        throw uhd::value_error( -            str(boost::format("[%s] Requesting invalid TX antenna value: %s") -                % unique_id() % ant)); -    } -    radio_ctrl_impl::set_tx_antenna(ant, chan); -    // We can't actually set the TX antenna, so let's stop here. -} - -void e3xx_radio_ctrl_impl::set_rx_antenna(const std::string& ant, const size_t chan) -{ -    UHD_ASSERT_THROW(chan <= E3XX_NUM_CHANS); -    if (std::find(E3XX_RX_ANTENNAS.begin(), E3XX_RX_ANTENNAS.end(), ant) -        == E3XX_RX_ANTENNAS.end()) { -        throw uhd::value_error( -            str(boost::format("[%s] Requesting invalid RX antenna value: %s") -                % unique_id() % ant)); -    } -    UHD_LOG_TRACE(unique_id(), "Setting RX antenna to " << ant << " for chan " << chan); - -    radio_ctrl_impl::set_rx_antenna(ant, chan); -    _set_atr_bits(chan); -} - -double e3xx_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan) -{ -    UHD_LOG_TRACE(unique_id(), "set_tx_frequency(f=" << freq << ", chan=" << chan << ")"); -    std::lock_guard<std::mutex> l(_set_lock); - -    double clipped_freq = uhd::clip(freq, AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ); - -    double coerced_freq = -        _ad9361->tune(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clipped_freq); -    radio_ctrl_impl::set_tx_frequency(coerced_freq, chan); -    // Front-end switching -    _set_atr_bits(chan); - -    return coerced_freq; -} - -double e3xx_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) -{ -    UHD_LOG_TRACE(unique_id(), "set_rx_frequency(f=" << freq << ", chan=" << chan << ")"); -    std::lock_guard<std::mutex> l(_set_lock); - -    double clipped_freq = uhd::clip(freq, AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ); - -    double coerced_freq = -        _ad9361->tune(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clipped_freq); -    radio_ctrl_impl::set_rx_frequency(coerced_freq, chan); -    // Front-end switching -    _set_atr_bits(chan); - -    return coerced_freq; -} - -double e3xx_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) -{ -    std::lock_guard<std::mutex> l(_set_lock); -    double clipped_bw = -        _ad9361->set_bw_filter(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), bandwidth); -    return radio_ctrl_impl::set_rx_bandwidth(clipped_bw, chan); -} - -double e3xx_radio_ctrl_impl::set_tx_bandwidth(const double bandwidth, const size_t chan) -{ -    std::lock_guard<std::mutex> l(_set_lock); -    double clipped_bw = -        _ad9361->set_bw_filter(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), bandwidth); -    return radio_ctrl_impl::set_tx_bandwidth(clipped_bw, chan); -} - -double e3xx_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) -{ -    std::lock_guard<std::mutex> l(_set_lock); -    UHD_LOG_TRACE(unique_id(), "set_tx_gain(gain=" << gain << ", chan=" << chan << ")"); -    double clip_gain = uhd::clip(gain, AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN); -    _ad9361->set_gain(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clip_gain); -    radio_ctrl_impl::set_tx_gain(clip_gain, chan); -    return clip_gain; -} - -double e3xx_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) -{ -    std::lock_guard<std::mutex> l(_set_lock); -    UHD_LOG_TRACE(unique_id(), "set_rx_gain(gain=" << gain << ", chan=" << chan << ")"); -    double clip_gain = uhd::clip(gain, AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN); -    _ad9361->set_gain(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clip_gain); -    radio_ctrl_impl::set_rx_gain(clip_gain, chan); -    return clip_gain; -} - -size_t e3xx_radio_ctrl_impl::get_chan_from_dboard_fe( -    const std::string& fe, const direction_t /* dir */ -) -{ -    const size_t chan = boost::lexical_cast<size_t>(fe); -    if (chan > _get_num_radios() - 1) { -        UHD_LOG_WARNING(unique_id(), -            boost::format("Invalid channel determined from dboard frontend %s.") % fe); -    } -    return chan; -} - -std::string e3xx_radio_ctrl_impl::get_dboard_fe_from_chan( -    const size_t chan, const direction_t /* dir */ -) -{ -    return std::to_string(chan); -} - -void e3xx_radio_ctrl_impl::set_rpc_client( -    uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args) -{ -    _rpcc       = rpcc; -    _block_args = block_args; -    UHD_LOG_TRACE(unique_id(), "Instantiating AD9361 control object..."); -    _ad9361 = make_rpc(_rpcc); - -    UHD_LOG_TRACE(unique_id(), "Setting Catalina Defaults... "); -    // Initialize catalina -    this->_init_codec(); - -    if (block_args.has_key("identify")) { -        const std::string identify_val = block_args.get("identify"); -        int identify_duration          = std::atoi(identify_val.c_str()); -        if (identify_duration == 0) { -            identify_duration = 5; -        } -        UHD_LOG_INFO(unique_id(), -            "Running LED identification process for " << identify_duration -                                                      << " seconds."); -        _identify_with_leds(identify_duration); -    } -    // Note: MCR gets set during the init() call (prior to this), which takes -    // in arguments from the device args. So if block_args contains a -    // master_clock_rate key, then it should better be whatever the device is -    // configured to do. -    _master_clock_rate = -        _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate"); -    if (block_args.cast<double>("master_clock_rate", _master_clock_rate) -        != _master_clock_rate) { -        throw uhd::runtime_error(str( -            boost::format("Master clock rate mismatch. Device returns %f MHz, " -                          "but should have been %f MHz.") -            % (_master_clock_rate / 1e6) -            % (block_args.cast<double>("master_clock_rate", _master_clock_rate) / 1e6))); -    } -    UHD_LOG_DEBUG( -        unique_id(), "Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz."); -    this->set_rate(_master_clock_rate); - -    // Loopback test -    for (size_t chan = 0; chan < _get_num_radios(); chan++) { -        loopback_self_test( -            [this, chan]( -                const uint32_t value) { this->sr_write(regs::CODEC_IDLE, value, chan); }, -            [this, chan]() { -                return this->user_reg_read64(regs::RB_CODEC_READBACK, chan); -            }); -    } - -    const size_t db_idx = get_block_id().get_block_count(); -    _tree->access<eeprom_map_t>(_root_path / "eeprom") -        .add_coerced_subscriber([this, db_idx](const eeprom_map_t& db_eeprom) { -            this->_rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom); -        }) -        .set_publisher([this, db_idx]() { -            return this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx); -        }); - -    // Init sensors -    for (const auto& dir : std::vector<direction_t>{RX_DIRECTION, TX_DIRECTION}) { -        for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) { -            _init_mpm_sensors(dir, chan_idx); -        } -    } -} - -bool e3xx_radio_ctrl_impl::get_lo_lock_status(const direction_t dir) -{ -    if (not(bool(_rpcc))) { -        UHD_LOG_DEBUG(unique_id(), "Reported no LO lock due to lack of RPC connection."); -        return false; -    } - -    const std::string trx = (dir == RX_DIRECTION) ? "rx" : "tx"; -    bool lo_lock = -        _rpcc->request_with_token<bool>(_rpc_prefix + "get_ad9361_lo_lock", trx); -    UHD_LOG_TRACE(unique_id(), -        "AD9361 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No")); - -    return lo_lock; -} - -void e3xx_radio_ctrl_impl::_set_atr_bits(const size_t chan) -{ -    const auto rx_freq       = radio_ctrl_impl::get_rx_frequency(chan); -    const auto tx_freq       = radio_ctrl_impl::get_tx_frequency(chan); -    const auto rx_ant        = radio_ctrl_impl::get_rx_antenna(chan); -    const uint32_t rx_regs   = this->get_rx_switches(chan, rx_freq, rx_ant); -    const uint32_t tx_regs   = this->get_tx_switches(chan, tx_freq); -    const uint32_t idle_regs = this->get_idle_switches(); - -    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_regs); -    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_RX_ONLY, rx_regs); -    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_regs); -    _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_regs | tx_regs); - -    // The LED signal names are reversed, but are consistent with the schematic -    const bool is_txrx = rx_ant == "TX/RX"; -    const int idle_led = 0; -    const int rx_led   = this->get_rx_led(); -    const int tx_led   = this->get_tx_led(); -    const int txrx_led = this->get_txrx_led(); - -    _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_led); -    _leds_gpio[chan]->set_atr_reg( -        usrp::gpio_atr::ATR_REG_RX_ONLY, is_txrx ? txrx_led : rx_led); -    _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_led); -    _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_led | tx_led); -} - -void e3xx_radio_ctrl_impl::_identify_with_leds(const int identify_duration) -{ -    auto end_time = -        std::chrono::steady_clock::now() + std::chrono::seconds(identify_duration); -    bool led_state = true; -    while (std::chrono::steady_clock::now() < end_time) { -        // Add update_leds -        led_state = !led_state; -        std::this_thread::sleep_for(std::chrono::milliseconds(500)); -    } -} diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp deleted file mode 100644 index 41c2c4594..000000000 --- a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp +++ /dev/null @@ -1,215 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP -#    define INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP - -#    include "e3xx_ad9361_iface.hpp" -#    include <uhd/types/serial.hpp> -#    include <uhd/usrp/dboard_manager.hpp> -#    include <uhd/usrp/gpio_defs.hpp> -#    include <uhdlib/rfnoc/radio_ctrl_impl.hpp> -#    include <uhdlib/rfnoc/rpc_block_ctrl.hpp> -#    include <uhdlib/usrp/cores/gpio_atr_3000.hpp> -#    include <mutex> - -namespace uhd { namespace rfnoc { - -/*! \brief Provide access to an E3xx radio. - */ -class e3xx_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl -{ -public: -    typedef boost::shared_ptr<e3xx_radio_ctrl_impl> sptr; - -    //! Frequency bands for RX. Bands are a function of the analog filter banks -    enum class rx_band { INVALID_BAND, LB_B2, LB_B3, LB_B4, LB_B5, LB_B6, LB_B7, HB }; - -    //! Frequency bands for TX. Bands are a function of the analog filter banks -    enum class tx_band { -        INVALID_BAND, -        LB_80, -        LB_160, -        LB_225, -        LB_400, -        LB_575, -        LB_1000, -        LB_1700, -        LB_2750, -        HB -    }; - -    /************************************************************************** -     * ATR/ Switches Types -     *************************************************************************/ -    //! ATR state -    enum atr_state_t { IDLE, RX_ONLY, TX_ONLY, FULL_DUPLEX }; - -    //! Channel select: -    enum chan_sel_t { CHAN1, CHAN2, BOTH }; - -    /************************************************************************ -     * Structors -     ***********************************************************************/ -    e3xx_radio_ctrl_impl(); -    virtual ~e3xx_radio_ctrl_impl(); - -    /************************************************************************ -     * API calls -     ***********************************************************************/ - -    // Note: We use the cached values in radio_ctrl_impl, so most getters are -    // not reimplemented here -    //! Set streaming mode - active chains, channel_mode, timing_mode -    void set_streaming_mode( -        const bool tx1, const bool tx2, const bool rx1, const bool rx2); - -    //! Set which channel mode is used -    void set_channel_mode(const std::string& channel_mode); - -    double set_rate(const double rate); - -    void set_tx_antenna(const std::string& ant, const size_t chan); -    void set_rx_antenna(const std::string& ant, const size_t chan); - -    double set_tx_frequency(const double freq, const size_t chan); -    double set_rx_frequency(const double freq, const size_t chan); -    double set_tx_bandwidth(const double bandwidth, const size_t chan); -    double set_rx_bandwidth(const double bandwidth, const size_t chan); - -    // gain -    double set_tx_gain(const double gain, const size_t chan); -    double set_rx_gain(const double gain, const size_t chan); - -    size_t get_chan_from_dboard_fe(const std::string& fe, const direction_t dir); -    std::string get_dboard_fe_from_chan(const size_t chan, const direction_t dir); - -    void set_rpc_client(uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args); - -protected: -    //! Map a frequency in Hz to an rx_band value. Will return -    //  rx_band::INVALID_BAND if the frequency is out of range. -    rx_band map_freq_to_rx_band(const double freq); -    //! Map a frequency in Hz to an tx_band value. Will return -    //  tx_band::INVALID_BAND if the frequency is out of range. -    tx_band map_freq_to_tx_band(const double freq); - -    virtual const std::string get_default_timing_mode() = 0; - -    /*! Run a loopback self test. -     * -     * This will write data to the AD936x and read it back again. -     * If this test fails, it generally means the interface is broken, -     * so we assume it passes and throw otherwise. Running this requires -     * a core that we can peek and poke the loopback values into. -     * -     * \param iface An interface to the associated radio control core -     * \param iface The radio control core's address to write the loopback value -     * \param iface The radio control core's readback address to read back the returned -     * value -     * -     * \throws a uhd::runtime_error if the loopback value didn't match. -     */ -    virtual void loopback_self_test(std::function<void(uint32_t)> poker_functor, -        std::function<uint64_t()> peeker_functor) = 0; - -    virtual uint32_t get_rx_switches( -        const size_t chan, const double freq, const std::string& ant) = 0; - -    virtual uint32_t get_tx_switches(const size_t chan, const double freq) = 0; - -    virtual uint32_t get_idle_switches() = 0; - -    virtual uint32_t get_tx_led()   = 0; -    virtual uint32_t get_rx_led()   = 0; -    virtual uint32_t get_txrx_led() = 0; -    virtual uint32_t get_idle_led() = 0; - -    //! Reference to the AD9361 controls -    // e3xx_ad9361_iface::uptr _ad9361; -    ad9361_ctrl::sptr _ad9361; - -    //! Swap RFA and RFB for catalina -    bool _fe_swap; - -private: -    /************************************************************************** -     * Helpers -     *************************************************************************/ -    //! Initialize all the peripherals connected to this block -    void _init_peripherals(); - -    //! Set state of this class to sensible defaults -    void _init_defaults(); - -    //! Init a subtree for the RF frontends -    void _init_frontend_subtree(uhd::property_tree::sptr subtree, const size_t chan_idx); - -    //! Initialize Catalina defaults -    void _init_codec(); - -    //! Initialize property tree -    void _init_prop_tree(); - -    void _init_mpm_sensors(const direction_t dir, const size_t chan_idx); - -    /************************************************************************* -     * Sensors -     *************************************************************************/ -    //! Return LO lock status. Factors in current band (low/high) and -    // direction (TX/RX) -    bool get_lo_lock_status(const direction_t dir); - -    /************************************************************************** -     * Misc Controls -     *************************************************************************/ -    //! Blink the front-panel LEDs for \p identify_duration, -    //  and resume normal operation. -    void _identify_with_leds(const int identify_duration); - -    void _set_atr_bits(const size_t chan); - -    /************************************************************************** -     * Private attributes -     *************************************************************************/ -    //! Locks access to setter APIs -    std::mutex _set_lock; - -    //! Letter representation of the radio we're currently running -    std::string _radio_slot; - -    //! Prepended for all dboard RPC calls -    std::string _rpc_prefix; - -    //! Additional block args; gets set during set_rpc_client() -    uhd::device_addr_t _block_args; - -    //! Reference to the RPC client -    uhd::rpc_client::sptr _rpcc; - -    //! Reference to the SPI core -    uhd::spi_iface::sptr _spi; - -    //! ATR controls. These control the AD9361 gain -    //  up/down bits. -    //  Every radio channel gets its own ATR state register. -    std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _db_gpio; - -    // ATR controls for LEDs -    std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _leds_gpio; - -    //! Front panel GPIO controller. Note that only one radio block per -    //  module can be the FP-GPIO master. -    usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio; - -    //! Sampling rate -    double _master_clock_rate = 1.0; -}; /* class radio_ctrl_impl */ - -}} /* namespace uhd::rfnoc */ - -#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */ -// vim: sw=4 et: diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp deleted file mode 100644 index 5b33b33e7..000000000 --- a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp +++ /dev/null @@ -1,427 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e3xx_constants.hpp" -#include "e3xx_radio_ctrl_impl.hpp" -#include <uhd/transport/chdr.hpp> -#include <uhd/types/eeprom.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/utils/log.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/algorithm/string/case_conv.hpp> -#include <boost/algorithm/string/split.hpp> -#include <string> -#include <vector> - -using namespace uhd; -using namespace uhd::rfnoc; - -//! Helper function to extract single value of port number. -// -// Each GPIO pins can be controlled by each radio output ports. -// This function convert the format of attribute "Radio_N_M" -// to a single value port number = N*number_of_port_per_radio + M - -uint32_t _extract_port_number( -    std::string radio_src_string, uhd::property_tree::sptr ptree) -{ -    std::string s_val = "0"; -    std::vector<std::string> radio_strings; -    boost::algorithm::split(radio_strings, -        radio_src_string, -        boost::is_any_of("_/"), -        boost::token_compress_on); -    boost::to_lower(radio_strings[0]); -    if (radio_strings.size() < 3) { -        throw uhd::runtime_error(str( -            boost::format("%s is an invalid GPIO source string.") % radio_src_string)); -    } -    size_t radio_num = std::stoi(radio_strings[1]); -    size_t port_num  = std::stoi(radio_strings[2]); -    if (radio_strings[0] != "radio") { -        throw uhd::runtime_error( -            "Front panel GPIO bank can only accept a radio block as its driver."); -    } -    std::string radio_port_out  = "Radio_" + radio_strings[1] + "/ports/out"; -    std::string radio_port_path = radio_port_out + "/" + radio_strings[2]; -    auto found                  = ptree->exists(fs_path("xbar") / radio_port_path); -    if (not found) { -        throw uhd::runtime_error( -            str(boost::format("Could not find radio port %s.\n") % radio_port_path)); -    } -    size_t port_size = ptree->list(fs_path("xbar") / radio_port_out).size(); -    return radio_num * port_size + port_num; -} - -void e3xx_radio_ctrl_impl::_init_defaults() -{ -    UHD_LOG_TRACE(unique_id(), "Initializing defaults..."); -    const size_t num_rx_chans = get_output_ports().size(); -    const size_t num_tx_chans = get_input_ports().size(); - -    UHD_LOG_TRACE(unique_id(), -        "Num TX chans: " << num_tx_chans << " Num RX chans: " << num_rx_chans); - -    for (size_t chan = 0; chan < num_rx_chans; chan++) { -        radio_ctrl_impl::set_rx_frequency(E3XX_DEFAULT_FREQ, chan); -        radio_ctrl_impl::set_rx_gain(E3XX_DEFAULT_GAIN, chan); -        radio_ctrl_impl::set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan); -        radio_ctrl_impl::set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); -    } - -    for (size_t chan = 0; chan < num_tx_chans; chan++) { -        radio_ctrl_impl::set_tx_frequency(E3XX_DEFAULT_FREQ, chan); -        radio_ctrl_impl::set_tx_gain(E3XX_DEFAULT_GAIN, chan); -        radio_ctrl_impl::set_tx_antenna(E3XX_DEFAULT_TX_ANTENNA, chan); -        radio_ctrl_impl::set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); -    } - -    /** Update default SPP (overwrites the default value from the XML file) **/ -    const size_t max_bytes_header = -        uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t); -    const size_t default_spp = -        (_tree->access<size_t>("mtu/recv").get() - max_bytes_header) -        / (2 * sizeof(int16_t)); -    UHD_LOG_DEBUG(unique_id(), "Setting default spp to " << default_spp); -    _tree->access<int>(get_arg_path("spp") / "value").set(default_spp); -} - -void e3xx_radio_ctrl_impl::_init_peripherals() -{ -    UHD_LOG_TRACE(unique_id(), "Initializing peripherals..."); - -    _db_gpio.clear(); // Following the as-if rule, this can get optimized out -    for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) { -        UHD_LOG_TRACE(unique_id(), "Initializing GPIOs for channel " << radio_idx); -        _db_gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make_write_only( -            _get_ctrl(radio_idx), regs::sr_addr(regs::GPIO))); -        _db_gpio[radio_idx]->set_atr_mode( -            usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); -    } -    _leds_gpio.clear(); // Following the as-if rule, this can get optimized out -    for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) { -        UHD_LOG_TRACE(unique_id(), "Initializing GPIOs for channel " << radio_idx); -        _leds_gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make_write_only( -            _get_ctrl(radio_idx), regs::sr_addr(regs::LEDS))); - -        _leds_gpio[radio_idx]->set_atr_mode( -            usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); -    } -    UHD_LOG_TRACE(unique_id(), "Initializing front-panel GPIO control...") -    _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make( -        _get_ctrl(0), regs::sr_addr(regs::FP_GPIO), regs::rb_addr(regs::RB_FP_GPIO)); -} - -void e3xx_radio_ctrl_impl::_init_frontend_subtree( -    uhd::property_tree::sptr subtree, const size_t chan_idx) -{ -    const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx; -    const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx; -    UHD_LOG_TRACE(unique_id(), -        "Adding non-RFNoC block properties for channel " -            << chan_idx << " to prop tree path " << tx_fe_path << " and " << rx_fe_path); -    // TX Standard attributes -    subtree->create<std::string>(tx_fe_path / "name").set(str(boost::format("E3xx"))); -    subtree->create<std::string>(tx_fe_path / "connection").set("IQ"); -    // RX Standard attributes -    subtree->create<std::string>(rx_fe_path / "name").set(str(boost::format("E3xx"))); -    subtree->create<std::string>(rx_fe_path / "connection").set("IQ"); -    // TX Antenna -    subtree->create<std::string>(tx_fe_path / "antenna" / "value") -        .add_coerced_subscriber([this, chan_idx](const std::string& ant) { -            this->set_tx_antenna(ant, chan_idx); -        }) -        .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); }); -    subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options") -        .set({E3XX_DEFAULT_TX_ANTENNA}) -        .add_coerced_subscriber([](const std::vector<std::string>&) { -            throw uhd::runtime_error("Attempting to update antenna options!"); -        }); -    // RX Antenna -    subtree->create<std::string>(rx_fe_path / "antenna" / "value") -        .add_coerced_subscriber([this, chan_idx](const std::string& ant) { -            this->set_rx_antenna(ant, chan_idx); -        }) -        .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); }); -    subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options") -        .set(E3XX_RX_ANTENNAS) -        .add_coerced_subscriber([](const std::vector<std::string>&) { -            throw uhd::runtime_error("Attempting to update antenna options!"); -        }); -    // TX frequency -    subtree->create<double>(tx_fe_path / "freq" / "value") -        .set_coercer([this, chan_idx](const double freq) { -            return this->set_tx_frequency(freq, chan_idx); -        }) -        .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); }); -    subtree->create<meta_range_t>(tx_fe_path / "freq" / "range") -        .set(meta_range_t(AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ, 1.0)) -        .add_coerced_subscriber([](const meta_range_t&) { -            throw uhd::runtime_error("Attempting to update freq range!"); -        }); -    // RX frequency -    subtree->create<double>(rx_fe_path / "freq" / "value") -        .set_coercer([this, chan_idx](const double freq) { -            return this->set_rx_frequency(freq, chan_idx); -        }) -        .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); }); -    subtree->create<meta_range_t>(rx_fe_path / "freq" / "range") -        .set(meta_range_t(AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ, 1.0)) -        .add_coerced_subscriber([](const meta_range_t&) { -            throw uhd::runtime_error("Attempting to update freq range!"); -        }); -    // TX bandwidth -    subtree->create<double>(tx_fe_path / "bandwidth" / "value") -        .set(AD9361_TX_MAX_BANDWIDTH) -        .set_coercer([this, chan_idx](const double bw) { -            return this->set_tx_bandwidth(bw, chan_idx); -        }) -        .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); }); -    subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range") -        .set(meta_range_t(AD9361_TX_MIN_BANDWIDTH, AD9361_TX_MAX_BANDWIDTH)) -        .add_coerced_subscriber([](const meta_range_t&) { -            throw uhd::runtime_error("Attempting to update bandwidth range!"); -        }); -    // RX bandwidth -    subtree->create<double>(rx_fe_path / "bandwidth" / "value") -        .set(AD9361_RX_MAX_BANDWIDTH) -        .set_coercer([this, chan_idx](const double bw) { -            return this->set_rx_bandwidth(bw, chan_idx); -        }); -    subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range") -        .set(meta_range_t(AD9361_RX_MIN_BANDWIDTH, AD9361_RX_MAX_BANDWIDTH)) -        .add_coerced_subscriber([](const meta_range_t&) { -            throw uhd::runtime_error("Attempting to update bandwidth range!"); -        }); - -    // TX gains -    const std::vector<std::string> tx_gain_names = ad9361_ctrl::get_gain_names("TX1"); -    for (auto tx_gain_name : tx_gain_names) { -        subtree->create<double>(tx_fe_path / "gains" / tx_gain_name / "value") -            .set_coercer([this, chan_idx](const double gain) { -                return this->set_tx_gain(gain, chan_idx); -            }) -            .set_publisher( -                [this, chan_idx]() { return radio_ctrl_impl::get_tx_gain(chan_idx); }); -        subtree->create<meta_range_t>(tx_fe_path / "gains" / tx_gain_name / "range") -            .add_coerced_subscriber([](const meta_range_t&) { -                throw uhd::runtime_error("Attempting to update gain range!"); -            }) -            .set_publisher([this]() { -                return meta_range_t( -                    AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN, AD9361_TX_GAIN_STEP); -            }); -    } - -    // RX gains -    const std::vector<std::string> rx_gain_names = ad9361_ctrl::get_gain_names("RX1"); -    for (auto rx_gain_name : rx_gain_names) { -        subtree->create<double>(rx_fe_path / "gains" / rx_gain_name / "value") -            .set_coercer([this, chan_idx](const double gain) { -                return this->set_rx_gain(gain, chan_idx); -            }) -            .set_publisher( -                [this, chan_idx]() { return radio_ctrl_impl::get_rx_gain(chan_idx); }); - -        subtree->create<meta_range_t>(rx_fe_path / "gains" / rx_gain_name / "range") -            .add_coerced_subscriber([](const meta_range_t&) { -                throw uhd::runtime_error("Attempting to update gain range!"); -            }) -            .set_publisher([this]() { -                return meta_range_t( -                    AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN, AD9361_RX_GAIN_STEP); -            }); -    } - -    // TX LO lock sensor ////////////////////////////////////////////////////// -    // Note: The AD9361 LO lock sensors are generated programmatically in -    // set_rpc_client(). The actual lo_locked publisher is also set there. -    subtree->create<sensor_value_t>(tx_fe_path / "sensors" / "lo_locked") -        .set(sensor_value_t("all_los", false, "locked", "unlocked")) -        .add_coerced_subscriber([](const sensor_value_t&) { -            throw uhd::runtime_error("Attempting to write to sensor!"); -        }) -        .set_publisher([this]() { -            return sensor_value_t( -                "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked"); -        }); -    // RX LO lock sensor (see not on TX LO lock sensor) -    subtree->create<sensor_value_t>(rx_fe_path / "sensors" / "lo_locked") -        .set(sensor_value_t("all_los", false, "locked", "unlocked")) -        .add_coerced_subscriber([](const sensor_value_t&) { -            throw uhd::runtime_error("Attempting to write to sensor!"); -        }) -        .set_publisher([this]() { -            return sensor_value_t( -                "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked"); -        }); -} - -void e3xx_radio_ctrl_impl::_init_prop_tree() -{ -    const fs_path fe_base = fs_path("dboards") / _radio_slot; -    for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) { -        this->_init_frontend_subtree(_tree->subtree(fe_base), chan_idx); -    } - -    _tree->create<eeprom_map_t>(_root_path / "eeprom").set(eeprom_map_t()); - -    _tree->create<int>("rx_codecs" / _radio_slot / "gains"); -    _tree->create<int>("tx_codecs" / _radio_slot / "gains"); -    _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("AD9361 Dual ADC"); -    _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("AD9361 Dual DAC"); - -    if (not _tree->exists("tick_rate")) { -        _tree->create<double>("tick_rate") -            .set_coercer([this](double tick_rate) { return this->set_rate(tick_rate); }) -            .set_publisher([this]() { return this->get_rate(); }); -    } else { -        UHD_LOG_WARNING(unique_id(), "Cannot set tick_rate again"); -    } - -    // *****FP_GPIO************************ -    for (const auto& attr : usrp::gpio_atr::gpio_attr_map) { -        if (not _tree->exists(fs_path("gpio") / "FP0" / attr.second)) { -            switch (attr.first) { -                case usrp::gpio_atr::GPIO_SRC: -                    // FIXME:  move this creation of this branch of ptree out side of -                    // radio impl; -                    // since there's no data dependency between radio and SRC setting for -                    // FP0 -                    _tree -                        ->create<std::vector<std::string>>( -                            fs_path("gpio") / "FP0" / attr.second) -                        .set(std::vector<std::string>( -                            32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) -                        .add_coerced_subscriber( -                            [this, attr](const std::vector<std::string> str_val) { -                                uint32_t radio_src_value = 0; -                                uint32_t master_value    = 0; -                                for (size_t i = 0; i < str_val.size(); i++) { -                                    if (str_val[i] == "PS") { -                                        master_value += 1 << i; -                                        ; -                                    } else { -                                        auto port_num = -                                            _extract_port_number(str_val[i], _tree); -                                        radio_src_value = -                                            (1 << (2 * i)) * port_num + radio_src_value; -                                    } -                                } -                                _rpcc->notify_with_token( -                                    "set_fp_gpio_master", master_value); -                                _rpcc->notify_with_token( -                                    "set_fp_gpio_radio_src", radio_src_value); -                            }); -                    break; -                case usrp::gpio_atr::GPIO_CTRL: -                case usrp::gpio_atr::GPIO_DDR: -                    _tree -                        ->create<std::vector<std::string>>( -                            fs_path("gpio") / "FP0" / attr.second) -                        .set(std::vector<std::string>( -                            32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) -                        .add_coerced_subscriber( -                            [this, attr](const std::vector<std::string> str_val) { -                                uint32_t val = 0; -                                for (size_t i = 0; i < str_val.size(); i++) { -                                    val += usrp::gpio_atr::gpio_attr_value_pair -                                               .at(attr.second) -                                               .at(str_val[i]) -                                           << i; -                                } -                                _fp_gpio->set_gpio_attr(attr.first, val); -                            }); -                    break; -                case usrp::gpio_atr::GPIO_READBACK: { -                    _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second) -                        .set_publisher([this]() { return _fp_gpio->read_gpio(); }); -                } break; -                default: -                    _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second) -                        .set(0) -                        .add_coerced_subscriber([this, attr](const uint32_t val) { -                            _fp_gpio->set_gpio_attr(attr.first, val); -                        }); -            } -        } else { -            switch (attr.first) { -                case usrp::gpio_atr::GPIO_SRC: -                    break; -                case usrp::gpio_atr::GPIO_CTRL: -                case usrp::gpio_atr::GPIO_DDR: -                    _tree -                        ->access<std::vector<std::string>>( -                            fs_path("gpio") / "FP0" / attr.second) -                        .set(std::vector<std::string>( -                            32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) -                        .add_coerced_subscriber( -                            [this, attr](const std::vector<std::string> str_val) { -                                uint32_t val = 0; -                                for (size_t i = 0; i < str_val.size(); i++) { -                                    val += usrp::gpio_atr::gpio_attr_value_pair -                                               .at(attr.second) -                                               .at(str_val[i]) -                                           << i; -                                } -                                _fp_gpio->set_gpio_attr(attr.first, val); -                            }); -                    break; -                case usrp::gpio_atr::GPIO_READBACK: -                    break; -                default: -                    _tree->access<uint32_t>(fs_path("gpio") / "FP0" / attr.second) -                        .set(0) -                        .add_coerced_subscriber([this, attr](const uint32_t val) { -                            _fp_gpio->set_gpio_attr(attr.first, val); -                        }); -            } -        } -    } -} - -void e3xx_radio_ctrl_impl::_init_codec() -{ -    for (size_t chan = 0; chan < _get_num_radios(); chan++) { -        std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan); -        this->set_rx_gain(E3XX_DEFAULT_GAIN, chan); -        this->set_rx_frequency(E3XX_DEFAULT_FREQ, chan); -        this->set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan); -        this->set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); -        _ad9361->set_dc_offset_auto(rx_fe, E3XX_DEFAULT_AUTO_DC_OFFSET); -        _ad9361->set_iq_balance_auto(rx_fe, E3XX_DEFAULT_AUTO_IQ_BALANCE); -        _ad9361->set_agc(rx_fe, E3XX_DEFAULT_AGC_ENABLE); -        std::string tx_fe = get_which_ad9361_chain(TX_DIRECTION, chan); -        this->set_tx_gain(E3XX_DEFAULT_GAIN, chan); -        this->set_tx_frequency(E3XX_DEFAULT_FREQ, chan); -        this->set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan); -    } -} - -void e3xx_radio_ctrl_impl::_init_mpm_sensors(const direction_t dir, const size_t chan_idx) -{ -    const std::string trx = (dir == RX_DIRECTION) ? "RX" : "TX"; -    const fs_path fe_path = fs_path("dboards") / _radio_slot -                            / (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends") -                            / chan_idx; -    auto sensor_list = _rpcc->request_with_token<std::vector<std::string>>( -        this->_rpc_prefix + "get_sensors", trx); -    UHD_LOG_TRACE(unique_id(), -        "Chan " << chan_idx << ": Found " << sensor_list.size() << " " << trx -                << " sensors."); -    for (const auto& sensor_name : sensor_list) { -        UHD_LOG_TRACE(unique_id(), "Adding " << trx << " sensor " << sensor_name); -        _tree->create<sensor_value_t>(fe_path / "sensors" / sensor_name) -            .add_coerced_subscriber([](const sensor_value_t&) { -                throw uhd::runtime_error("Attempting to write to sensor!"); -            }) -            .set_publisher([this, trx, sensor_name, chan_idx]() { -                return sensor_value_t( -                    this->_rpcc->request_with_token<sensor_value_t::sensor_map_t>( -                        this->_rpc_prefix + "get_sensor", trx, sensor_name, chan_idx)); -            }); -    } -} | 
