diff options
author | Sugandha Gupta <sugandha.gupta@ettus.com> | 2019-01-25 11:34:47 -0800 |
---|---|---|
committer | Brent Stapleton <brent.stapleton@ettus.com> | 2019-05-01 15:17:23 -0700 |
commit | 178b35569b1a25180a80a23b945b10b04c9f10f5 (patch) | |
tree | 3adb6f78ebd148867a50526c60fe7bf9694a4a72 /host/lib/usrp/dboard | |
parent | 8a400f6a30942c9d6d3596f6989720eb4cff058b (diff) | |
download | uhd-178b35569b1a25180a80a23b945b10b04c9f10f5.tar.gz uhd-178b35569b1a25180a80a23b945b10b04c9f10f5.tar.bz2 uhd-178b35569b1a25180a80a23b945b10b04c9f10f5.zip |
e310/e320: Move E310 to MPM architecture and refactor
- Turns the E310 into an MPM device (like N3xx, E320)
- Factor out common code between E320 and E310, maximize sharing between
the two devices
- Remove all pre-MPM E310 code that is no longer needed
- Modify MPM to remove all existing overlays before applying new ones
(this is necessary to enable idle image mode for E310)
Co-authored-by: Virendra Kakade <virendra.kakade@ni.com>
Signed-off-by: Virendra Kakade <virendra.kakade@ni.com>
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"); |