diff options
Diffstat (limited to 'host/lib/usrp/dboard')
19 files changed, 1484 insertions, 877 deletions
| diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 5328fcc90..8ee79103f 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -31,12 +31,6 @@ if(ENABLE_X300 OR ENABLE_USRP1 OR ENABLE_USRP2 OR ENABLE_B100)      )  endif(ENABLE_X300 OR ENABLE_USRP1 OR ENABLE_USRP2 OR ENABLE_B100) -if(ENABLE_E300) -    LIBUHD_APPEND_SOURCES( -        ${CMAKE_CURRENT_SOURCE_DIR}/db_e3x0.cpp -    ) -endif(ENABLE_E300) -  if(ENABLE_X300)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_ctrl.cpp @@ -46,6 +40,9 @@ if(ENABLE_X300)      )  endif(ENABLE_X300) +if(ENABLE_E300 OR ENABLE_E320) +    INCLUDE_SUBDIRECTORY(e3xx) +endif(ENABLE_E300 OR ENABLE_E320)  if(ENABLE_N300)      INCLUDE_SUBDIRECTORY(magnesium)  endif(ENABLE_N300) @@ -55,5 +52,3 @@ endif(ENABLE_N320)  if(ENABLE_MPMD AND ENABLE_EISCAT)      INCLUDE_SUBDIRECTORY(eiscat)  endif(ENABLE_MPMD AND ENABLE_EISCAT) - -INCLUDE_SUBDIRECTORY(neon) diff --git a/host/lib/usrp/dboard/db_e3x0.cpp b/host/lib/usrp/dboard/db_e3x0.cpp deleted file mode 100644 index 2745ee3e8..000000000 --- a/host/lib/usrp/dboard/db_e3x0.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include <uhd/utils/static.hpp> -#include <uhd/usrp/dboard_base.hpp> -#include <uhd/usrp/dboard_manager.hpp> - -namespace uhd { namespace usrp { - -/*********************************************************************** - * The E310 dboard - *   AD9361 Interface, thus two subdevs - **********************************************************************/ -class e310_dboard : public xcvr_dboard_base{ -public: -    e310_dboard(ctor_args_t args) : xcvr_dboard_base(args) {} - -    virtual ~e310_dboard(void) {} -}; - -/*********************************************************************** - * The E310 dboard - *   AD9364 Interface, thus one subdev - **********************************************************************/ -class e300_dboard : public xcvr_dboard_base{ -public: -    e300_dboard(ctor_args_t args) : xcvr_dboard_base(args) {} - -    virtual ~e300_dboard(void) {} -}; - -/*********************************************************************** - * Register the E310 dboards - **********************************************************************/ -static dboard_base::sptr make_e310_dboard(dboard_base::ctor_args_t args){ -    return dboard_base::sptr(new e310_dboard(args)); -} - -static dboard_base::sptr make_e300_dboard(dboard_base::ctor_args_t args){ -    return dboard_base::sptr(new e300_dboard(args)); -} - -}} // namespace - -using namespace uhd::usrp; - -UHD_STATIC_BLOCK(reg_e3x0_dboards){ -    dboard_manager::register_dboard(0x0110, &make_e310_dboard, "E310 MIMO XCVR"); -    dboard_manager::register_dboard(0x0100, &make_e300_dboard, "E300 SISO XCVR"); -} diff --git a/host/lib/usrp/dboard/e3xx/CMakeLists.txt b/host/lib/usrp/dboard/e3xx/CMakeLists.txt new file mode 100644 index 000000000..36376cd88 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# 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_ad9361_iface.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_bands.cpp +    ) +    LIBUHD_APPEND_SOURCES(${E3XX_SOURCES}) +ENDIF(ENABLE_E300 OR ENABLE_E320) + +IF(ENABLE_E300) +        MESSAGE(STATUS "E300 Enabled") +        LIST(APPEND E300_SOURCES +        ${CMAKE_CURRENT_SOURCE_DIR}/e31x_radio_ctrl_impl.cpp +    ) +    LIBUHD_APPEND_SOURCES(${E300_SOURCES}) +ENDIF(ENABLE_E300) + +IF(ENABLE_E320) +        MESSAGE(STATUS "E320 Enabled") +        LIST(APPEND E320_SOURCES +        ${CMAKE_CURRENT_SOURCE_DIR}/e320_radio_ctrl_impl.cpp +    ) +    LIBUHD_APPEND_SOURCES(${E320_SOURCES}) +ENDIF(ENABLE_E320) diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp new file mode 100644 index 000000000..33565102c --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp @@ -0,0 +1,302 @@ +// +// 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; +    } + +    UHD_LOG_INFO(unique_id(), "RX freq = " << freq); +    const auto band = e3xx_radio_ctrl_impl::map_freq_to_rx_band(freq); +    UHD_LOG_INFO(unique_id(), "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: +        UHD_LOG_ERROR(unique_id(), +            "Cannot map RX frequency to band: " << freq); +        UHD_THROW_INVALID_CODE_PATH(); +        break; +    } +    UHD_LOG_INFO(unique_id(), "RX SW1 = " << rx_sw1); +    UHD_LOG_INFO(unique_id(), "RX SWC = " << rx_swc); +    UHD_LOG_INFO(unique_id(), "RX SWB = " << rx_swb); +    UHD_LOG_INFO(unique_id(), "RX VCRX_SW = " << vcrx_sw); +    UHD_LOG_INFO(unique_id(), "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/e31x_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp new file mode 100644 index 000000000..581a90c8e --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp @@ -0,0 +1,144 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_E31X_RADIO_CTRL_IMPL_HPP +#define INCLUDED_LIBUHD_RFNOC_E31X_RADIO_CTRL_IMPL_HPP + +#include "e3xx_constants.hpp" +#include "e3xx_radio_ctrl_impl.hpp" + +namespace uhd { +    namespace rfnoc { + +/*! \brief Provide access to an E31X radio. + */ +class e31x_radio_ctrl_impl : public e3xx_radio_ctrl_impl +{ +public: +    /************************************************************************ +     * Structors +     ***********************************************************************/ +    e31x_radio_ctrl_impl( +        const make_args_t &make_args +    ); +    virtual ~e31x_radio_ctrl_impl(); + +protected: + +    /************************************************************************** +     * ATR/ Switches Types +     *************************************************************************/ + +    enum tx_sw1_t { +        TX_SW1_LB_80 = 7, +        TX_SW1_LB_160 = 6, +        TX_SW1_LB_225 = 5, +        TX_SW1_LB_400 = 4, +        TX_SW1_LB_575 = 3, +        TX_SW1_LB_1000 = 2, +        TX_SW1_LB_1700 = 1, +        TX_SW1_LB_2750 = 0, +        TX_SW1_HB_5850 = 7 +    }; + +    enum vctxrx_sw_t { +        VCTXRX_SW_TX_HB = 3, +        VCTXRX1_SW_TX_LB = 1, +        VCTXRX1_SW_RX = 2, +        VCTXRX2_SW_TX_LB = 2, +        VCTXRX2_SW_RX = 1, +        VCTXRX_SW_OFF = 0, +    }; + +    enum rx_sw1_t { +        RX_SW1_LB_B2 = 4, +        RX_SW1_LB_B3 = 2, +        RX_SW1_LB_B4 = 0, +        RX_SW1_LB_B5 = 1, +        RX_SW1_LB_B6 = 3, +        RX_SW1_LB_B7 = 5, +        RX_SW1_OFF = 7 +    }; + +    enum rx_swc_t { +        RX_SWC_LB_B2 = 2, +        RX_SWC_LB_B3 = 3, +        RX_SWC_LB_B4 = 1, +        RX_SWC_OFF = 0 +    }; + +    enum rx_swb_t { +        RX_SWB_LB_B5 = 2, +        RX_SWB_LB_B6 = 3, +        RX_SWB_LB_B7 = 1, +        RX_SWB_OFF = 0 +    }; + +    enum vcrx_sw_t { +        VCRX_SW_LB = 1, +        VCRX_SW_HB = 2, +        VCRX_SW_OFF = 0 //or 3 +    }; + +    // (TX_ENABLEB, TX_ENABLEA) +    enum tx_bias_t { +        TX1_BIAS_HB_ON = 1, +        TX1_BIAS_LB_ON = 2, +        TX2_BIAS_HB_ON = 1, +        TX2_BIAS_LB_ON = 2, +        TX_BIAS_OFF = 0 +    }; + +    /************************************************************************ +     * 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, +        const std::string &ant +    ); + +    uint32_t get_tx_switches( +        const size_t chan, +        const double freq +    ); + +    uint32_t get_idle_switches(); + +    uint32_t get_tx_led(); +    uint32_t get_rx_led(); +    uint32_t get_txrx_led(); +    uint32_t get_idle_led(); +}; /* class radio_ctrl_impl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_E31X_RADIO_CTRL_IMPL_HPP */ +// vim: sw=4 et: diff --git a/host/lib/usrp/dboard/e3xx/e31x_regs.hpp b/host/lib/usrp/dboard/e3xx/e31x_regs.hpp new file mode 100644 index 000000000..15ce90743 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e31x_regs.hpp @@ -0,0 +1,26 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_E31X_REGS_HPP +#define INCLUDED_E31X_REGS_HPP + +#include <uhd/config.hpp> +#include <cstdint> + +static const uint32_t VCRX_SW_SHIFT = 14; +static const uint32_t VCTXRX_SW_SHIFT  = 12; +static const uint32_t TX_BIAS_SHIFT  = 10; +static const uint32_t RX_SWC_SHIFT  = 8; +static const uint32_t RX_SWB_SHIFT  = 6; +static const uint32_t RX_SW1_SHIFT  = 3; +static const uint32_t TX_SW1_SHIFT  = 0; + +static const uint32_t LED_RX_RX_SHIFT  = 2; +static const uint32_t LED_TXRX_TX_SHIFT  = 1; +static const uint32_t LED_TXRX_RX_SHIFT  = 0; + +#endif /* INCLUDED_E31X_REGS_HPP */ + diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp new file mode 100644 index 000000000..c48cabc9c --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp @@ -0,0 +1,272 @@ +// +// 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/e320_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp new file mode 100644 index 000000000..7f75cadc7 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp @@ -0,0 +1,134 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_E320_RADIO_CTRL_IMPL_HPP +#    define INCLUDED_LIBUHD_RFNOC_E320_RADIO_CTRL_IMPL_HPP + +#    include "e3xx_constants.hpp" +#    include "e3xx_radio_ctrl_impl.hpp" + +namespace uhd { namespace rfnoc { + +/*! \brief Provide access to an E320 radio. + */ +class e320_radio_ctrl_impl : public e3xx_radio_ctrl_impl +{ +public: +    /************************************************************************ +     * Structors +     ***********************************************************************/ +    e320_radio_ctrl_impl(const make_args_t& make_args); +    virtual ~e320_radio_ctrl_impl(); + +protected: +    /************************************************************************** +     * ATR/ Switches Types +     *************************************************************************/ + +    enum tx_sw1_t { +        TX_SW1_LB_80   = 3, +        TX_SW1_LB_160  = 7, +        TX_SW1_LB_225  = 1, +        TX_SW1_LB_400  = 5, +        TX_SW1_LB_575  = 2, +        TX_SW1_LB_1000 = 6, +        TX_SW1_LB_1700 = 0, +        TX_SW1_LB_2750 = 4 +    }; + +    enum tx_sw2_t { +        TX_SW2_LB_80   = 7, +        TX_SW2_LB_160  = 3, +        TX_SW2_LB_225  = 5, +        TX_SW2_LB_400  = 1, +        TX_SW2_LB_575  = 6, +        TX_SW2_LB_1000 = 2, +        TX_SW2_LB_1700 = 4, +        TX_SW2_LB_2750 = 0 +    }; + +    enum trx_sw_t { +        TRX1_SW_TX_HB = 2, +        TRX1_SW_TX_LB = 1, +        TRX1_SW_RX    = 4, +        TRX2_SW_TX_HB = 2, +        TRX2_SW_TX_LB = 4, +        TRX2_SW_RX    = 1 +    }; + +    enum rx_sw1_t { +        RX_SW1_LB_B2 = 4, +        RX_SW1_LB_B3 = 5, +        RX_SW1_LB_B4 = 2, +        RX_SW1_LB_B5 = 3, +        RX_SW1_LB_B6 = 0, +        RX_SW1_LB_B7 = 1, +        RX_SW1_OFF   = 7 + +    }; + +    enum rx_sw2_t { +        RX_SW2_LB_B2 = 5, +        RX_SW2_LB_B3 = 4, +        RX_SW2_LB_B4 = 3, +        RX_SW2_LB_B5 = 2, +        RX_SW2_LB_B6 = 1, +        RX_SW2_LB_B7 = 0, +        RX_SW2_OFF   = 7 +    }; + +    enum rx_sw3_t { +        RX_SW3_HBRX_LBTRX = 1, +        RX_SW3_HBTRX_LBRX = 2, +        RX_SW3_OFF        = 0 // or 3 +    }; + +    enum tx_amp_t { TX_AMP_HF_ON = 2, TX_AMP_LF_ON = 1, TX_AMP_OFF = 3 }; + +    /************************************************************************ +     * 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); + +    uint32_t get_tx_switches(const size_t chan, const double freq); + +    uint32_t get_idle_switches(); + +    uint32_t get_tx_led(); +    uint32_t get_rx_led(); +    uint32_t get_txrx_led(); +    uint32_t get_idle_led(); +}; /* class radio_ctrl_impl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_E320_RADIO_CTRL_IMPL_HPP */ +// vim: sw=4 et: diff --git a/host/lib/usrp/dboard/neon/neon_regs.hpp b/host/lib/usrp/dboard/e3xx/e320_regs.hpp index d1ff940c6..f5d28a63e 100644 --- a/host/lib/usrp/dboard/neon/neon_regs.hpp +++ b/host/lib/usrp/dboard/e3xx/e320_regs.hpp @@ -4,8 +4,8 @@  // SPDX-License-Identifier: GPL-3.0-or-later  // -#ifndef INCLUDED_NEON_REGS_HPP -#define INCLUDED_NEON_REGS_HPP +#ifndef INCLUDED_E320_REGS_HPP +#define INCLUDED_E320_REGS_HPP  #include <uhd/config.hpp>  #include <cstdint> @@ -22,4 +22,4 @@ static const uint32_t TRX_LED_GRN_SHIFT = 0;  static const uint32_t TX_LED_RED_SHIFT  = 1;  static const uint32_t RX_LED_GRN_SHIFT  = 2; -#endif /* INCLUDED_NEON_REGS_HPP */ +#endif /* INCLUDED_E320_REGS_HPP */ diff --git a/host/lib/usrp/dboard/neon/neon_ad9361_iface.cpp b/host/lib/usrp/dboard/e3xx/e3xx_ad9361_iface.cpp index a9cf28fc2..d9cadc86f 100644 --- a/host/lib/usrp/dboard/neon/neon_ad9361_iface.cpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_ad9361_iface.cpp @@ -4,17 +4,17 @@  // SPDX-License-Identifier: GPL-3.0-or-later  // -#include "neon_ad9361_iface.hpp" +#include "e3xx_ad9361_iface.hpp"  #include <uhd/exception.hpp>  #include <uhd/utils/log.hpp>  #include <boost/format.hpp>  using namespace uhd; -class neon_ad9361_iface : public ad9361_ctrl +class e3xx_ad9361_iface : public ad9361_ctrl  {  public: -    neon_ad9361_iface(rpc_client::sptr rpcc) +    e3xx_ad9361_iface(rpc_client::sptr rpcc)          : _rpcc(rpcc), _rpc_prefix("db_0_"), _log_prefix("AD9361")      {          UHD_LOG_TRACE( @@ -46,7 +46,7 @@ public:      double set_clock_rate(const double rate)      {          return _rpcc->request_with_token<double>( -            this->_rpc_prefix + "set_clock_rate", rate); +            E3XX_RATE_TIMEOUT, this->_rpc_prefix + "set_catalina_clock_rate", rate);      }      void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) @@ -58,7 +58,7 @@ public:      double tune(const std::string& which, const double value)      {          return _rpcc->request_with_token<double>( -            this->_rpc_prefix + "tune", which, value); +            E3XX_TUNE_TIMEOUT, this->_rpc_prefix + "catalina_tune", which, value);      }      void set_dc_offset_auto(const std::string& which, const bool on) @@ -139,17 +139,18 @@ private:      const std::string _log_prefix;  }; -//! Factory function for Neon's AD9361 RPC Controller +//! Factory function for E3xx's AD9361 RPC Controller  ad9361_ctrl::sptr make_rpc(rpc_client::sptr rpcc)  { -    return ad9361_ctrl::sptr(new neon_ad9361_iface(rpcc)); +    return ad9361_ctrl::sptr(new e3xx_ad9361_iface(rpcc));  }  /*! Helper function to convert direction and channel to the 'which' required by most     Catalina driver functions */ -std::string get_which_ad9361_chain(const direction_t dir, const size_t chan) +std::string get_which_ad9361_chain(const direction_t dir, const size_t chan, const bool fe_swap)  {      UHD_ASSERT_THROW(dir == RX_DIRECTION or dir == TX_DIRECTION);      UHD_ASSERT_THROW(chan == 0 or chan == 1); -    return str(boost::format("%s%d") % (dir == RX_DIRECTION ? "RX" : "TX") % (chan + 1)); +    size_t ad9361_chan = fe_swap ? (chan ? 0 : 1) : chan; +    return str(boost::format("%s%d") % (dir == RX_DIRECTION ? "RX" : "TX") % (ad9361_chan + 1));  } diff --git a/host/lib/usrp/dboard/e3xx/e3xx_ad9361_iface.hpp b/host/lib/usrp/dboard/e3xx/e3xx_ad9361_iface.hpp new file mode 100644 index 000000000..129015420 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e3xx_ad9361_iface.hpp @@ -0,0 +1,28 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_AD9361_IFACE_HPP +#define INCLUDED_LIBUHD_RFNOC_E3XX_AD9361_IFACE_HPP + +#    include <uhd/types/direction.hpp> +#    include <uhd/types/filters.hpp> +#    include <uhd/types/sensors.hpp> +#    include <uhdlib/usrp/common/ad9361_ctrl.hpp> +#    include <uhdlib/utils/rpc.hpp> +#    include <memory> +#    include <string> +#    include <vector> + +using namespace uhd; +using namespace uhd::usrp; + +static constexpr size_t E3XX_TUNE_TIMEOUT = 60000; +static constexpr size_t E3XX_RATE_TIMEOUT = 60000; +ad9361_ctrl::sptr make_rpc(rpc_client::sptr rpcc); +std::string get_which_ad9361_chain(const direction_t dir, const size_t chan, const bool fe_swap=false); + +#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_AD9361_IFACE_HPP */ +// vim: sw=4 et: diff --git a/host/lib/usrp/dboard/neon/neon_bands.cpp b/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp index 303d2801d..001cf5d1b 100644 --- a/host/lib/usrp/dboard/neon/neon_bands.cpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp @@ -4,12 +4,12 @@  // SPDX-License-Identifier: GPL-3.0-or-later  // -#include "neon_constants.hpp" -#include "neon_radio_ctrl_impl.hpp" +#include "e3xx_constants.hpp" +#include "e3xx_radio_ctrl_impl.hpp"  #include <uhd/utils/math.hpp>  /* - * Neon frequency bands: + * E320 frequency bands:   *   * For RX:   *     Band   Freq      BSEL-210 BSEL-543         TX/RX                RX2 @@ -41,7 +41,41 @@   *     LB_1000 723.8-1154.9   RF4 110  RF3 010   RF1 001   RF3 100   *     LB_1700 1154.9-1842.6  RF1 000  RF2 100   RF1 001   RF3 100   *     LB_2750 1842.6-2940.0  RF2 100  RF1 000   RF1 001   RF3 100 - *     HB_5850 > 2490.0       --- ---  --- ---   RF2 010   RF2 010 + *     HB_5850 > 2940.0       --- ---  --- ---   RF2 010   RF2 010 + * + */ + +/* + * E31x frequency bands: + * + * For RX: + *     Band   Freq      RX_BSEL-210 RXC_BSEL-10 RXB_BSEL-10   RX2            TX/RX + *                                                          VCRX_V1_V2    VCTXRX_V1_V2 + *                                                                       chan 1 | chan 2 + *     --------------------------------------------------------------------------------- + *     LB_B2: < 450       RF5 100    J2 10       -- 00          01       J2 10    J1 01 + *     LB_B3: 450-700     RF3 010    J3 11       -- 00          01       J2 10    J1 01 + *     LB_B4: 700-1200    RF1 000    J1 01       -- 00          01       J2 10    J1 01 + *     LB_B5: 1200-1800   RF2 001    -- 00       J2 10          01       J2 10    J1 01 + *     LB_B6: 1800-2350   RF4 011    -- 00       J3 11          01       J2 10    J1 01 + *     LB_B7: 2350-2600   RF6 101    -- 00       J1 01          01       J2 10    J1 01 + *     HB:    2600+       --- 111    -- 00       -- 00          10       J2 10    J1 01 + * + * + * For TX: + *     Band-Freq              TX_BSEL-210           TX/RX +                                                VCTXRX_V1_V2 + *                                             chan 1 | chan 2 + *     ------------------------------------------------------------ + *     LB_80   < 117.7        RF8 111          J1 01    J2 10 + *     LB_160  117.7-178.2    RF7 110          J1 01    J2 10 + *     LB_225  178.2-284.3    RF6 101          J1 01    J2 10 + *     LB_400  284.3-453.7    RF5 100          J1 01    J2 10 + *     LB_575  453.7-723.8    RF4 011          J1 01    J2 10 + *     LB_1000 723.8-1154.9   RF3 010          J1 01    J2 10 + *     LB_1700 1154.9-1842.6  RF2 001          J1 01    J2 10 + *     LB_2750 1842.6-2940.0  RF1 000          J1 01    J2 10 + *     HB_5850 > 2940.0       RF8 111          J3 11    J3 11   *   */ @@ -58,19 +92,19 @@ namespace {   *   * The following constants define lower cutoff frequencies for each band.   * LB_B2 does not have a lower cutoff frequency, it is implied by - * AD9361_MIN_FREQ. NEON_RX_BAND1_MIN_FREQ is the cutover frequency + * AD9361_MIN_FREQ. E3XX_RX_BAND1_MIN_FREQ is the cutover frequency   * for switching from LB_B2 to LB_B3, and so on.   *   * Bands 1-6 have both high- and low-pass filters (effectively band   * passes). Frequencies need to be chosen to allow as much of the full   * bandwidth through unattenuated.   */ -constexpr double NEON_RX_LB_BAND3_MIN_FREQ = 450e6; -constexpr double NEON_RX_LB_BAND4_MIN_FREQ = 700e6; -constexpr double NEON_RX_LB_BAND5_MIN_FREQ = 1200e6; -constexpr double NEON_RX_LB_BAND6_MIN_FREQ = 1800e6; -constexpr double NEON_RX_LB_BAND7_MIN_FREQ = 2350e6; -constexpr double NEON_RX_HB_MIN_FREQ       = 2600e6; +constexpr double E3XX_RX_LB_BAND3_MIN_FREQ = 450e6; +constexpr double E3XX_RX_LB_BAND4_MIN_FREQ = 700e6; +constexpr double E3XX_RX_LB_BAND5_MIN_FREQ = 1200e6; +constexpr double E3XX_RX_LB_BAND6_MIN_FREQ = 1800e6; +constexpr double E3XX_RX_LB_BAND7_MIN_FREQ = 2350e6; +constexpr double E3XX_RX_HB_MIN_FREQ       = 2600e6;  /* Note on the TX filter bank:   * @@ -78,43 +112,42 @@ constexpr double NEON_RX_HB_MIN_FREQ       = 2600e6;   *   * The following constants define lower cutoff frequencies for each band.   * LB_80 does not have a lower cutoff frequency, it is implied by - * AD9361_MIN_FREQ. NEON_TX_LB_160_MIN_FREQ is the cutover frequency + * AD9361_MIN_FREQ. E3XX_TX_LB_160_MIN_FREQ is the cutover frequency   * for switching from LB_80 to LB_160, and so on.   * - * On current Neon revisions, all filters on the TX filter bank are + * All filters on the TX filter bank are   * low pass filters (no high pass filters).   * Frequencies need to be chosen to allow as much of the full bandwidth   * through unattenuated (so don't go all the way up to the cutoff frequency   * of that filter).   */ -constexpr double NEON_TX_LB_160_MIN_FREQ  = 117.7e6; -constexpr double NEON_TX_LB_225_MIN_FREQ  = 178.2e6; -constexpr double NEON_TX_LB_400_MIN_FREQ  = 284.3e6; -constexpr double NEON_TX_LB_575_MIN_FREQ  = 453.7e6; -constexpr double NEON_TX_LB_1000_MIN_FREQ = 723.8e6; -constexpr double NEON_TX_LB_1700_MIN_FREQ = 1154.9e6; -constexpr double NEON_TX_LB_2750_MIN_FREQ = 1842.6e6; -constexpr double NEON_TX_HB_MIN_FREQ      = 2940.0e6; +constexpr double E3XX_TX_LB_160_MIN_FREQ  = 117.7e6; +constexpr double E3XX_TX_LB_225_MIN_FREQ  = 178.2e6; +constexpr double E3XX_TX_LB_400_MIN_FREQ  = 284.3e6; +constexpr double E3XX_TX_LB_575_MIN_FREQ  = 453.7e6; +constexpr double E3XX_TX_LB_1000_MIN_FREQ = 723.8e6; +constexpr double E3XX_TX_LB_1700_MIN_FREQ = 1154.9e6; +constexpr double E3XX_TX_LB_2750_MIN_FREQ = 1842.6e6; +constexpr double E3XX_TX_HB_MIN_FREQ      = 2940.0e6;  } // namespace -neon_radio_ctrl_impl::rx_band neon_radio_ctrl_impl::_map_freq_to_rx_band( -    const double freq) +e3xx_radio_ctrl_impl::rx_band e3xx_radio_ctrl_impl::map_freq_to_rx_band(const double freq)  { -    neon_radio_ctrl_impl::rx_band band; +    e3xx_radio_ctrl_impl::rx_band band;      if (fp_compare_epsilon<double>(freq) < AD9361_RX_MIN_FREQ) {          band = rx_band::INVALID_BAND; -    } else if (fp_compare_epsilon<double>(freq) < NEON_RX_LB_BAND3_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_RX_LB_BAND3_MIN_FREQ) {          band = rx_band::LB_B2; -    } else if (fp_compare_epsilon<double>(freq) < NEON_RX_LB_BAND4_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_RX_LB_BAND4_MIN_FREQ) {          band = rx_band::LB_B3; -    } else if (fp_compare_epsilon<double>(freq) < NEON_RX_LB_BAND5_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_RX_LB_BAND5_MIN_FREQ) {          band = rx_band::LB_B4; -    } else if (fp_compare_epsilon<double>(freq) < NEON_RX_LB_BAND6_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_RX_LB_BAND6_MIN_FREQ) {          band = rx_band::LB_B5; -    } else if (fp_compare_epsilon<double>(freq) < NEON_RX_LB_BAND7_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_RX_LB_BAND7_MIN_FREQ) {          band = rx_band::LB_B6; -    } else if (fp_compare_epsilon<double>(freq) < NEON_RX_HB_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_RX_HB_MIN_FREQ) {          band = rx_band::LB_B7;      } else if (fp_compare_epsilon<double>(freq) <= AD9361_RX_MAX_FREQ) {          band = rx_band::HB; @@ -125,28 +158,27 @@ neon_radio_ctrl_impl::rx_band neon_radio_ctrl_impl::_map_freq_to_rx_band(      return band;  } -neon_radio_ctrl_impl::tx_band neon_radio_ctrl_impl::_map_freq_to_tx_band( -    const double freq) +e3xx_radio_ctrl_impl::tx_band e3xx_radio_ctrl_impl::map_freq_to_tx_band(const double freq)  { -    neon_radio_ctrl_impl::tx_band band; +    e3xx_radio_ctrl_impl::tx_band band;      if (fp_compare_epsilon<double>(freq) < AD9361_TX_MIN_FREQ) {          band = tx_band::INVALID_BAND; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_160_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_160_MIN_FREQ) {          band = tx_band::LB_80; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_225_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_225_MIN_FREQ) {          band = tx_band::LB_160; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_400_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_400_MIN_FREQ) {          band = tx_band::LB_225; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_575_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_575_MIN_FREQ) {          band = tx_band::LB_400; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_1000_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_1000_MIN_FREQ) {          band = tx_band::LB_575; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_1700_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_1700_MIN_FREQ) {          band = tx_band::LB_1000; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_LB_2750_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_LB_2750_MIN_FREQ) {          band = tx_band::LB_1700; -    } else if (fp_compare_epsilon<double>(freq) < NEON_TX_HB_MIN_FREQ) { +    } else if (fp_compare_epsilon<double>(freq) < E3XX_TX_HB_MIN_FREQ) {          band = tx_band::LB_2750;      } else if (fp_compare_epsilon<double>(freq) <= AD9361_TX_MAX_FREQ) {          band = tx_band::HB; diff --git a/host/lib/usrp/dboard/neon/neon_constants.hpp b/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp index 42cfb6579..53f64d837 100644 --- a/host/lib/usrp/dboard/neon/neon_constants.hpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp @@ -4,8 +4,8 @@  // SPDX-License-Identifier: GPL-3.0-or-later  // -#ifndef INCLUDED_LIBUHD_NEON_CONSTANTS_HPP -#define INCLUDED_LIBUHD_NEON_CONSTANTS_HPP +#ifndef INCLUDED_LIBUHD_E3XX_CONSTANTS_HPP +#define INCLUDED_LIBUHD_E3XX_CONSTANTS_HPP  #include <cstddef>  #include <string> @@ -25,7 +25,7 @@ static constexpr double AD9361_TX_MAX_FREQ = 6.0e9; // Hz  static constexpr double AD9361_RX_MIN_FREQ = 70.0e6; // Hz  static constexpr double AD9361_RX_MAX_FREQ = 6.0e9; // Hz -static constexpr double NEON_RADIO_RATE = 16e6; // Hz +static constexpr double E3XX_RADIO_RATE = 16e6; // Hz  static constexpr double AD9361_MIN_RX_GAIN  = 0.0; // dB  static constexpr double AD9361_MAX_RX_GAIN  = 76; // dB @@ -34,19 +34,23 @@ static constexpr double AD9361_MIN_TX_GAIN  = 0.0; // dB  static constexpr double AD9361_MAX_TX_GAIN  = 89.75; // dB  static constexpr double AD9361_TX_GAIN_STEP = 0.25; // dB +static constexpr bool E3XX_DEFAULT_AUTO_DC_OFFSET  = true; +static constexpr bool E3XX_DEFAULT_AUTO_IQ_BALANCE = true; +static constexpr bool E3XX_DEFAULT_AGC_ENABLE      = false; -static constexpr bool NEON_DEFAULT_AUTO_DC_OFFSET  = true; -static constexpr bool NEON_DEFAULT_AUTO_IQ_BALANCE = true; -static constexpr bool NEON_DEFAULT_AGC_ENABLE      = false; +static constexpr double E3XX_DEFAULT_GAIN       = 0.0; +static constexpr double E3XX_DEFAULT_FREQ       = 2.4e9; // Hz +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 constexpr double NEON_DEFAULT_GAIN       = 0.0; -static constexpr double NEON_DEFAULT_FREQ       = 2.4e9; // Hz -static constexpr double NEON_DEFAULT_BANDWIDTH  = 40e6; // Hz -static constexpr char NEON_DEFAULT_RX_ANTENNA[] = "RX2"; -static constexpr char NEON_DEFAULT_TX_ANTENNA[] = "TX/RX"; +static const std::vector<std::string> E3XX_RX_ANTENNAS = {"RX2", "TX/RX"}; -static const std::vector<std::string> NEON_RX_ANTENNAS = {"RX2", "TX/RX"}; +static constexpr size_t E3XX_NUM_CHANS = 2; -static constexpr size_t NEON_NUM_CHANS = 2; - -#endif /* INCLUDED_LIBUHD_NEON_CONSTANTS_HPP */ +static constexpr char TIMING_MODE_2R2T[] = "2R2T"; +static constexpr char TIMING_MODE_1R1T[] = "1R1T"; +static constexpr char MIMO[]             = "MIMO"; // 2R2T +static constexpr char SISO_TX1[]         = "SISO_TX1"; // 1R1T +static constexpr char SISO_TX0[]         = "SISO_TX0"; // 1R1T +#endif /* INCLUDED_LIBUHD_E3XX_CONSTANTS_HPP */ diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp new file mode 100644 index 000000000..989b73b82 --- /dev/null +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp @@ -0,0 +1,343 @@ +// +// 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/neon/neon_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp index 88d1eed73..41c2c4594 100644 --- a/host/lib/usrp/dboard/neon/neon_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp @@ -4,26 +4,26 @@  // SPDX-License-Identifier: GPL-3.0-or-later  // -#ifndef INCLUDED_LIBUHD_RFNOC_NEON_RADIO_CTRL_IMPL_HPP -#define INCLUDED_LIBUHD_RFNOC_NEON_RADIO_CTRL_IMPL_HPP - -#include "neon_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> +#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 Magnesium radio. +/*! \brief Provide access to an E3xx radio.   */ -class neon_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl +class e3xx_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl  {  public: -    typedef boost::shared_ptr<neon_radio_ctrl_impl> sptr; +    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 }; @@ -51,86 +51,53 @@ public:      //! Channel select:      enum chan_sel_t { CHAN1, CHAN2, BOTH }; -    enum tx_sw1_t { -        TX_SW1_LB_80   = 3, -        TX_SW1_LB_160  = 7, -        TX_SW1_LB_225  = 1, -        TX_SW1_LB_400  = 5, -        TX_SW1_LB_575  = 2, -        TX_SW1_LB_1000 = 6, -        TX_SW1_LB_1700 = 0, -        TX_SW1_LB_2750 = 4 -    }; - -    enum tx_sw2_t { -        TX_SW2_LB_80   = 7, -        TX_SW2_LB_160  = 3, -        TX_SW2_LB_225  = 5, -        TX_SW2_LB_400  = 1, -        TX_SW2_LB_575  = 6, -        TX_SW2_LB_1000 = 2, -        TX_SW2_LB_1700 = 4, -        TX_SW2_LB_2750 = 0 -    }; - -    enum trx_sw_t { -        TRX1_SW_TX_HB = 2, -        TRX1_SW_TX_LB = 1, -        TRX1_SW_RX    = 4, -        TRX2_SW_TX_HB = 2, -        TRX2_SW_TX_LB = 4, -        TRX2_SW_RX    = 1 -    }; - -    enum rx_sw1_t { -        RX_SW1_LB_B2 = 4, -        RX_SW1_LB_B3 = 5, -        RX_SW1_LB_B4 = 2, -        RX_SW1_LB_B5 = 3, -        RX_SW1_LB_B6 = 0, -        RX_SW1_LB_B7 = 1, -        RX_SW1_OFF   = 7 - -    }; - -    enum rx_sw2_t { -        RX_SW2_LB_B2 = 5, -        RX_SW2_LB_B3 = 4, -        RX_SW2_LB_B4 = 3, -        RX_SW2_LB_B5 = 2, -        RX_SW2_LB_B6 = 1, -        RX_SW2_LB_B7 = 0, -        RX_SW2_OFF   = 7 -    }; - -    enum rx_sw3_t { -        RX_SW3_HBRX_LBTRX = 1, -        RX_SW3_HBTRX_LBRX = 2, -        RX_SW3_OFF        = 0 // or 3 -    }; - -    enum tx_amp_t { TX_AMP_HF_ON = 2, TX_AMP_LF_ON = 1, TX_AMP_OFF = 3 }; -      /************************************************************************       * Structors       ***********************************************************************/ -    UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(neon_radio_ctrl) -    virtual ~neon_radio_ctrl_impl(); +    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 - -    virtual bool check_radio_config(); +    //! 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); -    //! 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); +    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.       * @@ -146,27 +113,27 @@ public:       *       * \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); +    virtual void loopback_self_test(std::function<void(uint32_t)> poker_functor, +        std::function<uint64_t()> peeker_functor) = 0; -    double set_rate(const double rate); +    virtual uint32_t get_rx_switches( +        const size_t chan, const double freq, const std::string& ant) = 0; -    void set_tx_antenna(const std::string& ant, const size_t chan); -    void set_rx_antenna(const std::string& ant, const size_t chan); +    virtual uint32_t get_tx_switches(const size_t chan, const double freq) = 0; -    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); +    virtual uint32_t get_idle_switches() = 0; -    // gain -    double set_tx_gain(const double gain, const size_t chan); -    double set_rx_gain(const double gain, const size_t chan); +    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; -    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); +    //! Reference to the AD9361 controls +    // e3xx_ad9361_iface::uptr _ad9361; +    ad9361_ctrl::sptr _ad9361; -    void set_rpc_client(uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args); +    //! Swap RFA and RFB for catalina +    bool _fe_swap;  private:      /************************************************************************** @@ -189,13 +156,6 @@ private:      void _init_mpm_sensors(const direction_t dir, const size_t chan_idx); -    //! 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); -      /*************************************************************************       * Sensors       *************************************************************************/ @@ -210,11 +170,6 @@ private:      //  and resume normal operation.      void _identify_with_leds(const int identify_duration); -    uint32_t _get_rx_switches( -        const size_t chan, const double freq, const std::string& ant); - -    uint32_t _get_tx_switches(const size_t chan, const double freq); -      void _set_atr_bits(const size_t chan);      /************************************************************************** @@ -238,10 +193,6 @@ private:      //! Reference to the SPI core      uhd::spi_iface::sptr _spi; -    //! Reference to the AD9361 controls -    // neon_ad9361_iface::uptr _ad9361; -    ad9361_ctrl::sptr _ad9361; -      //! ATR controls. These control the AD9361 gain      //  up/down bits.      //  Every radio channel gets its own ATR state register. @@ -260,4 +211,5 @@ private:  }} /* namespace uhd::rfnoc */ -#endif /* INCLUDED_LIBUHD_RFNOC_NEON_RADIO_CTRL_IMPL_HPP */ +#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */ +// vim: sw=4 et: diff --git a/host/lib/usrp/dboard/neon/neon_radio_ctrl_init.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp index 783f0c7b2..5b33b33e7 100644 --- a/host/lib/usrp/dboard/neon/neon_radio_ctrl_init.cpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp @@ -4,8 +4,8 @@  // SPDX-License-Identifier: GPL-3.0-or-later  // -#include "neon_constants.hpp" -#include "neon_radio_ctrl_impl.hpp" +#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> @@ -56,7 +56,7 @@ uint32_t _extract_port_number(      return radio_num * port_size + port_num;  } -void neon_radio_ctrl_impl::_init_defaults() +void e3xx_radio_ctrl_impl::_init_defaults()  {      UHD_LOG_TRACE(unique_id(), "Initializing defaults...");      const size_t num_rx_chans = get_output_ports().size(); @@ -66,17 +66,17 @@ void neon_radio_ctrl_impl::_init_defaults()          "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(NEON_DEFAULT_FREQ, chan); -        radio_ctrl_impl::set_rx_gain(NEON_DEFAULT_GAIN, chan); -        radio_ctrl_impl::set_rx_antenna(NEON_DEFAULT_RX_ANTENNA, chan); -        radio_ctrl_impl::set_rx_bandwidth(NEON_DEFAULT_BANDWIDTH, 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(NEON_DEFAULT_FREQ, chan); -        radio_ctrl_impl::set_tx_gain(NEON_DEFAULT_GAIN, chan); -        radio_ctrl_impl::set_tx_antenna(NEON_DEFAULT_TX_ANTENNA, chan); -        radio_ctrl_impl::set_tx_bandwidth(NEON_DEFAULT_BANDWIDTH, 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) **/ @@ -89,7 +89,7 @@ void neon_radio_ctrl_impl::_init_defaults()      _tree->access<int>(get_arg_path("spp") / "value").set(default_spp);  } -void neon_radio_ctrl_impl::_init_peripherals() +void e3xx_radio_ctrl_impl::_init_peripherals()  {      UHD_LOG_TRACE(unique_id(), "Initializing peripherals..."); @@ -115,7 +115,7 @@ void neon_radio_ctrl_impl::_init_peripherals()          _get_ctrl(0), regs::sr_addr(regs::FP_GPIO), regs::rb_addr(regs::RB_FP_GPIO));  } -void neon_radio_ctrl_impl::_init_frontend_subtree( +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; @@ -124,10 +124,10 @@ void neon_radio_ctrl_impl::_init_frontend_subtree(          "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("Neon"))); +    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("Neon"))); +    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") @@ -136,7 +136,7 @@ void neon_radio_ctrl_impl::_init_frontend_subtree(          })          .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); });      subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options") -        .set({NEON_DEFAULT_TX_ANTENNA}) +        .set({E3XX_DEFAULT_TX_ANTENNA})          .add_coerced_subscriber([](const std::vector<std::string>&) {              throw uhd::runtime_error("Attempting to update antenna options!");          }); @@ -147,7 +147,7 @@ void neon_radio_ctrl_impl::_init_frontend_subtree(          })          .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); });      subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options") -        .set(NEON_RX_ANTENNAS) +        .set(E3XX_RX_ANTENNAS)          .add_coerced_subscriber([](const std::vector<std::string>&) {              throw uhd::runtime_error("Attempting to update antenna options!");          }); @@ -260,10 +260,10 @@ void neon_radio_ctrl_impl::_init_frontend_subtree(          });  } -void neon_radio_ctrl_impl::_init_prop_tree() +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 < NEON_NUM_CHANS; chan_idx++) { +    for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) {          this->_init_frontend_subtree(_tree->subtree(fe_base), chan_idx);      } @@ -383,25 +383,25 @@ void neon_radio_ctrl_impl::_init_prop_tree()      }  } -void neon_radio_ctrl_impl::_init_codec() +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(NEON_DEFAULT_GAIN, chan); -        this->set_rx_frequency(NEON_DEFAULT_FREQ, chan); -        this->set_rx_antenna(NEON_DEFAULT_RX_ANTENNA, chan); -        this->set_rx_bandwidth(NEON_DEFAULT_BANDWIDTH, chan); -        _ad9361->set_dc_offset_auto(rx_fe, NEON_DEFAULT_AUTO_DC_OFFSET); -        _ad9361->set_iq_balance_auto(rx_fe, NEON_DEFAULT_AUTO_IQ_BALANCE); -        _ad9361->set_agc(rx_fe, NEON_DEFAULT_AGC_ENABLE); +        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(NEON_DEFAULT_GAIN, chan); -        this->set_tx_frequency(NEON_DEFAULT_FREQ, chan); -        this->set_tx_bandwidth(NEON_DEFAULT_BANDWIDTH, 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 neon_radio_ctrl_impl::_init_mpm_sensors(const direction_t dir, const size_t chan_idx) +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 diff --git a/host/lib/usrp/dboard/neon/CMakeLists.txt b/host/lib/usrp/dboard/neon/CMakeLists.txt deleted file mode 100644 index a2473bd0a..000000000 --- a/host/lib/usrp/dboard/neon/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright 2018 Ettus Research, a National Instruments Company -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -if(ENABLE_E320) -    list(APPEND NEON_SOURCES -        ${CMAKE_CURRENT_SOURCE_DIR}/neon_radio_ctrl_impl.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/neon_radio_ctrl_init.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/neon_ad9361_iface.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/neon_bands.cpp -    ) -    LIBUHD_APPEND_SOURCES(${NEON_SOURCES}) -endif(ENABLE_E320) diff --git a/host/lib/usrp/dboard/neon/neon_ad9361_iface.hpp b/host/lib/usrp/dboard/neon/neon_ad9361_iface.hpp deleted file mode 100644 index 1b7a48142..000000000 --- a/host/lib/usrp/dboard/neon/neon_ad9361_iface.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_LIBUHD_RFNOC_NEON_AD9361_IFACE_HPP -#define INCLUDED_LIBUHD_RFNOC_NEON_AD9361_IFACE_HPP - -#include <uhd/types/direction.hpp> -#include <uhd/types/filters.hpp> -#include <uhd/types/sensors.hpp> -#include <uhdlib/usrp/common/ad9361_ctrl.hpp> -#include <uhdlib/utils/rpc.hpp> -#include <memory> -#include <string> -#include <vector> - -using namespace uhd; -using namespace uhd::usrp; - -ad9361_ctrl::sptr make_rpc(rpc_client::sptr rpcc); -std::string get_which_ad9361_chain(const direction_t dir, const size_t chan); - -#endif /* INCLUDED_LIBUHD_RFNOC_NEON_AD9361_IFACE_HPP */ diff --git a/host/lib/usrp/dboard/neon/neon_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/neon/neon_radio_ctrl_impl.cpp deleted file mode 100644 index d8fabcee0..000000000 --- a/host/lib/usrp/dboard/neon/neon_radio_ctrl_impl.cpp +++ /dev/null @@ -1,563 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "neon_radio_ctrl_impl.hpp" -#include "neon_constants.hpp" -#include "neon_regs.hpp" -#include <uhd/exception.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 - *****************************************************************************/ -UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(neon_radio_ctrl) -{ -    UHD_LOG_TRACE(unique_id(), "Entering neon_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(); -} - -neon_radio_ctrl_impl::~neon_radio_ctrl_impl() -{ -    UHD_LOG_TRACE(unique_id(), "neon_radio_ctrl_impl::dtor() "); -} - - -/****************************************************************************** - * API Calls - *****************************************************************************/ - -bool neon_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(false, 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; -} - -void neon_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 -    _ad9361->set_active_chains(tx1, tx2, rx1, rx2); - -    const std::string TIMING_MODE_2R2T = "2R2T"; -    const std::string TIMING_MODE_1R1T = "1R1T"; -    const std::string MIMO             = "MIMO"; // 2R2T -    const std::string SISO_TX1         = "SISO_TX1"; // 1R1T -    const std::string SISO_TX0         = "SISO_TX0"; // 1R1T -    // 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 2R2T mode -        _ad9361->set_timing_mode(TIMING_MODE_2R2T); -        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 neon_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); -} - -/*  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 neon_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); -} - -double neon_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 neon_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 neon_radio_ctrl_impl::set_rx_antenna(const std::string& ant, const size_t chan) -{ -    UHD_ASSERT_THROW(chan <= NEON_NUM_CHANS); -    if (std::find(NEON_RX_ANTENNAS.begin(), NEON_RX_ANTENNAS.end(), ant) -        == NEON_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 neon_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), clipped_freq); -    radio_ctrl_impl::set_tx_frequency(coerced_freq, chan); -    // Front-end switching -    _set_atr_bits(chan); - -    return coerced_freq; -} - -double neon_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), clipped_freq); -    radio_ctrl_impl::set_rx_frequency(coerced_freq, chan); -    // Front-end switching -    _set_atr_bits(chan); - -    return coerced_freq; -} - -double neon_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), bandwidth); -    return radio_ctrl_impl::set_rx_bandwidth(clipped_bw, chan); -} - -double neon_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), bandwidth); -    return radio_ctrl_impl::set_tx_bandwidth(clipped_bw, chan); -} - -double neon_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), clip_gain); -    radio_ctrl_impl::set_tx_gain(clip_gain, chan); -    return clip_gain; -} - -double neon_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), clip_gain); -    radio_ctrl_impl::set_rx_gain(clip_gain, chan); -    return clip_gain; -} - -size_t neon_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 neon_radio_ctrl_impl::get_dboard_fe_from_chan( -    const size_t chan, const direction_t /* dir */ -) -{ -    return std::to_string(chan); -} - -void neon_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 < NEON_NUM_CHANS; chan_idx++) { -            _init_mpm_sensors(dir, chan_idx); -        } -    } -} - -bool neon_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 neon_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   = _get_rx_switches(chan, rx_freq, rx_ant); -    const uint32_t tx_regs   = _get_tx_switches(chan, tx_freq); -    const 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; - -    _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 int idle_led = 0; -    const bool is_txrx = rx_ant == "TX/RX"; -    const int rx_led   = 1 << TRX_LED_GRN_SHIFT; -    const int tx_led   = 1 << TX_LED_RED_SHIFT; -    const int txrx_led = 1 << RX_LED_GRN_SHIFT; - -    _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 neon_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)); -    } -} - -uint32_t neon_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 = _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 neon_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 = _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; -} - -UHD_RFNOC_BLOCK_REGISTER(neon_radio_ctrl, "NeonRadio"); | 
