diff options
80 files changed, 3186 insertions, 6174 deletions
diff --git a/host/include/uhd/rfnoc/blocks/radio_e3xx.xml b/host/include/uhd/rfnoc/blocks/radio_e31x.xml index aece593a5..0635e397c 100644 --- a/host/include/uhd/rfnoc/blocks/radio_e3xx.xml +++ b/host/include/uhd/rfnoc/blocks/radio_e31x.xml @@ -1,11 +1,11 @@ <!--This defines one NoC-Block.--> <nocblock> - <name>Radio (E3XX)</name> + <name>Radio (E31X)</name> <blockname>Radio</blockname> - <key>E3XXRadio</key> + <key>E31XRadio</key> <!--There can be several of these:--> <ids> - <id revision="0">12AD100000000000</id> + <id revision="0">12AD100000003310</id> </ids> <!-- Registers --> <registers> diff --git a/host/include/uhd/rfnoc/blocks/radio_neon.xml b/host/include/uhd/rfnoc/blocks/radio_e320.xml index 01f286324..01f286324 100644 --- a/host/include/uhd/rfnoc/blocks/radio_neon.xml +++ b/host/include/uhd/rfnoc/blocks/radio_e320.xml diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 85e07c242..b171cbfef 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -69,7 +69,6 @@ LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBG # Devices LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) @@ -78,6 +77,7 @@ LIBUHD_REGISTER_COMPONENT("MPMD" ENABLE_MPMD ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("N300" ENABLE_N300 ON "ENABLE_LIBUHD;ENABLE_MPMD" OFF OFF) LIBUHD_REGISTER_COMPONENT("N320" ENABLE_N320 ON "ENABLE_LIBUHD;ENABLE_MPMD" OFF OFF) LIBUHD_REGISTER_COMPONENT("E320" ENABLE_E320 ON "ENABLE_LIBUHD;ENABLE_MPMD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 ON "ENABLE_LIBUHD;ENABLE_MPMD" OFF OFF) LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("DPDK" ENABLE_DPDK ON "ENABLE_MPMD;DPDK_FOUND" OFF OFF) diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 7f457d66a..6df37c2cf 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -46,7 +46,6 @@ INCLUDE_SUBDIRECTORY(mpmd) INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) INCLUDE_SUBDIRECTORY(b100) -INCLUDE_SUBDIRECTORY(e300) INCLUDE_SUBDIRECTORY(x300) INCLUDE_SUBDIRECTORY(b200) INCLUDE_SUBDIRECTORY(n230) diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index f0e10871e..e12519c36 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -1916,7 +1916,7 @@ double ad9361_device_t::set_clock_rate(const double req_rate) /* This function returns the RX / TX rate between AD9361 and the FPGA. */ -double ad9361_device_t::get_clock_rate() +double ad9361_device_t::get_clock_rate() const { return _baseband_bw; } diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 57f71193f..96a105438 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -162,7 +162,7 @@ public: void set_io_iface(ad9361_io::sptr io_iface); /* Get the current clock rate. */ - double get_clock_rate(); + double get_clock_rate() const; /* This function sets the RX / TX rate between AD9361 and the FPGA, and * thus determines the interpolation / decimation required in the FPGA to 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"); diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt deleted file mode 100644 index 9821fe34a..000000000 --- a/host/lib/usrp/e300/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright 2013-2015 Ettus Research LLC -# Copyright 2018 Ettus Research, a National Instruments Company -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -######################################################################## -# This file included, use CMake directory variables -######################################################################## - -######################################################################## -# Conditionally configure the USRP-E300 support -######################################################################## -find_package(UDev) - -if(ENABLE_E300) - list(APPEND E300_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_io_impl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_fifo_config.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_sysfs_hooks.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_network.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_global_regs.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_spi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_sensor_manager.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_impl.cpp - ) - LIBUHD_APPEND_SOURCES(${E300_SOURCES}) - if(UDEV_FOUND AND NOT E300_FORCE_NETWORK) - include_directories(${UDEV_INCLUDE_DIR}) - LIBUHD_APPEND_LIBS(${UDEV_LIBS}) - set_source_files_properties( - ${E300_SOURCES} - PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1" - ) - endif(UDEV_FOUND AND NOT E300_FORCE_NETWORK) - - if(ENABLE_GPSD) - set_source_files_properties( - ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_impl.cpp - PROPERTIES COMPILE_DEFINITIONS "E300_GPSD=1" - ) - endif(ENABLE_GPSD) -endif(ENABLE_E300) diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp deleted file mode 100644 index cd52bb9d0..000000000 --- a/host/lib/usrp/e300/e300_common.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright 2014-2015 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// -#include <uhd/image_loader.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/static.hpp> - -#include "e300_impl.hpp" -#include "e300_fifo_config.hpp" -#include "e300_fifo_config.hpp" - -#include "e300_common.hpp" - -#include <boost/filesystem.hpp> -#include <fstream> -#include <string> - -#ifdef E300_NATIVE -namespace uhd { namespace usrp { namespace e300 { - -namespace common { - -void load_fpga_image(const std::string &path) -{ - if (not boost::filesystem::exists("/dev/xdevcfg")) - ::system("mknod /dev/xdevcfg c 259 0"); - - UHD_LOGGER_INFO("E300") << "Loading FPGA image: " << path << "..."; - - std::ifstream fpga_file(path.c_str(), std::ios_base::binary); - UHD_ASSERT_THROW(fpga_file.good()); - - std::FILE *wfile; - wfile = std::fopen("/dev/xdevcfg", "wb"); - UHD_ASSERT_THROW(!(wfile == NULL)); - - char buff[16384]; // devcfg driver can't handle huge writes - do { - fpga_file.read(buff, sizeof(buff)); - std::fwrite(buff, 1, size_t(fpga_file.gcount()), wfile); - } while (fpga_file); - - fpga_file.close(); - std::fclose(wfile); - - UHD_LOGGER_INFO("E300") << "FPGA image loaded"; -} - -static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) { - // Make sure this is an E3x0 and we don't want to use anything connected - uhd::device_addrs_t devs = e300_find(image_loader_args.args); - if(devs.size() == 0 or !image_loader_args.load_fpga) return false; - - std::string fpga_filename, idle_image; // idle_image never used, just needed for function - if(image_loader_args.fpga_path == "") { - get_e3x0_fpga_images(devs[0], fpga_filename, idle_image); - } - else { - if(not boost::filesystem::exists(image_loader_args.fpga_path)) { - throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") - % image_loader_args.fpga_path)); - } - else fpga_filename = image_loader_args.fpga_path; - } - - load_fpga_image(fpga_filename); - return true; -} - -UHD_STATIC_BLOCK(register_e300_image_loader) { - std::string recovery_instructions = "The default FPGA image will be loaded the next " - "time UHD uses this device."; - - image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions); -} - -} - -}}} - -#else -namespace uhd { namespace usrp { namespace e300 { - -namespace common { - -void load_fpga_image(const std::string&) -{ - throw uhd::assertion_error("load_fpga_image() !E300_NATIVE"); -} - -} - -}}} -#endif diff --git a/host/lib/usrp/e300/e300_common.hpp b/host/lib/usrp/e300/e300_common.hpp deleted file mode 100644 index 8624f0e3c..000000000 --- a/host/lib/usrp/e300/e300_common.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_COMMON_HPP -#define INCLUDED_E300_COMMON_HPP - -namespace uhd { namespace usrp { namespace e300 { - -namespace common { - -void load_fpga_image(const std::string &path); - -}; - -}}} - -#endif // INCLUDED_E300_COMMON_HPP diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp deleted file mode 100644 index 97b0ddc3f..000000000 --- a/host/lib/usrp/e300/e300_defaults.hpp +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_DEFAULTS_HPP -#define INCLUDED_E300_DEFAULTS_HPP - -#include "ad9361_client.h" - -namespace uhd { namespace usrp { namespace e300 { - -static const double DEFAULT_TICK_RATE = 32e6; -static const double MIN_TICK_RATE = 10e6; - -static const double DEFAULT_TX_SAMP_RATE = 1.0e6; -static const double DEFAULT_RX_SAMP_RATE = 1.0e6; -static const double DEFAULT_DDC_FREQ = 0.0; -static const double DEFAULT_DUC_FREQ = 0.0; - -static const std::string DEFAULT_TIME_SRC = "internal"; -static const std::string DEFAULT_CLOCK_SRC = "internal"; - -static const size_t DEFAULT_RX_DATA_FRAME_SIZE = 4096; -static const size_t DEFAULT_RX_DATA_NUM_FRAMES = 32; - -static const size_t DEFAULT_TX_DATA_FRAME_SIZE = 4096; -static const size_t DEFAULT_TX_DATA_NUM_FRAMES = 32; - -static const size_t DEFAULT_CTRL_FRAME_SIZE = 64; -static const size_t DEFAULT_CTRL_NUM_FRAMES = 32; - -static const size_t MAX_NET_RX_DATA_FRAME_SIZE = 1200; -static const size_t MAX_NET_TX_DATA_FRAME_SIZE = 1200; - -static const size_t MAX_AXI_RX_DATA_FRAME_SIZE = 4096; -static const size_t MAX_AXI_TX_DATA_FRAME_SIZE = 4096; - -static const size_t MAX_DMA_CHANNEL_PAIRS = 16; - -static const double AD9361_SPI_RATE = 8e6; - -class e300_ad9361_client_t : public ad9361_params { -public: - ~e300_ad9361_client_t() {} - double get_band_edge(frequency_band_t band) { - switch (band) { - case AD9361_RX_BAND0: return 1.2e9; - case AD9361_RX_BAND1: return 2.6e9; - case AD9361_TX_BAND0: return 2940.0e6; - default: return 0; - } - } - clocking_mode_t get_clocking_mode() { - return clocking_mode_t::AD9361_XTAL_N_CLK_PATH; - } - digital_interface_mode_t get_digital_interface_mode() { - return AD9361_DDR_FDD_LVCMOS; - } - digital_interface_delays_t get_digital_interface_timing() { - digital_interface_delays_t delays; - delays.rx_clk_delay = 0; - delays.rx_data_delay = 0xF; - delays.tx_clk_delay = 0; - delays.tx_data_delay = 0xF; - return delays; - } -}; - -}}} // namespace - -#endif // INCLUDED_E300_DEFAULTS_HPP diff --git a/host/lib/usrp/e300/e300_eeprom_manager.cpp b/host/lib/usrp/e300/e300_eeprom_manager.cpp deleted file mode 100644 index 6547048ec..000000000 --- a/host/lib/usrp/e300/e300_eeprom_manager.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_eeprom_manager.hpp" -#include <uhd/types/mac_addr.hpp> -#include <uhd/utils/byteswap.hpp> - -namespace uhd { namespace usrp { namespace e300 { - -static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len) -{ - std::string out; - for (size_t i = 0; i < max_len; i++) { - if (bytes[i] < 32 or bytes[i] > 127) return out; - out += bytes[i]; - } - return out; -} - -static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer) -{ - byte_vector_t bytes; - const size_t len = std::min(string.size(), max_len); - for (size_t i = 0; i < len; i++){ - buffer[i] = string[i]; - } - if (len < max_len) - buffer[len] = '\0'; -} - -e300_eeprom_manager::e300_eeprom_manager(i2c::sptr i2c) : _i2c(i2c) -{ - read_mb_eeprom(); - read_db_eeprom(); -} - -e300_eeprom_manager::~e300_eeprom_manager(void) -{ -} - -const mboard_eeprom_t& e300_eeprom_manager::read_mb_eeprom(void) -{ - boost::mutex::scoped_lock(_mutex); - - std::vector<uint8_t> bytes; - bytes.resize(sizeof(mb_eeprom_map_t)); - mb_eeprom_map_t *map_ptr = reinterpret_cast<mb_eeprom_map_t*>(&bytes[0]); - memset(map_ptr, 0xff, sizeof(mb_eeprom_map_t)); - - // get the old contents - for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) - bytes[i] = _i2c->get_i2c_reg8(MB_ADDR, i); - - mb_eeprom_map_t &map = *map_ptr; - - _mb_eeprom["product"] = std::to_string( - uhd::ntohx<uint16_t>(map.hw_product)); - _mb_eeprom["revision"] = std::to_string( - uhd::ntohx<uint16_t>(map.hw_revision)); - _mb_eeprom["serial"] = _bytes_to_string( - map.serial, MB_SERIAL_LEN); - - byte_vector_t mac_addr(map.mac_addr, map.mac_addr + 6); - _mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(mac_addr).to_string(); - - _mb_eeprom["name"] = _bytes_to_string( - map.user_name, MB_NAME_LEN); - - return _mb_eeprom; -} - -const dboard_eeprom_t& e300_eeprom_manager::read_db_eeprom(void) -{ - boost::mutex::scoped_lock(_mutex); - - std::vector<uint8_t> bytes; - bytes.resize(sizeof(db_eeprom_map_t)); - db_eeprom_map_t *map_ptr = reinterpret_cast<db_eeprom_map_t*>(&bytes[0]); - memset(map_ptr, 0xff, sizeof(db_eeprom_map_t)); - - // get the old contents - for(size_t i = 0; i < sizeof(db_eeprom_map_t); i++) - bytes[i] = _i2c->get_i2c_reg16(DB_ADDR, i); - - db_eeprom_map_t &map = *map_ptr; - - _db_eeprom.id = uhd::usrp::dboard_id_t::from_uint16( - uhd::ntohx<uint16_t>(map.hw_product)); - - _db_eeprom.revision = std::to_string( - uhd::ntohx<uint16_t>(map.hw_revision)); - _db_eeprom.serial = _bytes_to_string( - map.serial, DB_SERIAL_LEN); - - return _db_eeprom; -} - -void e300_eeprom_manager::write_db_eeprom(const dboard_eeprom_t& eeprom) -{ - boost::mutex::scoped_lock(_mutex); - _db_eeprom = eeprom; - std::vector<uint8_t> bytes; - bytes.resize(sizeof(db_eeprom_map_t)); - - - db_eeprom_map_t *map_ptr = reinterpret_cast<db_eeprom_map_t*>(&bytes[0]); - memset(map_ptr, 0xff, sizeof(db_eeprom_map_t)); - - // get the old contents - for(size_t i = 0; i < sizeof(db_eeprom_map_t); i++) - bytes[i] = _i2c->get_i2c_reg16(DB_ADDR, i); - - db_eeprom_map_t &map = *map_ptr; - - // set the data version, that can be used to distinguish eeprom layouts - map.data_version_major = E310_DB_MAP_MAJOR; - map.data_version_minor = E310_DB_MAP_MINOR; - - if (_db_eeprom.id != dboard_id_t::none()) { - map.hw_product = uhd::htonx<uint16_t>( - _db_eeprom.id.to_uint16()); - } - - if (not _db_eeprom.revision.empty()) { - map.hw_revision = uhd::htonx<uint16_t>( - boost::lexical_cast<uint16_t>(_db_eeprom.revision)); - } - - if (not _db_eeprom.serial.empty()) { - _string_to_bytes(_db_eeprom.serial, DB_SERIAL_LEN, map.serial); - } - for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) - _i2c->set_i2c_reg16(DB_ADDR, i, bytes[i]); -} - -void e300_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom) -{ - boost::mutex::scoped_lock(_mutex); - _mb_eeprom = eeprom; - std::vector<uint8_t> bytes; - bytes.resize(sizeof(mb_eeprom_map_t)); - - - mb_eeprom_map_t *map_ptr = reinterpret_cast<mb_eeprom_map_t*>(&bytes[0]); - memset(map_ptr, 0xff, sizeof(mb_eeprom_map_t)); - - // get the old contents - for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) - bytes[i] = _i2c->get_i2c_reg8(MB_ADDR, i); - - mb_eeprom_map_t &map = *map_ptr; - - // set the data version, that can be used to distinguish eeprom layouts - map.data_version_major = E310_MB_MAP_MAJOR; - map.data_version_minor = E310_MB_MAP_MINOR; - - - if (_mb_eeprom.has_key("product")) { - map.hw_product = uhd::htonx<uint16_t>( - boost::lexical_cast<uint16_t>(_mb_eeprom["product"])); - } - if (_mb_eeprom.has_key("revision")) { - map.hw_revision = uhd::htonx<uint16_t>( - boost::lexical_cast<uint16_t>(_mb_eeprom["revision"])); - } - if (_mb_eeprom.has_key("serial")) { - _string_to_bytes(_mb_eeprom["serial"], MB_SERIAL_LEN, map.serial); - } - if (_mb_eeprom.has_key("mac-addr")) { - byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"]).to_bytes(); - std::copy(mac_addr.begin(), mac_addr.end(), map.mac_addr); - } - - //store the name - if (_mb_eeprom.has_key("name")) { - _string_to_bytes(_mb_eeprom["name"], MB_NAME_LEN, map.user_name); - } - - for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) - _i2c->set_i2c_reg8(MB_ADDR, i, bytes[i]); - -} - -e300_eeprom_manager::mboard_t e300_eeprom_manager::get_mb_type(void) const -{ - boost::mutex::scoped_lock(_mutex); - uint16_t pid = boost::lexical_cast<uint16_t>( - _mb_eeprom["product"]); - return get_mb_type(pid); -} - -e300_eeprom_manager::mboard_t e300_eeprom_manager::get_mb_type( - uint16_t pid) -{ - switch (pid) { - case E300_MB_PID: - return USRP_E300_MB; - - case E310_SG1_MB_PID: - return USRP_E310_SG1_MB; - - case E310_SG3_MB_PID: - return USRP_E310_SG3_MB; - - default: - return UNKNOWN; - }; -} - - -std::string e300_eeprom_manager::get_mb_type_string(void) const -{ - boost::mutex::scoped_lock(_mutex); - uint16_t product = boost::lexical_cast<uint16_t>( - _mb_eeprom["product"]); - switch (product) { - case E300_MB_PID: - return "E3XX"; - - case E310_SG1_MB_PID: - return "E3XX SG1"; - - case E310_SG3_MB_PID: - return "E3XX SG3"; - - default: - return "UNKNOWN"; - }; -} - -i2c::sptr e300_eeprom_manager::get_i2c_sptr(void) -{ - return _i2c; -} - - -}}} // namespace diff --git a/host/lib/usrp/e300/e300_eeprom_manager.hpp b/host/lib/usrp/e300/e300_eeprom_manager.hpp deleted file mode 100644 index ee4497211..000000000 --- a/host/lib/usrp/e300/e300_eeprom_manager.hpp +++ /dev/null @@ -1,122 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_EEPROM_MANAGER_HPP -#define INCLUDED_E300_EEPROM_MANAGER_HPP - -#include <boost/thread/mutex.hpp> -#include <boost/shared_ptr.hpp> -#include <uhd/types/dict.hpp> -#include <uhd/usrp/mboard_eeprom.hpp> -#include <uhd/usrp/dboard_eeprom.hpp> - -#include "e300_i2c.hpp" - -namespace uhd { namespace usrp { namespace e300 { - -static const uint16_t E300_MB_PID = 0x77d1; -static const uint16_t E310_SG1_MB_PID = 0x77d2; -static const uint16_t E310_SG3_MB_PID = 0x77d3; - -static const uint16_t E300_DB_PID = 0x0100; -static const uint16_t E310_DB_PID = 0x0110; - -static const uint16_t E310_MB_MAP_MAJOR = 0x0001; -static const uint16_t E310_MB_MAP_MINOR = 0x0000; - -static const uint16_t E310_DB_MAP_MAJOR = 0x0001; -static const uint16_t E310_DB_MAP_MINOR = 0x0000; - -class e300_eeprom_manager : uhd::noncopyable -{ -public: - typedef boost::shared_ptr<e300_eeprom_manager> sptr; - e300_eeprom_manager(i2c::sptr i2c); - ~e300_eeprom_manager(); - - // mboard - const mboard_eeprom_t& read_mb_eeprom(); - void write_mb_eeprom(const mboard_eeprom_t& eeprom); - - UHD_INLINE const mboard_eeprom_t& get_mb_eeprom() - { - return _mb_eeprom; - } - - // dboard - const dboard_eeprom_t& read_db_eeprom(); - void write_db_eeprom(const dboard_eeprom_t& eeprom); - - UHD_INLINE const dboard_eeprom_t& get_db_eeprom() - { - return _db_eeprom; - } - - - i2c::sptr get_i2c_sptr(void); - - enum mboard_t {USRP_E300_MB, USRP_E310_SG1_MB, USRP_E310_SG3_MB, UNKNOWN}; - - mboard_t get_mb_type(void) const; - static mboard_t get_mb_type(uint16_t pid); - std::string get_mb_type_string(void) const; - -private: // types - const static size_t MB_SERIAL_LEN = 8; - const static size_t MB_NAME_LEN = 32; - const static size_t MB_ADDR = 0x51; - - const static size_t DB_SERIAL_LEN = 8; - const static size_t DB_ADDR = 0x50; - - struct mb_eeprom_map_t - { - // Data format version - uint16_t data_version_major; - uint16_t data_version_minor; - - // NIC mac address - uint8_t mac_addr[6]; - - // HW identification info - uint16_t hw_product; - uint16_t hw_revision; - - // serial - uint8_t serial[MB_SERIAL_LEN]; - uint8_t pad[20 - MB_SERIAL_LEN]; - - //User specific - uint8_t user_name[MB_NAME_LEN]; - }; - - struct db_eeprom_map_t - { - // Data format version - uint16_t data_version_major; - uint16_t data_version_minor; - - // HW identification info - uint16_t hw_product; - uint16_t hw_revision; - - // serial - uint8_t serial[DB_SERIAL_LEN]; - uint8_t pad[20 - DB_SERIAL_LEN]; - }; - -private: // members - mboard_eeprom_t _mb_eeprom; - dboard_eeprom_t _db_eeprom; - i2c::sptr _i2c; - - boost::mutex _mutex; -}; - -}}} //namespace - -#endif // INCLUDED_E300_EEPROM_MANAGER_HPP diff --git a/host/lib/usrp/e300/e300_fifo_config.cpp b/host/lib/usrp/e300/e300_fifo_config.cpp deleted file mode 100644 index e34b56ed4..000000000 --- a/host/lib/usrp/e300/e300_fifo_config.cpp +++ /dev/null @@ -1,437 +0,0 @@ -// -// Copyright 2013-2017 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifdef E300_NATIVE - -#include <uhdlib/utils/system_time.hpp> -#include <uhd/config.hpp> -#include <stdint.h> -#include <atomic> -#include <chrono> -#include <thread> - -// constants coded into the fpga parameters -static const size_t ZF_CONFIG_BASE = 0x40000000; -static const size_t ZF_PAGE_WIDTH = 10; -static const size_t H2S_STREAMS_WIDTH = 4; -static const size_t H2S_CMDFIFO_DEPTH = 5; -static const size_t S2H_STREAMS_WIDTH = 4; -static const size_t S2H_CMDFIFO_DEPTH = 5; - -// calculate more useful constants for this module -static const size_t ZF_PAGE_SIZE(1 << ZF_PAGE_WIDTH); -static const size_t H2S_NUM_STREAMS(1 << H2S_STREAMS_WIDTH); -static const size_t H2S_NUM_CMDS(1 << H2S_CMDFIFO_DEPTH); -static const size_t S2H_NUM_STREAMS(1 << S2H_STREAMS_WIDTH); -static const size_t S2H_NUM_CMDS(1 << S2H_CMDFIFO_DEPTH); - -//offsetsinto the arbiter memory map -static const size_t ARBITER_WR_CLEAR = 0; -static const size_t ARBITER_RD_SIG = 0; -static const size_t ARBITER_WR_ADDR = 4; -static const size_t ARBITER_WR_SIZE = 8; -static const size_t ARBITER_WR_STS_RDY = 12; -static const size_t ARBITER_WR_STS = 16; -static const size_t ARBITER_RB_STATUS = 16; -static const size_t ARBITER_RB_STATUS_OCC = 20; -static const size_t ARBITER_RB_ADDR_SPACE = 24; -static const size_t ARBITER_RB_SIZE_SPACE = 28; - -// registers for the wb32_iface -static const size_t SR_CORE_READBACK = 0; - - -static UHD_INLINE size_t S2H_BASE(const size_t base) -{ - return base + ZF_PAGE_SIZE * 0; -} - -static UHD_INLINE size_t H2S_BASE(const size_t base) -{ - return base + ZF_PAGE_SIZE * 1; -} - -static UHD_INLINE size_t REG_BASE(const size_t base) -{ - return base + ZF_PAGE_SIZE * 2; -} - -static UHD_INLINE size_t DST_BASE(const size_t base) -{ - return base + ZF_PAGE_SIZE * 3; -} - -static UHD_INLINE size_t ZF_STREAM_OFF(const size_t which) -{ - return which * 32; -} - -#include "e300_fifo_config.hpp" -#include <sys/mman.h> //mmap -#include <fcntl.h> //open, close -#include <poll.h> //poll -#include <uhd/utils/log.hpp> - -#include <boost/format.hpp> -#include <boost/thread/thread.hpp> //sleep -#include <uhd/types/time_spec.hpp> //timeout -#include <uhd/utils/log.hpp> -#include <uhdlib/utils/atomic.hpp> - -//locking stuff for shared irq -#include <boost/thread/mutex.hpp> -#include <boost/thread/condition_variable.hpp> - -struct e300_fifo_poll_waiter -{ - e300_fifo_poll_waiter(const int fd): - _fd(fd), - _poll_claimed(false) - { - //NOP - } - - /*! - * Waits until the file descriptor fd has data to read. - * Access to the file descriptor is thread safe. - */ - void wait(const double timeout) - { - if (timeout == 0) { - return; - } - - boost::mutex::scoped_lock l(_mutex); - if (_poll_claimed) - { - _cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1000000))); - } - else - { - _poll_claimed = true; - l.unlock(); - struct pollfd fds[1]; - fds[0].fd = _fd; - fds[0].events = POLLIN; - ::poll(fds, 1, long(timeout*1000)); - if (fds[0].revents & POLLIN) - ::read(_fd, NULL, 0); - - l.lock(); - _poll_claimed = 0; - _cond.notify_all(); - } - } - - boost::condition_variable _cond; - boost::mutex _mutex; - int _fd; - bool _poll_claimed; -}; - -static const size_t DEFAULT_FRAME_SIZE = 2048; -static const size_t DEFAULT_NUM_FRAMES = 32; - -using namespace uhd; -using namespace uhd::transport; - -struct __mem_addrz_t -{ - size_t which, phys, data, ctrl; -}; - -/*********************************************************************** - * peek n' poke mmapped space - **********************************************************************/ -UHD_INLINE void zf_poke32(const uint32_t addr, const uint32_t data) -{ - volatile uint32_t *p = reinterpret_cast<uint32_t *>(addr); - *p = data; -} - -UHD_INLINE uint32_t zf_peek32(const uint32_t addr) -{ - volatile const uint32_t *p = reinterpret_cast<const uint32_t *>(addr); - return *p; -} - -/*********************************************************************** - * managed buffer - **********************************************************************/ -struct e300_fifo_mb : managed_buffer -{ - e300_fifo_mb(const __mem_addrz_t &addrs, const size_t len): - ctrl_base(addrs.ctrl), phys_mem(addrs.phys), mem((void *)addrs.data), len(len){} - - void release(void) - { - UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_ADDR_SPACE) > 0); - UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_SIZE_SPACE) > 0); - zf_poke32(ctrl_base + ARBITER_WR_ADDR, phys_mem); - zf_poke32(ctrl_base + ARBITER_WR_SIZE, this->size()); - } - - template <typename T> - UHD_INLINE typename T::sptr get_new(void) - { - return make(reinterpret_cast<T *>(this), mem, len); - } - - const size_t ctrl_base; - const size_t phys_mem; - void *const mem; - const size_t len; -}; - -/*********************************************************************** - * transport - **********************************************************************/ -class e300_transport : public zero_copy_if -{ -public: - e300_transport( - boost::shared_ptr<void> allocator, - const __mem_addrz_t &addrs, - const size_t num_frames, - const size_t frame_size, - e300_fifo_poll_waiter *waiter, - const bool auto_release - ): - _allocator(allocator), - _addrs(addrs), - _num_frames(num_frames), - _frame_size(frame_size), - _index(0), - _waiter(waiter) - { - //UHD_LOGGER_INFO("E300") << boost::format("phys 0x%x") % addrs.phys ; - //UHD_LOGGER_INFO("E300") << boost::format("data 0x%x") % addrs.data ; - //UHD_LOGGER_INFO("E300") << boost::format("ctrl 0x%x") % addrs.ctrl ; - - const uint32_t sig = zf_peek32(_addrs.ctrl + ARBITER_RD_SIG); - UHD_ASSERT_THROW((sig >> 16) == 0xACE0); - - zf_poke32(_addrs.ctrl + ARBITER_WR_CLEAR, 1); - for (size_t i = 0; i < num_frames; i++) - { - //create a managed buffer at the given offset - __mem_addrz_t mb_addrs = addrs; - mb_addrs.phys += (i*frame_size); - mb_addrs.data += (i*frame_size); - boost::shared_ptr<e300_fifo_mb> mb(new e300_fifo_mb(mb_addrs, frame_size)); - - //setup the buffers so they are "positioned for use" - const size_t sts_good = (1 << 7) | (_addrs.which & 0xf); - if (auto_release) mb->get_new<managed_recv_buffer>(); //release for read - else zf_poke32(_addrs.ctrl + ARBITER_WR_STS, sts_good); //poke an ok into the sts fifo - - _buffs.push_back(mb); - } - } - - ~e300_transport(void) - { - //NOP - } - - template <typename T> - UHD_INLINE typename T::sptr get_buff(const double timeout) - { - const time_spec_t exit_time = uhd::get_system_time() + time_spec_t(timeout); - while (1) - { - if (zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS_OCC)) - { - const uint32_t sts = zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS); - UHD_ASSERT_THROW((sts >> 7) & 0x1); //assert OK - UHD_ASSERT_THROW((sts & 0xf) == _addrs.which); //expected tag - zf_poke32(_addrs.ctrl + ARBITER_WR_STS_RDY, 1); //pop from sts fifo - if (_index == _num_frames) - _index = 0; - return _buffs[_index++]->get_new<T>(); - } - if (uhd::get_system_time() > exit_time) { - break; - } - _waiter->wait(timeout); - //std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - return typename T::sptr(); - } - - managed_recv_buffer::sptr get_recv_buff(const double timeout) - { - return this->get_buff<managed_recv_buffer>(timeout); - } - - size_t get_num_recv_frames(void) const - { - return _num_frames; - } - - size_t get_recv_frame_size(void) const - { - return _frame_size; - } - - managed_send_buffer::sptr get_send_buff(const double timeout) - { - return this->get_buff<managed_send_buffer>(timeout); - } - - size_t get_num_send_frames(void) const - { - return _num_frames; - } - - size_t get_send_frame_size(void) const - { - return _frame_size; - } - -private: - boost::shared_ptr<void> _allocator; - const __mem_addrz_t _addrs; - const size_t _num_frames; - const size_t _frame_size; - size_t _index; - e300_fifo_poll_waiter *_waiter; - std::vector<boost::shared_ptr<e300_fifo_mb> > _buffs; -}; - -/*********************************************************************** - * memory mapping - **********************************************************************/ -class e300_fifo_interface_impl : public virtual e300_fifo_interface -{ -public: - e300_fifo_interface_impl(const e300_fifo_config_t &config): - _config(config), - _bytes_in_use(0), - _recv_entries_in_use(std::vector<size_t>(S2H_NUM_STREAMS, 0)), - _send_entries_in_use(std::vector<size_t>(H2S_NUM_STREAMS, 0)) - { - //open the file descriptor to our kernel module - const std::string dev = "/dev/axi_fpga"; - _fd = ::open(dev.c_str(), O_RDWR|O_SYNC); - if (_fd < 0) - { - throw uhd::runtime_error("e300: failed to open " + dev); - } - - //mmap the control and data regions into virtual space - //UHD_VAR(_config.ctrl_length); - //UHD_VAR(_config.buff_length); - //UHD_VAR(_config.phys_addr); - _buff = ::mmap(NULL, _config.ctrl_length + _config.buff_length, PROT_READ|PROT_WRITE, MAP_SHARED, _fd, 0); - if (_buff == MAP_FAILED) - { - ::close(_fd); - throw uhd::runtime_error("e300: failed to mmap " + dev); - } - - //segment the memory according to zynq fifo arbiter - _ctrl_space = size_t(_buff); - _data_space = size_t(_buff) + _config.ctrl_length; - - //zero out the data region - std::memset((void *)_data_space, 0, _config.buff_length); - - //create a poll _waiter for the transports - _waiter = new e300_fifo_poll_waiter(_fd); - } - - virtual ~e300_fifo_interface_impl(void) - { - delete _waiter; - UHD_LOGGER_TRACE("E300")<< "cleanup: munmap" ; - ::munmap(_buff, _config.ctrl_length + _config.buff_length); - ::close(_fd); - } - - uhd::transport::zero_copy_if::sptr make_recv_xport( - const size_t which_stream, - const uhd::transport::zero_copy_xport_params ¶ms) - { - return this->_make_xport(which_stream, params, true); - } - - uhd::transport::zero_copy_if::sptr make_send_xport( - const size_t which_stream, - const uhd::transport::zero_copy_xport_params ¶ms) - { - return this->_make_xport(which_stream, params, false); - } - - size_t get_global_regs_base() const - { - return REG_BASE(_ctrl_space); - } - -private: - uhd::transport::zero_copy_if::sptr _make_xport( - const size_t which_stream, - const uhd::transport::zero_copy_xport_params ¶ms, - const bool is_recv) - { - boost::mutex::scoped_lock lock(_setup_mutex); - - const size_t frame_size = is_recv ? params.recv_frame_size : params.send_frame_size; - const size_t num_frames = is_recv ? params.num_recv_frames : params.num_send_frames; - size_t &entries_in_use = (is_recv)? _recv_entries_in_use.at(which_stream) - : _send_entries_in_use.at(which_stream); - - __mem_addrz_t addrs; - addrs.which = which_stream; - addrs.phys = _config.phys_addr + _bytes_in_use; - addrs.data = _data_space + _bytes_in_use; - addrs.ctrl = ((is_recv)? S2H_BASE(_ctrl_space) : H2S_BASE(_ctrl_space)) + ZF_STREAM_OFF(which_stream); - - uhd::transport::zero_copy_if::sptr xport; - if (is_recv) xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv)); - else xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv)); - - _bytes_in_use += num_frames*frame_size; - entries_in_use += num_frames; - - UHD_ASSERT_THROW(_recv_entries_in_use.at(which_stream) <= S2H_NUM_CMDS); - UHD_ASSERT_THROW(_send_entries_in_use.at(which_stream) <= H2S_NUM_CMDS); - UHD_ASSERT_THROW(_bytes_in_use <= _config.buff_length); - - - return xport; - } - - e300_fifo_config_t _config; - e300_fifo_poll_waiter *_waiter; - size_t _bytes_in_use; - int _fd; - void *_buff; - size_t _ctrl_space; - size_t _data_space; - std::vector<size_t> _recv_entries_in_use; - std::vector<size_t> _send_entries_in_use; - boost::mutex _setup_mutex; -}; - -e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &config) -{ - return e300_fifo_interface::sptr(new e300_fifo_interface_impl(config)); -} - -#else //E300_NATIVE - -#include "e300_fifo_config.hpp" -#include <uhd/exception.hpp> - -e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &) -{ - throw uhd::assertion_error("e300_fifo_interface::make() !E300_NATIVE"); -} - -#endif //E300_NATIVE diff --git a/host/lib/usrp/e300/e300_fifo_config.hpp b/host/lib/usrp/e300/e300_fifo_config.hpp deleted file mode 100644 index fa6b00eec..000000000 --- a/host/lib/usrp/e300/e300_fifo_config.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_FIFO_CONFIG_HPP -#define INCLUDED_E300_FIFO_CONFIG_HPP - -#include <uhd/types/device_addr.hpp> -#include <uhd/types/wb_iface.hpp> -#include <uhd/transport/zero_copy.hpp> -#include <boost/enable_shared_from_this.hpp> - -struct e300_fifo_config_t -{ - size_t ctrl_length; - size_t buff_length; - size_t phys_addr; -}; - -e300_fifo_config_t e300_read_sysfs(void); -std::string e300_get_sysfs_attr(const std::string &node, const std::string &attr); - -struct e300_fifo_interface : boost::enable_shared_from_this<e300_fifo_interface> -{ - typedef boost::shared_ptr<e300_fifo_interface> sptr; - static sptr make(const e300_fifo_config_t &config); - - virtual uhd::transport::zero_copy_if::sptr make_recv_xport( - const size_t which_stream, - const uhd::transport::zero_copy_xport_params ¶ms) = 0; - - virtual uhd::transport::zero_copy_if::sptr make_send_xport( - const size_t which_stream, - const uhd::transport::zero_copy_xport_params &parms) = 0; - - virtual size_t get_global_regs_base(void) const = 0; -}; - -#endif /* INCLUDED_E300_FIFO_CONFIG_HPP */ diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp deleted file mode 100644 index 517aa4653..000000000 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_FPGA_DEFS_HPP -#define INCLUDED_E300_FPGA_DEFS_HPP -namespace uhd { namespace usrp { namespace e300 { namespace fpga { - -static const size_t NUM_RADIOS = 2; - -static const uint32_t COMPAT_MAJOR = 255; -static const uint32_t COMPAT_MINOR = 0; - -}}}} // namespace - -#endif // INCLUDED_E300_FPGA_DEFS_HPP diff --git a/host/lib/usrp/e300/e300_global_regs.cpp b/host/lib/usrp/e300/e300_global_regs.cpp deleted file mode 100644 index ef607797a..000000000 --- a/host/lib/usrp/e300/e300_global_regs.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_global_regs.hpp" - -#include <stdint.h> -#include <uhd/exception.hpp> -#include <uhd/utils/byteswap.hpp> -#include <cstring> -#include <iostream> - -namespace uhd { namespace usrp { namespace e300 { - -class global_regs_local_impl : public global_regs -{ -public: - global_regs_local_impl(const size_t ctrl_base) : _ctrl_base(ctrl_base) - { - } - - virtual ~global_regs_local_impl(void) - { - } - - uint32_t peek32(const uhd::wb_iface::wb_addr_type addr) - { - // setup readback register - _poke32(_ctrl_base + global_regs::SR_CORE_READBACK, addr); - return _peek32(_ctrl_base); - } - - void poke32(const uhd::wb_iface::wb_addr_type addr, const uint32_t data) - { - _poke32(_ctrl_base + static_cast<size_t>(addr), data); - } - - -private: - const size_t _ctrl_base; - - UHD_INLINE void _poke32(const uint32_t addr, const uint32_t data) - { - volatile uint32_t *p = reinterpret_cast<uint32_t *>(addr); - *p = data; - } - - UHD_INLINE uint32_t _peek32(const uint32_t addr) - { - volatile const uint32_t *p = reinterpret_cast<const uint32_t *>(addr); - return *p; - } -}; - -global_regs::sptr global_regs::make(const size_t ctrl_base) -{ - return sptr(new global_regs_local_impl(ctrl_base)); -} - -class global_regs_zc_impl : public global_regs -{ -public: - global_regs_zc_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport) - { - } - - virtual ~global_regs_zc_impl(void) - { - } - - uint32_t peek32(const uhd::wb_iface::wb_addr_type addr) - { - global_regs_transaction_t transaction; - transaction.is_poke = uhd::htonx<uint32_t>(0); - transaction.addr = uhd::htonx<uint32_t>( - static_cast<uint32_t>(addr)); - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw std::runtime_error("global_regs_zc_impl send timeout"); - std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw std::runtime_error("global_regs_zc_impl recv timeout"); - std::memcpy(&transaction, buff->cast<const void *>(), sizeof(transaction)); - } - return uhd::ntohx<uint32_t>(transaction.data); - } - - void poke32(const uhd::wb_iface::wb_addr_type addr, const uint32_t data) - { - global_regs_transaction_t transaction; - transaction.is_poke = uhd::htonx<uint32_t>(1); - transaction.addr = uhd::htonx<uint32_t>( - static_cast<uint32_t>(addr)); - transaction.data = uhd::htonx<uint32_t>(data); - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("global_regs_zc_impl send timeout"); - std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - } - -private: - uhd::transport::zero_copy_if::sptr _xport; -}; - -global_regs::sptr global_regs::make(uhd::transport::zero_copy_if::sptr xport) -{ - return sptr(new global_regs_zc_impl(xport)); -} - -}}}; diff --git a/host/lib/usrp/e300/e300_global_regs.hpp b/host/lib/usrp/e300/e300_global_regs.hpp deleted file mode 100644 index e7d668d78..000000000 --- a/host/lib/usrp/e300/e300_global_regs.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_GLOBAL_REGS_HPP -#define INCLUDED_E300_GLOBAL_REGS_HPP - -#include <uhd/types/wb_iface.hpp> -#include <uhd/transport/zero_copy.hpp> - -namespace uhd { namespace usrp { namespace e300 { - -struct global_regs_transaction_t { - global_regs_transaction_t(): is_poke(0), addr(0), data(0), pad(0) {} - uint32_t is_poke; - uint32_t addr; - uint32_t data; - uint32_t pad; -}; - -class global_regs : uhd::noncopyable, public virtual uhd::wb_iface -{ -public: - typedef boost::shared_ptr<global_regs> sptr; - - static sptr make(const size_t ctrl_base); - static sptr make(uhd::transport::zero_copy_if::sptr xport); - - static const size_t SR_CORE_READBACK = 0; - static const size_t SR_CORE_MISC = 4; - static const size_t SR_CORE_TEST = 28; - static const size_t SR_CORE_XB_LOCAL = 32; - static const size_t SR_CORE_SPI_SEL = 64; - - // leave some room for registers, - // xbar starts with an offset of one - // 1K page. A part of which is used for - // DST_LOOKUP for DST_LOOKUP - - static const size_t SR_CORE_DST = 1024; - static const size_t SR_CORE_XBAR = 2048; - - static const size_t RB32_CORE_MISC = 1; - static const size_t RB32_CORE_COMPAT = 2; - static const size_t RB32_CORE_GITHASH = 3; - static const size_t RB32_CORE_PLL = 4; - static const size_t RB32_CORE_NUM_CE = 8; - static const size_t RB32_CORE_TEST = 24; - - // PPS selection - static const size_t PPS_GPS = 0; - static const size_t PPS_INT = 2; - static const size_t PPS_EXT = 3; -}; - -UHD_INLINE uint32_t XB_ADDR(const uint32_t addr) -{ - return global_regs::SR_CORE_XBAR + (addr << 2); -} - -UHD_INLINE uint32_t DST_ADDR(const uint32_t addr) -{ - return global_regs::SR_CORE_DST + (addr << 2); -} - -}}}; - -#endif /* INCLUDED_E300_GLOBAL_REGS_HPP */ diff --git a/host/lib/usrp/e300/e300_i2c.cpp b/host/lib/usrp/e300/e300_i2c.cpp deleted file mode 100644 index 24fd429b5..000000000 --- a/host/lib/usrp/e300/e300_i2c.cpp +++ /dev/null @@ -1,400 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_i2c.hpp" -#include <uhd/exception.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/transport/udp_simple.hpp> - -#include <cstring> -#include <chrono> -#include <thread> - -namespace uhd { namespace usrp { namespace e300 { - -class zc_impl : public i2c -{ -public: - zc_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport) - { - } - - virtual ~zc_impl(void) - { - } - - void set_i2c_reg8( - const uint8_t addr, - const uint8_t reg, - const uint8_t value) - { - i2c_transaction_t transaction; - transaction.type = WRITE | ONEBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - transaction.data = value; - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("i2c_zc_impl send timeout"); - std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - } - - uint8_t get_i2c_reg8( - const uint8_t addr, - const uint8_t reg) - { - i2c_transaction_t transaction; - transaction.type = READ | ONEBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw std::runtime_error("i2c_zc_impl send timeout"); - std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw std::runtime_error("i2c_zc_impl recv timeout"); - std::memcpy(&transaction, buff->cast<const void *>(), sizeof(transaction)); - } - return transaction.data; - } - - void set_i2c_reg16( - const uint8_t addr, - const uint16_t reg, - const uint8_t value) - { - i2c_transaction_t transaction; - transaction.type = WRITE | TWOBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - transaction.data = value; - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("i2c_zc_impl send timeout"); - std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - } - - uint8_t get_i2c_reg16( - const uint8_t addr, - const uint16_t reg) - { - i2c_transaction_t transaction; - transaction.type = READ | TWOBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw std::runtime_error("i2c_zc_impl send timeout"); - std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); - if (not buff or buff->size() < sizeof(transaction)) - throw std::runtime_error("i2c_zc_impl recv timeout"); - std::memcpy(&transaction, buff->cast<const void *>(), sizeof(transaction)); - } - return transaction.data; - } - - -private: - uhd::transport::zero_copy_if::sptr _xport; -}; - -i2c::sptr i2c::make_zc(uhd::transport::zero_copy_if::sptr xport) -{ - return sptr(new zc_impl(xport)); -} - -class simple_udp_impl : public i2c -{ -public: - simple_udp_impl(const std::string &ip_addr, const std::string &port) - { - _xport = uhd::transport::udp_simple::make_connected(ip_addr, port); - } - - virtual ~simple_udp_impl(void) - { - } - - void set_i2c_reg8( - const uint8_t addr, - const uint8_t reg, - const uint8_t value) - { - i2c_transaction_t transaction; - transaction.type = i2c::WRITE | ONEBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - transaction.data = value; - - _xport->send( - boost::asio::buffer( - &transaction, - sizeof(transaction))); - } - - uint8_t get_i2c_reg8( - const uint8_t addr, - const uint8_t reg) - { - i2c_transaction_t transaction; - transaction.type = i2c::READ | ONEBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - transaction.data = 0; - - _xport->send( - boost::asio::buffer( - &transaction, - sizeof(transaction))); - - uint8_t buff[sizeof(i2c_transaction_t)] = {}; - const size_t nbytes = _xport->recv( - boost::asio::buffer(buff), 0.100); - if (not (nbytes == sizeof(transaction))) - throw std::runtime_error("i2c_simple_udp_impl recv timeout"); - i2c_transaction_t *reply = reinterpret_cast<i2c_transaction_t*>(buff); - return reply->data; - } - - void set_i2c_reg16( - const uint8_t addr, - const uint16_t reg, - const uint8_t value) - { - i2c_transaction_t transaction; - transaction.type = i2c::WRITE | TWOBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - transaction.data = value; - - _xport->send( - boost::asio::buffer( - &transaction, - sizeof(transaction))); - } - - uint8_t get_i2c_reg16( - const uint8_t addr, - const uint16_t reg) - { - i2c_transaction_t transaction; - transaction.type = i2c::READ | TWOBYTE; - transaction.addr = addr; - transaction.reg = uhd::htonx<uint16_t>(reg); - transaction.data = 0; - - _xport->send( - boost::asio::buffer( - &transaction, - sizeof(transaction))); - - uint8_t buff[sizeof(i2c_transaction_t)] = {}; - const size_t nbytes = _xport->recv( - boost::asio::buffer(buff), 0.100); - if (not (nbytes == sizeof(transaction))) - throw std::runtime_error("i2c_simple_udp_impl recv timeout"); - i2c_transaction_t *reply = reinterpret_cast<i2c_transaction_t*>(buff); - return reply->data; - } - -private: - uhd::transport::udp_simple::sptr _xport; -}; - -i2c::sptr i2c::make_simple_udp( - const std::string &ip_addr, - const std::string &port) -{ - return sptr(new simple_udp_impl(ip_addr,port)); -} - -}}} // namespace - -#ifdef E300_NATIVE - -#include <linux/i2c.h> -#include <linux/i2c-dev.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/ioctl.h> - -#include <boost/thread.hpp> -#include <stdint.h> - -namespace uhd { namespace usrp { namespace e300 { - -class i2cdev_impl : public i2c -{ -public: - i2cdev_impl(const std::string &device) - { - _fd = ::open(device.c_str(), O_RDWR); - if (_fd < 0) - throw uhd::system_error("open failed."); - } - - virtual ~i2cdev_impl(void) - { - close(_fd); - } - - void set_i2c_reg8( - const uint8_t addr, - const uint8_t reg, - const uint8_t value) - { - uint8_t outbuf[2]; - i2c_rdwr_ioctl_data packets; - i2c_msg messages[1]; - - messages[0].addr = addr; - messages[0].flags = 0; - messages[0].len = sizeof(outbuf); - messages[0].buf = outbuf; - - outbuf[0] = reg; - outbuf[1] = value; - - packets.msgs = messages; - packets.nmsgs = 1; - - if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { - throw std::runtime_error("ioctl failed"); - } - // this is ugly - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } - - uint8_t get_i2c_reg8( - const uint8_t addr, - const uint8_t reg) - { - i2c_rdwr_ioctl_data packets; - i2c_msg messages[2]; - - uint8_t outbuf = reg; - messages[0].addr = addr; - messages[0].flags = 0; - messages[0].len = sizeof(outbuf); - messages[0].buf = &outbuf; - - uint8_t inbuf; - messages[1].addr = addr; - messages[1].flags = I2C_M_RD; - messages[1].len = sizeof(inbuf); - messages[1].buf = &inbuf; - - packets.msgs = messages; - packets.nmsgs = 2; - - if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { - throw std::runtime_error("ioctl failed."); - } - - return inbuf; - } - - // the daughterboard uses 16 bit addresses - void set_i2c_reg16( - const uint8_t addr, - const uint16_t reg, - const uint8_t value) - { - uint8_t outbuf[3]; - i2c_rdwr_ioctl_data packets; - i2c_msg messages[1]; - - messages[0].addr = addr; - messages[0].flags = 0; - messages[0].len = sizeof(outbuf); - messages[0].buf = outbuf; - - outbuf[0] = (reg >> 8) & 0xff; - outbuf[1] = reg & 0xff; - outbuf[2] = value; - - packets.msgs = messages; - packets.nmsgs = 1; - - if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { - throw std::runtime_error("ioctl failed"); - } - // this is ugly - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } - - - // the daughterboard eeprom uses 16 bit addresses - uint8_t get_i2c_reg16( - const uint8_t addr, - const uint16_t reg) - { - i2c_rdwr_ioctl_data packets; - i2c_msg messages[2]; - - // always little endian - uint8_t outbuf[2]; - outbuf[0] = (reg >> 8) & 0xff; - outbuf[1] = reg & 0xff; - - messages[0].addr = addr; - messages[0].flags = 0; - messages[0].len = sizeof(outbuf); - messages[0].buf = outbuf; - - uint8_t inbuf; - messages[1].addr = addr; - messages[1].flags = I2C_M_RD; - messages[1].len = sizeof(inbuf); - messages[1].buf = &inbuf; - - packets.msgs = messages; - packets.nmsgs = 2; - - if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { - throw std::runtime_error("ioctl failed."); - } - - return inbuf; - } - -private: - int _fd; -}; - -}}} // namespace - -using namespace uhd::usrp::e300; - -i2c::sptr i2c::make_i2cdev(const std::string &device) -{ - return sptr(new i2cdev_impl(device)); -} -#else -using namespace uhd::usrp::e300; - -i2c::sptr i2c::make_i2cdev(const std::string &) -{ - throw uhd::assertion_error("i2c::make() !E300_NATIVE"); -} -#endif // E300_NATIVE diff --git a/host/lib/usrp/e300/e300_i2c.hpp b/host/lib/usrp/e300/e300_i2c.hpp deleted file mode 100644 index 1606d2146..000000000 --- a/host/lib/usrp/e300/e300_i2c.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_I2C_HPP -#define INCLUDED_E300_I2C_HPP - -#include <uhd/utils/noncopyable.hpp> -#include <stdint.h> -#include <boost/shared_ptr.hpp> - -#include <uhd/transport/zero_copy.hpp> - -namespace uhd { namespace usrp { namespace e300 { - -struct i2c_transaction_t { - i2c_transaction_t(): reg(0), addr(0), data(0), type(0) {}; - uint16_t reg; - uint8_t addr; - uint8_t data; - uint8_t type; -}; - -class i2c : public uhd::noncopyable -{ -public: - typedef boost::shared_ptr<i2c> sptr; - - static sptr make_i2cdev(const std::string &device); - static sptr make_zc(uhd::transport::zero_copy_if::sptr xport); - static sptr make_simple_udp( - const std::string &ip_addr, - const std::string &port); - - virtual uint8_t get_i2c_reg8( - const uint8_t addr, - const uint8_t reg) = 0; - - virtual uint8_t get_i2c_reg16( - const uint8_t addr, - const uint16_t reg) = 0; - - virtual void set_i2c_reg8( - const uint8_t addr, - const uint8_t reg, - const uint8_t value) = 0; - - virtual void set_i2c_reg16( - const uint8_t addr, - const uint16_t reg, - const uint8_t value) = 0; - - - static const uint8_t DB_EEPROM_ADDR = 0x50; - static const uint8_t MB_EEPROM_ADDR = 0x51; - - static const uint8_t WRITE = 0x1; - static const uint8_t READ = 0x0; - static const uint8_t TWOBYTE = 0x4; - static const uint8_t ONEBYTE = 0x2; -}; - -}}}; - -#endif // INCLUDED_E300_I2C_HPP diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp deleted file mode 100644 index 59a8cec88..000000000 --- a/host/lib/usrp/e300/e300_impl.cpp +++ /dev/null @@ -1,749 +0,0 @@ -// -// Copyright 2013-2015 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_impl.hpp" -#include "e300_defaults.hpp" -#include "e300_fpga_defs.hpp" -#include "e300_spi.hpp" -#include "e300_regs.hpp" -#include "e300_eeprom_manager.hpp" -#include "e300_sensor_manager.hpp" -#include "e300_common.hpp" -#include "e300_remote_codec_ctrl.hpp" -#include "e3xx_radio_ctrl_impl.hpp" - - -#include <uhd/utils/log.hpp> -#include <uhd/utils/static.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/usrp/dboard_eeprom.hpp> -#include <uhd/transport/if_addrs.hpp> -#include <uhd/transport/udp_zero_copy.hpp> -#include <uhd/transport/udp_simple.hpp> -#include <uhd/types/sensors.hpp> -#include <boost/make_shared.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/format.hpp> -#include <boost/filesystem.hpp> -#include <boost/functional/hash.hpp> -#include <boost/bind.hpp> -#include <boost/make_shared.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/thread/thread.hpp> //sleep -#include <boost/asio.hpp> -#include <fstream> - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::usrp::gpio_atr; -using namespace uhd::transport; -namespace fs = boost::filesystem; -namespace asio = boost::asio; - -namespace uhd { namespace usrp { namespace e300 { - -/*********************************************************************** - * Discovery - **********************************************************************/ - -static std::vector<std::string> discover_ip_addrs( - const std::string& addr_hint, const std::string& port) -{ - std::vector<std::string> addrs; - - // Create a UDP transport to communicate: - // Some devices will cause a throw when opened for a broadcast address. - // We print and recover so the caller can loop through all bcast addrs. - uhd::transport::udp_simple::sptr udp_bcast_xport; - try { - udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port); - } catch(const std::exception &e) { - UHD_LOGGER_ERROR("E300") << boost::format("Cannot open UDP transport on %s for discovery%s") - % addr_hint % e.what() ; - return addrs; - } catch(...) { - UHD_LOGGER_ERROR("E300") << "E300 Network discovery unknown error"; - return addrs; - } - - // TODO: Do not abuse the I2C transport here ... - // we send a read request to i2c address 0x51, - // to read register 0 - i2c_transaction_t req; - req.type = i2c::READ | i2c::ONEBYTE; - req.addr = 0x51; // mboard's eeprom address, we don't really care - req.reg = 4; - - // send dummy request - try { - udp_bcast_xport->send(boost::asio::buffer(&req, sizeof(req))); - } catch (const std::exception &ex) { - UHD_LOGGER_ERROR("E300") << "E300 Network discovery error " << ex.what(); - return addrs; - } catch(...) { - UHD_LOGGER_ERROR("E300") << "E300 Network discovery unknown error"; - return addrs; - } - - // loop for replies until timeout - while (true) { - uint8_t buff[sizeof(i2c_transaction_t)] = {}; - const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050); - if (nbytes == 0) - break; //No more responses - - const i2c_transaction_t *reply = reinterpret_cast<const i2c_transaction_t*>(buff); - if (req.addr == reply->addr) - addrs.push_back(udp_bcast_xport->get_recv_addr()); - } - - return addrs; -} - -static bool is_loopback(const if_addrs_t &if_addrs) -{ - return if_addrs.inet == asio::ip::address_v4::loopback().to_string(); -} - -device_addrs_t e300_find(const device_addr_t &multi_dev_hint) -{ - // handle multi device discovery - device_addrs_t hints = separate_device_addr(multi_dev_hint); - - if (hints.size() > 1) { - device_addrs_t found_devices; - std::string err_msg; - BOOST_FOREACH(const device_addr_t &hint_i, hints) - { - device_addrs_t found_devices_i = e300_find(hint_i); - if(found_devices_i.size() != 1) - err_msg += str(boost::format( - "Could not resolve device hint \"%s\" to a single device.") - % hint_i.to_string()); - else - found_devices.push_back(found_devices_i[0]); - if (found_devices.empty()) - return device_addrs_t(); - - if (not err_msg.empty()) - throw uhd::value_error(err_msg); - } - return device_addrs_t(1, combine_device_addrs(found_devices)); - } - - // initialize the hint for a single device case - UHD_ASSERT_THROW(hints.size() <= 1); - hints.resize(1); // in case it was empty - device_addr_t hint = hints[0]; - device_addrs_t e300_addrs; - - // return an empty list of addresses when type is set to non-e300 - if (hint.has_key("type") and hint["type"] != "e3x0") - return e300_addrs; - - const bool loopback_only = - get_if_addrs().size() == 1 and is_loopback(get_if_addrs().at(0)); - - // if we don't have connectivity, we might as well skip the network part - if (not loopback_only) { - // if no address or node has been specified, send a broadcast - if ((not hint.has_key("addr")) and (not hint.has_key("node"))) { - BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) - { - // avoid the loopback device - if (is_loopback(if_addrs)) - continue; - - // create a new hint with this broadcast address - device_addr_t new_hint = hint; - new_hint["addr"] = if_addrs.bcast; - - // call discover with the new hint and append results - device_addrs_t new_e300_addrs = e300_find(new_hint); - e300_addrs.insert(e300_addrs.begin(), - new_e300_addrs.begin(), new_e300_addrs.end()); - - } - return e300_addrs; - } - - std::vector<std::string> ip_addrs = discover_ip_addrs( - hint["addr"], E300_SERVER_I2C_PORT); - - BOOST_FOREACH(const std::string &ip_addr, ip_addrs) - { - device_addr_t new_addr; - new_addr["type"] = "e3x0"; - new_addr["addr"] = ip_addr; - - // see if we can read the eeprom - try { - e300_eeprom_manager eeprom_manager( - i2c::make_simple_udp(new_addr["addr"], E300_SERVER_I2C_PORT)); - const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom(); - new_addr["name"] = eeprom["name"]; - new_addr["serial"] = eeprom["serial"]; - new_addr["product"] = eeprom_manager.get_mb_type_string(); - } catch (...) { - // set these values as empty string, so the device may still be found - // and the filters below can still operate on the discovered device - new_addr["name"] = ""; - new_addr["serial"] = ""; - } - // filter the discovered device below by matching optional keys - if ((not hint.has_key("name") or hint["name"] == new_addr["name"]) and - (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])) - { - e300_addrs.push_back(new_addr); - } - } - } - - // finally search locally - // if device node is not provided, - // use the default one - if (not hint.has_key("node")) { - device_addr_t new_addr = hint; - new_addr["node"] = "/dev/axi_fpga"; - return e300_find(new_addr); - } - - // use the given node - if (fs::exists(hint["node"])) { - device_addr_t new_addr; - new_addr["type"] = "e3x0"; - new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string(); - - try { - e300_eeprom_manager eeprom_manager(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); - const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom(); - new_addr["name"] = eeprom["name"]; - new_addr["serial"] = eeprom["serial"]; - new_addr["product"] = eeprom_manager.get_mb_type_string(); - } catch (...) { - // set these values as empty string, so the device may still be found - // and the filters below can still operate on the discovered device - new_addr["name"] = ""; - new_addr["serial"] = ""; - } - // filter the discovered device below by matching optional keys - if ((not hint.has_key("name") or hint["name"] == new_addr["name"]) and - (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])) - { - e300_addrs.push_back(new_addr); - } - } - - return e300_addrs; -} - - -/*********************************************************************** - * Make - **********************************************************************/ -static device::sptr e300_make(const device_addr_t &device_addr) -{ - UHD_LOGGER_DEBUG("E300")<< "e300_make with args " << device_addr.to_pp_string() ; - if(device_addr.has_key("server")) - throw uhd::runtime_error( - str(boost::format("Please run the server executable \"%s\"") - % "usrp_e3x0_network_mode")); - else - return device::sptr(new e300_impl(device_addr)); -} - -// Common code used by e300_impl and e300_image_loader -void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr, - std::string &fpga_image, - std::string &idle_image){ - const uint16_t pid = boost::lexical_cast<uint16_t>( - device_addr["product"]); - - //extract the FPGA path for the e300 - switch(e300_eeprom_manager::get_mb_type(pid)) { - case e300_eeprom_manager::USRP_E310_SG1_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E310_SG1_FPGA_FILE_NAME)); - idle_image = find_image_path(E3XX_SG1_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::USRP_E310_SG3_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E310_SG3_FPGA_FILE_NAME)); - idle_image = find_image_path(E3XX_SG3_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::USRP_E300_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E300_FPGA_FILE_NAME)); - idle_image = find_image_path(E3XX_SG1_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::UNKNOWN: - default: - UHD_LOGGER_WARNING("E300") << "Unknown motherboard type, loading e300 image." - ; - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E300_FPGA_FILE_NAME)); - idle_image = find_image_path(E3XX_SG1_FPGA_IDLE_FILE_NAME); - break; - } -} - -/*********************************************************************** - * Structors - **********************************************************************/ -e300_impl::e300_impl(const uhd::device_addr_t &device_addr) - : _device_addr(device_addr) - , _xport_path(device_addr.has_key("addr") ? ETH : AXI) - , _dma_chans_available(MAX_DMA_CHANNEL_PAIRS, ~size_t(0) /* all available at the beginning */) -{ - stream_options.rx_fc_request_freq = E300_RX_FC_REQUEST_FREQ; - - //////////////////////////////////////////////////////////////////// - // load the fpga image - //////////////////////////////////////////////////////////////////// - if (_xport_path == AXI) { - _do_not_reload = device_addr.has_key("no_reload_fpga"); - if (not _do_not_reload) { - std::string fpga_image; - - // need to re-read product ID code because of conversion into string in find function - e300_eeprom_manager eeprom_manager(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); - const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom(); - device_addr_t device_addr_cp(device_addr.to_string()); - device_addr_cp["product"] = eeprom["product"]; - - get_e3x0_fpga_images(device_addr_cp, - fpga_image, - _idle_image); - common::load_fpga_image(fpga_image); - } - } - - //////////////////////////////////////////////////////////////////// - // setup fifo xports - //////////////////////////////////////////////////////////////////// - _ctrl_xport_params.recv_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; - _ctrl_xport_params.num_recv_frames = e300::DEFAULT_CTRL_NUM_FRAMES; - _ctrl_xport_params.send_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; - _ctrl_xport_params.num_send_frames = e300::DEFAULT_CTRL_NUM_FRAMES; - - _data_xport_params.recv_frame_size = device_addr.cast<size_t>("recv_frame_size", - e300::DEFAULT_RX_DATA_FRAME_SIZE); - _data_xport_params.num_recv_frames = device_addr.cast<size_t>("num_recv_frames", - e300::DEFAULT_RX_DATA_NUM_FRAMES); - _data_xport_params.send_frame_size = device_addr.cast<size_t>("send_frame_size", - e300::DEFAULT_TX_DATA_FRAME_SIZE); - _data_xport_params.num_send_frames = device_addr.cast<size_t>("num_send_frames", - e300::DEFAULT_TX_DATA_NUM_FRAMES); - - - // until we figure out why this goes wrong we'll keep this hack around for - // the ethernet case, in the AXI case we cannot go above one page - if (_xport_path == ETH) { - _data_xport_params.recv_frame_size = - std::min(e300::MAX_NET_RX_DATA_FRAME_SIZE, _data_xport_params.recv_frame_size); - _data_xport_params.send_frame_size = - std::min(e300::MAX_NET_TX_DATA_FRAME_SIZE, _data_xport_params.send_frame_size); - } else { - _data_xport_params.recv_frame_size = - std::min(e300::MAX_AXI_RX_DATA_FRAME_SIZE, _data_xport_params.recv_frame_size); - _data_xport_params.send_frame_size = - std::min(e300::MAX_AXI_TX_DATA_FRAME_SIZE, _data_xport_params.send_frame_size); - } - udp_zero_copy::buff_params dummy_buff_params_out; - - ad9361_ctrl::sptr codec_ctrl; - if (_xport_path == ETH) { - zero_copy_if::sptr codec_xport = - udp_zero_copy::make(device_addr["addr"], E300_SERVER_CODEC_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr); - codec_ctrl = e300_remote_codec_ctrl::make(codec_xport); - zero_copy_if::sptr gregs_xport = - udp_zero_copy::make(device_addr["addr"], E300_SERVER_GREGS_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr); - _global_regs = global_regs::make(gregs_xport); - - zero_copy_if::sptr i2c_xport; - i2c_xport = udp_zero_copy::make(device_addr["addr"], E300_SERVER_I2C_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr); - _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_zc(i2c_xport)); - - uhd::transport::zero_copy_xport_params sensor_xport_params; - sensor_xport_params.recv_frame_size = 128; - sensor_xport_params.num_recv_frames = 10; - sensor_xport_params.send_frame_size = 128; - sensor_xport_params.num_send_frames = 10; - - zero_copy_if::sptr sensors_xport; - sensors_xport = udp_zero_copy::make(device_addr["addr"], E300_SERVER_SENSOR_PORT, sensor_xport_params, dummy_buff_params_out, device_addr); - _sensor_manager = e300_sensor_manager::make_proxy(sensors_xport); - - } else { - e300_fifo_config_t fifo_cfg; - try { - fifo_cfg = e300_read_sysfs(); - } catch (...) { - throw uhd::runtime_error("Failed to get driver parameters from sysfs."); - } - _fifo_iface = e300_fifo_interface::make(fifo_cfg); - _global_regs = global_regs::make(_fifo_iface->get_global_regs_base()); - - ad9361_params::sptr client_settings = boost::make_shared<e300_ad9361_client_t>(); - codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1); - // This is horrible ... why do I have to sleep here? - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); - _sensor_manager = e300_sensor_manager::make_local(_global_regs); - } - -#ifdef E300_GPSD - UHD_LOGGER_INFO("E300") << "Detecting internal GPS "; - try { - if (_xport_path == AXI) - _gps = gpsd_iface::make("localhost", 2947); - else - _gps = gpsd_iface::make(device_addr["addr"], 2947); - } catch (std::exception &e) { - UHD_LOGGER_ERROR("E300") << "An error occured making GPSDd interface: " << e.what(); - } - - if (_gps) { - for (size_t i = 0; i < _GPS_TIMEOUT; i++) - { - boost::this_thread::sleep(boost::posix_time::seconds(1)); - if (!_gps->gps_detected()) - std::cout << "." << std::flush; - else { - std::cout << ".... " << std::flush; - break; - } - } - UHD_LOGGER_INFO("E300") << "GPSDO " << (_gps->gps_detected() ? "found" : "not found"); - } -#endif - - // Verify we can talk to the e300 core control registers ... - UHD_LOGGER_INFO("E300") << "Initializing core control (global registers)..." << std::endl; - this->_register_loopback_self_test( - _global_regs, - global_regs::SR_CORE_TEST, - global_regs::RB32_CORE_TEST - ); - - // Verify fpga compatibility version matches at least for the major - if (_get_version(FPGA_MAJOR) != fpga::COMPAT_MAJOR) { - throw uhd::runtime_error(str(boost::format( - "Expected FPGA compatibility number %lu.x, but got %lu.%lu:\n" - "The FPGA build is not compatible with the host code build.\n" - "%s" - ) % fpga::COMPAT_MAJOR - % _get_version(FPGA_MAJOR) % _get_version(FPGA_MINOR) - % print_utility_error("uhd_images_downloader.py"))); - } - - //////////////////////////////////////////////////////////////////// - // Initialize the properties tree - //////////////////////////////////////////////////////////////////// - _tree->create<std::string>("/name").set("E-Series Device"); - const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name") - .set(_eeprom_manager->get_mb_type_string()); - - _tree->create<std::string>(mb_path / "codename").set("Troll"); - - _tree->create<std::string>(mb_path / "fpga_version").set( - str(boost::format("%u.%u") - % _get_version(FPGA_MAJOR) - % _get_version(FPGA_MINOR))); - - _tree->create<std::string>(mb_path / "fpga_version_hash").set( - _get_version_hash()); - - // Clock reference source - _tree->create<std::string>(mb_path / "clock_source" / "value") - .add_coerced_subscriber(boost::bind(&e300_impl::_update_clock_source, this, _1)) - .set(e300::DEFAULT_CLOCK_SRC); - static const std::vector<std::string> clock_sources = - boost::assign::list_of("internal"); //external,gpsdo not supported - _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources); - - //////////////////////////////////////////////////////////////////// - // and do the misc mboard sensors - //////////////////////////////////////////////////////////////////// - _tree->create<int>(mb_path / "sensors"); - BOOST_FOREACH(const std::string &name, _sensor_manager->get_sensors()) - { - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .set_publisher(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name)); - } -#ifdef E300_GPSD - if (_gps) { - BOOST_FOREACH(const std::string &name, _gps->get_sensors()) - { - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .set_publisher(boost::bind(&gpsd_iface::get_sensor, _gps, name)); - } - } -#endif - - //////////////////////////////////////////////////////////////////// - // setup the mboard eeprom - //////////////////////////////////////////////////////////////////// - _tree->create<mboard_eeprom_t>(mb_path / "eeprom") - .set(_eeprom_manager->get_mb_eeprom()) // set first... - .add_coerced_subscriber(boost::bind( - &e300_eeprom_manager::write_mb_eeprom, - _eeprom_manager, _1)); - - //////////////////////////////////////////////////////////////////// - // dboard eeproms but not really - //////////////////////////////////////////////////////////////////// - dboard_eeprom_t db_eeprom; - _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom") - .set(_eeprom_manager->get_db_eeprom()) - .add_coerced_subscriber(boost::bind( - &e300_eeprom_manager::write_db_eeprom, - _eeprom_manager, _1)); - - _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom") - .set(_eeprom_manager->get_db_eeprom()) - .add_coerced_subscriber(boost::bind( - &e300_eeprom_manager::write_db_eeprom, - _eeprom_manager, _1)); - - _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom); - - //////////////////////////////////////////////////////////////////// - // Access to global regs - //////////////////////////////////////////////////////////////////// - _tree->create<uint32_t>(mb_path / "global_regs" / "misc") - .add_coerced_subscriber(boost::bind(&global_regs::poke32, _global_regs, global_regs::SR_CORE_MISC, _1)) - ; - _tree->create<uint32_t>(mb_path / "global_regs" / "pll") - .set_publisher(boost::bind(&global_regs::peek32, _global_regs, global_regs::RB32_CORE_PLL)) - ; - - //////////////////////////////////////////////////////////////////// - // clocking - //////////////////////////////////////////////////////////////////// - _tree->create<double>(mb_path / "tick_rate") - .add_coerced_subscriber(boost::bind(&device3_impl::update_tx_streamers, this, _1)) - .add_coerced_subscriber(boost::bind(&device3_impl::update_rx_streamers, this, _1)) - ; - - //default some chains on -- needed for setup purposes - UHD_LOGGER_DEBUG("E300") << "Initializing AD9361 using hard SPI core..." << std::flush; - codec_ctrl->set_active_chains(true, false, true, false); - codec_ctrl->set_clock_rate(50e6); - UHD_LOGGER_DEBUG("E300") << "OK" << std::endl; - - //////////////////////////////////////////////////////////////////// - // Set up RFNoC blocks - //////////////////////////////////////////////////////////////////// - const size_t n_rfnoc_blocks = _global_regs->peek32(global_regs::RB32_CORE_NUM_CE); - enumerate_rfnoc_blocks( - 0, /* mboard index */ - n_rfnoc_blocks, - E300_XB_DST_AXI + 1, /* base port, rfnoc blocks come after the AXI connect */ - uhd::sid_t(E300_DEVICE_HERE, 0, E300_DEVICE_THERE, 0), - device_addr_t() - ); - - // If we have a radio, we must configure its codec control: - std::vector<rfnoc::block_id_t> radio_ids = find_blocks<rfnoc::e3xx_radio_ctrl_impl>("Radio"); - if (radio_ids.size() > 0) { - UHD_LOGGER_DEBUG("E300") << "Initializing Radio Block..." << std::endl; - get_block_ctrl<rfnoc::e3xx_radio_ctrl_impl>(radio_ids[0])->setup_radio(codec_ctrl); - if (radio_ids.size() != 1) { - UHD_LOGGER_WARNING("E300") << "Too many Radio Blocks found. Using only " << radio_ids[0] << std::endl; - } - } else { - UHD_LOGGER_DEBUG("E300") << "No Radio Block found. Assuming radio-less operation." << std::endl; - } - - //////////////////////////////////////////////////////////////////// - // do some post-init tasks - //////////////////////////////////////////////////////////////////// - // init the clock rate to something reasonable - _tree->access<double>(mb_path / "tick_rate") - .set(device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE)); - - // subdev spec contains full width of selections - subdev_spec_t rx_spec, tx_spec; - BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends")) - { - rx_spec.push_back(subdev_spec_pair_t("A", fe)); - } - BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends")) - { - tx_spec.push_back(subdev_spec_pair_t("A", fe)); - } - _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec); - _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); - UHD_LOGGER_DEBUG("E300") << "end of e300_impl()" << std::endl; -} - -e300_impl::~e300_impl(void) -{ - // Force RFNoC destructors to fire before loading the idle image - _rfnoc_block_ctrl.clear(); - if (_xport_path == AXI and not _do_not_reload) - common::load_fpga_image(_idle_image); -} - -void e300_impl::_register_loopback_self_test(wb_iface::sptr iface, uint32_t w_addr, uint32_t r_addr) -{ - bool test_fail = false; - UHD_LOGGER_INFO("E300") << "Performing register loopback test... "; - size_t hash = size_t(time(NULL)); - for (size_t i = 0; i < 100; i++) - { - boost::hash_combine(hash, i); - iface->poke32(w_addr, uint32_t(hash)); - test_fail = iface->peek32(r_addr) != uint32_t(hash); - if (test_fail) break; //exit loop on any failure - } - UHD_LOGGER_INFO("E300") << "Register loopback test " << ((test_fail)? " failed" : "passed"); -} - -uint32_t e300_impl::_get_version(compat_t which) -{ - const uint16_t compat_num - = _global_regs->peek32(global_regs::RB32_CORE_COMPAT); - - switch(which) { - case FPGA_MINOR: - return compat_num & 0xff; - case FPGA_MAJOR: - return (compat_num & 0xff00) >> 8; - default: - throw uhd::value_error("Requested unknown version."); - }; -} - -std::string e300_impl::_get_version_hash(void) -{ - const uint32_t git_hash - = _global_regs->peek32(global_regs::RB32_CORE_GITHASH); - return str(boost::format("%7x%s") - % (git_hash & 0x0FFFFFFF) - % ((git_hash & 0xF0000000) ? "-dirty" : "")); -} - - -void e300_impl::_setup_dest_mapping( - const uhd::sid_t &sid, - const size_t which_stream) -{ - UHD_LOGGER_DEBUG("E300") << boost::format("[E300] Setting up dest map for host ep %lu to be stream %d") - % sid.get_src_endpoint() % which_stream << std::endl; - _global_regs->poke32(DST_ADDR(sid.get_src_endpoint()), which_stream); -} - -size_t e300_impl::_get_axi_dma_channel_pair() -{ - if (_dma_chans_available.none()) { - throw uhd::runtime_error("No more free DMA channels available."); - } - - size_t first_free_pair = _dma_chans_available.find_first(); - _dma_chans_available.reset(first_free_pair); - return first_free_pair; -} - -uint16_t e300_impl::_get_udp_port( - uint8_t destination, - uint8_t prefix) -{ - if (destination == E300_XB_DST_RADIO) { - if (prefix == E300_RADIO_DEST_PREFIX_CTRL) - return boost::lexical_cast<uint16_t>(E300_SERVER_CTRL_PORT0); - else if (prefix == E300_RADIO_DEST_PREFIX_TX) - return boost::lexical_cast<uint16_t>(E300_SERVER_TX_PORT0); - else if (prefix == E300_RADIO_DEST_PREFIX_RX) - return boost::lexical_cast<uint16_t>(E300_SERVER_RX_PORT0); - } else if (destination == E300_XB_DST_R1) { - if (prefix == E300_RADIO_DEST_PREFIX_CTRL) - return boost::lexical_cast<uint16_t>(E300_SERVER_CTRL_PORT1); - else if (prefix == E300_RADIO_DEST_PREFIX_TX) - return boost::lexical_cast<uint16_t>(E300_SERVER_TX_PORT1); - else if (prefix == E300_RADIO_DEST_PREFIX_RX) - return boost::lexical_cast<uint16_t>(E300_SERVER_RX_PORT1); - } - throw uhd::value_error(str(boost::format("No UDP port defined for combination: %u %u") % destination % prefix)); -} - -uhd::sid_t e300_impl::_allocate_sid( - const uhd::sid_t &address) -{ - uhd::sid_t sid = address; - sid.set_src_addr(E300_DEVICE_HERE); - sid.set_src_endpoint(_sid_framer); - - // TODO: We don't have to do this everytime ... - // Program the E300 to recognize it's own local address. - _global_regs->poke32(global_regs::SR_CORE_XB_LOCAL, address.get_dst_addr()); - - // Program CAM entry for outgoing packets matching a E300 resource - // (e.g. Radio). - // This type of packet matches the XB_LOCAL address and is looked up in - // the upper half of the CAM - _global_regs->poke32(XB_ADDR(256 + address.get_dst_endpoint()), address.get_dst_xbarport()); - - // TODO: We don't have to do this everytime ... - // Program CAM entry for returning packets to us - // (for example host via zynq_fifo) - // This type of packet does not match the XB_LOCAL address and is - // looked up in the lower half of the CAM - _global_regs->poke32(XB_ADDR(E300_DEVICE_HERE), E300_XB_DST_AXI); - - // increment for next setup - _sid_framer++; - - return sid; -} - -uhd::both_xports_t e300_impl::make_transport( - const uhd::sid_t &address, - const xport_type_t type, - const uhd::device_addr_t &) -{ - uhd::both_xports_t xports; - xports.endianness = ENDIANNESS_LITTLE; - - const uhd::transport::zero_copy_xport_params params = - (type == CTRL) ? _ctrl_xport_params : _data_xport_params; - - xports.send_sid = _allocate_sid(address); - xports.recv_sid = xports.send_sid.reversed(); - xports.recv_buff_size = params.recv_frame_size * params.num_recv_frames; - xports.send_buff_size = params.send_frame_size * params.num_send_frames; - - if (_xport_path != AXI) { - throw uhd::runtime_error("[E300] Currently only AXI transport supported with RFNOC"); - } - - const size_t chan_pair = _get_axi_dma_channel_pair(); - xports.send = _fifo_iface->make_send_xport(chan_pair, params); - xports.recv = _fifo_iface->make_recv_xport(chan_pair, params); - _setup_dest_mapping(xports.send_sid, chan_pair); - - return xports; -} - -void e300_impl::_update_clock_source(const std::string &source) -{ - if (source != "internal") { - throw uhd::value_error(boost::str( - boost::format("Clock source option not supported: %s. The only value supported is \"internal\". " \ - "To discipline the internal oscillator, set the appropriate time source.") % source - )); - } -} - -}}} // namespace - -UHD_STATIC_BLOCK(register_e300_device) -{ - device::register_device(&uhd::usrp::e300::e300_find, &uhd::usrp::e300::e300_make, uhd::device::USRP); -} diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp deleted file mode 100644 index 2e919cf9b..000000000 --- a/host/lib/usrp/e300/e300_impl.hpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// Copyright 2013-2015 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_IMPL_HPP -#define INCLUDED_E300_IMPL_HPP - -#include "../device3/device3_impl.hpp" -#include <uhd/property_tree.hpp> -#include <uhd/usrp/mboard_eeprom.hpp> -#include <uhd/usrp/dboard_eeprom.hpp> -#include <uhd/usrp/subdev_spec.hpp> -#include <uhd/types/serial.hpp> -#include <uhd/types/sensors.hpp> - -#include <boost/weak_ptr.hpp> -#include <boost/thread/mutex.hpp> -#include <boost/dynamic_bitset.hpp> -#include <string> -#include "e300_fifo_config.hpp" - -#include "e300_global_regs.hpp" -#include "e300_i2c.hpp" -#include "e300_eeprom_manager.hpp" -#include "e300_sensor_manager.hpp" - -/* if we don't compile with gpsd support, don't bother */ -#ifdef E300_GPSD -#include "gpsd_iface.hpp" -#endif - -#include <atomic> - -namespace uhd { namespace usrp { namespace e300 { - -static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit"; -static const std::string E310_SG1_FPGA_FILE_NAME = "usrp_e310_fpga.bit"; -static const std::string E310_SG3_FPGA_FILE_NAME = "usrp_e310_fpga_sg3.bit"; - -static const std::string E3XX_SG1_FPGA_IDLE_FILE_NAME = "usrp_e3xx_fpga_idle.bit"; -static const std::string E3XX_SG3_FPGA_IDLE_FILE_NAME = "usrp_e3xx_fpga_idle_sg3.bit"; - -static const std::string E300_TEMP_SYSFS = "iio:device0"; -static const std::string E300_SPIDEV_DEVICE = "/dev/spidev0.1"; -static const std::string E300_I2CDEV_DEVICE = "/dev/i2c-0"; - -static std::string E300_SERVER_RX_PORT0 = "21756"; -static std::string E300_SERVER_TX_PORT0 = "21757"; -static std::string E300_SERVER_CTRL_PORT0 = "21758"; - -static std::string E300_SERVER_RX_PORT1 = "21856"; -static std::string E300_SERVER_TX_PORT1 = "21857"; -static std::string E300_SERVER_CTRL_PORT1 = "21858"; - - -static std::string E300_SERVER_CODEC_PORT = "21759"; -static std::string E300_SERVER_GREGS_PORT = "21760"; -static std::string E300_SERVER_I2C_PORT = "21761"; -static std::string E300_SERVER_SENSOR_PORT = "21762"; - -static const double E300_RX_SW_BUFF_FULLNESS = 0.9; //Buffer should be half full -static const size_t E300_RX_FC_REQUEST_FREQ = 5; // per flow ctrl window -static const size_t E300_TX_FC_RESPONSE_FREQ = 8; // per flow ctrl window - -// crossbar settings -static const uint8_t E300_RADIO_DEST_PREFIX_TX = 0; -static const uint8_t E300_RADIO_DEST_PREFIX_CTRL = 1; -static const uint8_t E300_RADIO_DEST_PREFIX_RX = 2; - -static const uint8_t E300_XB_DST_AXI = 0; -static const uint8_t E300_XB_DST_RADIO = 1; -static const uint8_t E300_XB_DST_R1 = 2; -// RFNoC blocks are connected to the first port -// after the last radio (there might be less than 2 -// radios). - -static const uint8_t E300_DEVICE_THERE = 2; -static const uint8_t E300_DEVICE_HERE = 0; - -static const size_t E300_R0_CTRL_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_CTRL; -static const size_t E300_R0_TX_DATA_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_TX; -static const size_t E300_R0_RX_DATA_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_RX; - -static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_CTRL; -static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX; -static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX; - -uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint); -void get_e3x0_fpga_images(const uhd::device_addr_t &device_args, - std::string &fpga_image, - std::string &idle_image); - -/*! - * USRP-E300 implementation guts: - * The implementation details are encapsulated here. - * Handles properties on the mboard, dboard, dsps... - */ -class e300_impl : public uhd::usrp::device3_impl -{ -public: - /************************************************************************ - * Structors - ***********************************************************************/ - e300_impl(const uhd::device_addr_t &); - virtual ~e300_impl(void); - -private: // types - enum compat_t {FPGA_MAJOR, FPGA_MINOR}; - -protected: // methods - /************************************************************************ - * Legacy device3 stuff - ***********************************************************************/ - void subdev_to_blockid( - const uhd::usrp::subdev_spec_pair_t &spec, const size_t mb_i, - rfnoc::block_id_t &block_id, uhd::device_addr_t &block_args - ); - uhd::usrp::subdev_spec_pair_t blockid_to_subdev( - const rfnoc::block_id_t &blockid, const device_addr_t &block_args - ); - - /************************************************************************ - * Transport related - ***********************************************************************/ - uhd::device_addr_t get_rx_hints(size_t); - -private: // methods - /************************************************************************ - * Initialization - ***********************************************************************/ - void _register_loopback_self_test(wb_iface::sptr iface, uint32_t w_addr, uint32_t r_addr); - - uint32_t _get_version(compat_t which); - std::string _get_version_hash(void); - - /************************************************************************ - * Transport related - ***********************************************************************/ - uhd::sid_t _allocate_sid(const uhd::sid_t &address); - - void _setup_dest_mapping( - const uhd::sid_t &sid, - const size_t which_stream); - - /*! Return the first free AXI channel pair. - * - * \throws uhd::runtime_error if no free channel pairs are available. - */ - size_t _get_axi_dma_channel_pair(); - - // For network mode - uint16_t _get_udp_port( - uint8_t destination, - uint8_t prefix); - - uhd::both_xports_t make_transport( - const uhd::sid_t &address, - const xport_type_t type, - const uhd::device_addr_t &args - ); - - uhd::endianness_t get_transport_endianness(size_t) { - return uhd::ENDIANNESS_LITTLE; - }; - - /************************************************************************ - * Helpers - ***********************************************************************/ - void _update_clock_source(const std::string &); - -private: // members - const uhd::device_addr_t _device_addr; - xport_t _xport_path; - e300_fifo_interface::sptr _fifo_iface; - std::atomic<size_t> _sid_framer; - boost::dynamic_bitset<> _dma_chans_available; - global_regs::sptr _global_regs; - e300_sensor_manager::sptr _sensor_manager; - e300_eeprom_manager::sptr _eeprom_manager; - uhd::transport::zero_copy_xport_params _data_xport_params; - uhd::transport::zero_copy_xport_params _ctrl_xport_params; - std::string _idle_image; - bool _do_not_reload; -#ifdef E300_GPSD - gpsd_iface::sptr _gps; - static const size_t _GPS_TIMEOUT = 5; -#endif -}; - -}}} // namespace uhd::usrp::e300 - -#endif /* INCLUDED_E300_IMPL_HPP */ diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp deleted file mode 100644 index 4460d5616..000000000 --- a/host/lib/usrp/e300/e300_io_impl.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_regs.hpp" -#include "e300_impl.hpp" -#include "e300_fpga_defs.hpp" -#include "e300_defaults.hpp" -#include "../../transport/super_recv_packet_handler.hpp" -#include "../../transport/super_send_packet_handler.hpp" -#include <uhd/utils/tasks.hpp> -#include <boost/bind.hpp> -#include <boost/format.hpp> - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::transport; - -namespace uhd { namespace usrp { namespace e300 { - -uhd::device_addr_t e300_impl::get_rx_hints(size_t) -{ - return uhd::device_addr_t(str(boost::format("max_recv_window=%d") % DEFAULT_RX_DATA_NUM_FRAMES)); -} - -}}} // namespace diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp deleted file mode 100644 index d9a104dda..000000000 --- a/host/lib/usrp/e300/e300_network.cpp +++ /dev/null @@ -1,655 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_network.hpp" - -#ifdef E300_NATIVE - -#include "e300_impl.hpp" - -#include "e300_sensor_manager.hpp" -#include "e300_fifo_config.hpp" -#include "e300_spi.hpp" -#include "e300_i2c.hpp" -#include "e300_defaults.hpp" -#include "e300_common.hpp" -#include "e300_remote_codec_ctrl.hpp" - -#include <uhd/utils/log.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/paths.hpp> - -#include <uhdlib/usrp/common/ad9361_ctrl.hpp> - -#include <boost/asio.hpp> -#include <boost/filesystem.hpp> -#include <boost/make_shared.hpp> - -#include <fstream> -#include <chrono> -#include <thread> - -using namespace uhd; -using namespace uhd::transport; -namespace asio = boost::asio; -namespace fs = boost::filesystem; - -namespace uhd { namespace usrp { namespace e300 { - -static const size_t E300_NETWORK_DEBUG = false; - -static inline bool wait_for_recv_ready(int sock_fd, const size_t timeout_ms) -{ - //setup timeval for timeout - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = timeout_ms*1000; - - //setup rset for timeout - fd_set rset; - FD_ZERO(&rset); - FD_SET(sock_fd, &rset); - - //call select with timeout on receive socket - return ::select(sock_fd+1, &rset, NULL, NULL, &tv) > 0; -} - -static boost::mutex endpoint_mutex; - -/*********************************************************************** - * Receive tunnel - forwards recv interface to send socket - **********************************************************************/ -static void e300_recv_tunnel( - const std::string &name, - uhd::transport::zero_copy_if::sptr recver, - boost::shared_ptr<asio::ip::udp::socket> sender, - asio::ip::udp::endpoint *endpoint, - bool *running -) -{ - asio::ip::udp::endpoint _tx_endpoint; - try - { - while (*running) - { - //step 1 - get the buffer - managed_recv_buffer::sptr buff = recver->get_recv_buff(); - if (not buff) continue; - if (E300_NETWORK_DEBUG) UHD_LOGGER_INFO("E300") << name << " got " << buff->size(); - - //step 1.5 -- update endpoint - { - boost::mutex::scoped_lock l(endpoint_mutex); - _tx_endpoint = *endpoint; - } - - //step 2 - send to the socket - sender->send_to(asio::buffer(buff->cast<const void *>(), buff->size()), _tx_endpoint); - } - } - catch(const std::exception &ex) - { - UHD_LOGGER_ERROR("E300") << "e300_recv_tunnel exit " << name << " " << ex.what(); - } - catch(...) - { - UHD_LOGGER_ERROR("E300") << "e300_recv_tunnel exit " << name ; - } - UHD_LOGGER_INFO("E300") << "e300_recv_tunnel exit " << name; - *running = false; -} - -/*********************************************************************** - * Send tunnel - forwards recv socket to send interface - **********************************************************************/ -static void e300_send_tunnel( - const std::string &name, - boost::shared_ptr<asio::ip::udp::socket> recver, - uhd::transport::zero_copy_if::sptr sender, - asio::ip::udp::endpoint *endpoint, - bool *running -) -{ - asio::ip::udp::endpoint _rx_endpoint; - try - { - while (*running) - { - //step 1 - get the buffer - managed_send_buffer::sptr buff = sender->get_send_buff(); - if (not buff) continue; - - //step 2 - recv from socket - while (not wait_for_recv_ready(recver->native_handle(), 100) and *running){} - if (not *running) break; - const size_t num_bytes = recver->receive_from(asio::buffer(buff->cast<void *>(), buff->size()), _rx_endpoint); - if (E300_NETWORK_DEBUG) UHD_LOGGER_INFO("E300") << name << " got " << num_bytes; - - //step 2.5 -- update endpoint - { - boost::mutex::scoped_lock l(endpoint_mutex); - *endpoint = _rx_endpoint; - } - - //step 3 - commit the buffer - buff->commit(num_bytes); - } - } - catch(const std::exception &ex) - { - UHD_LOGGER_ERROR("E300") << "e300_send_tunnel exit " << name << " " << ex.what() ; - } - catch(...) - { - UHD_LOGGER_ERROR("E300") << "e300_send_tunnel exit " << name ; - } - UHD_LOGGER_INFO("E300") << "e300_send_tunnel exit " << name; - *running = false; -} - -static void e300_codec_ctrl_tunnel( - const std::string &name, - boost::shared_ptr<asio::ip::udp::socket> socket, - ad9361_ctrl::sptr _codec_ctrl, - asio::ip::udp::endpoint *endpoint, - bool *running -) -{ - asio::ip::udp::endpoint _endpoint; - try - { - while (*running) - { - uint8_t in_buff[64] = {}; - uint8_t out_buff[64] = {}; - - const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); - - typedef e300_remote_codec_ctrl::transaction_t codec_xact_t; - - if (num_bytes < sizeof(codec_xact_t)) { - std::cout << "Received short packet of " << num_bytes << std::endl; - continue; - } - - codec_xact_t *in = reinterpret_cast<codec_xact_t*>(in_buff); - codec_xact_t *out = reinterpret_cast<codec_xact_t*>(out_buff); - std::memcpy(out, in, sizeof(codec_xact_t)); - - std::string which_str; - switch (uhd::ntohx<uint32_t>(in->which)) { - case codec_xact_t::CHAIN_TX1: - which_str = "TX1"; break; - case codec_xact_t::CHAIN_TX2: - which_str = "TX2"; break; - case codec_xact_t::CHAIN_RX1: - which_str = "RX1"; break; - case codec_xact_t::CHAIN_RX2: - which_str = "RX2"; break; - default: - which_str = ""; break; - } - - switch (uhd::ntohx<uint32_t>(in->action)) { - case codec_xact_t::ACTION_SET_GAIN: - out->gain = _codec_ctrl->set_gain(which_str, in->gain); - break; - case codec_xact_t::ACTION_SET_CLOCK_RATE: - out->rate = _codec_ctrl->set_clock_rate(in->rate); - break; - case codec_xact_t::ACTION_SET_ACTIVE_CHANS: - _codec_ctrl->set_active_chains( - uhd::ntohx<uint32_t>(in->bits) & (1<<0), - uhd::ntohx<uint32_t>(in->bits) & (1<<1), - uhd::ntohx<uint32_t>(in->bits) & (1<<2), - uhd::ntohx<uint32_t>(in->bits) & (1<<3)); - break; - case codec_xact_t::ACTION_TUNE: - out->freq = _codec_ctrl->tune(which_str, in->freq); - break; - case codec_xact_t::ACTION_GET_FREQ: - out->freq = _codec_ctrl->get_freq(which_str); - break; - case codec_xact_t::ACTION_SET_LOOPBACK: - _codec_ctrl->data_port_loopback( - uhd::ntohx<uint32_t>(in->bits) & 1); - break; - case codec_xact_t::ACTION_GET_RSSI: - out->rssi = _codec_ctrl->get_rssi(which_str).to_real(); - break; - case codec_xact_t::ACTION_GET_TEMPERATURE: - out->temp = _codec_ctrl->get_temperature().to_real(); - break; - case codec_xact_t::ACTION_SET_DC_OFFSET_AUTO: - _codec_ctrl->set_dc_offset_auto(which_str, in->use_dc_correction == 1); - break; - case codec_xact_t::ACTION_SET_IQ_BALANCE_AUTO: - _codec_ctrl->set_iq_balance_auto(which_str, in->use_iq_correction == 1); - case codec_xact_t::ACTION_SET_AGC: - _codec_ctrl->set_agc(which_str, in->use_agc == 1); - break; - case codec_xact_t::ACTION_SET_AGC_MODE: - if(in->agc_mode == 0) { - _codec_ctrl->set_agc_mode(which_str, "slow"); - } else if (in->agc_mode == 1) { - _codec_ctrl->set_agc_mode(which_str, "fast"); - } - break; - case codec_xact_t::ACTION_SET_BW: - out->bw = _codec_ctrl->set_bw_filter(which_str, in->bw); - break; - default: - UHD_LOGGER_INFO("E300") << "Got unknown request?!"; - //Zero out actions to fail this request on client - out->action = uhd::htonx<uint32_t>(0); - } - - socket->send_to(asio::buffer(out_buff, 64), *endpoint); - } - } - catch(const std::exception &ex) - { - UHD_LOGGER_ERROR("E300") << "e300_ctrl_tunnel exit " << name << " " << ex.what() ; - } - catch(...) - { - UHD_LOGGER_ERROR("E300") << "e300_ctrl_tunnel exit " << name ; - } - UHD_LOGGER_INFO("E300") << "e300_ctrl_tunnel exit " << name; - *running = false; -} - -static void e300_global_regs_tunnel( - const std::string &name, - boost::shared_ptr<asio::ip::udp::socket> socket, - global_regs::sptr regs, - asio::ip::udp::endpoint *endpoint, - bool *running -) -{ - UHD_ASSERT_THROW(regs); - asio::ip::udp::endpoint _endpoint; - try - { - while (*running) - { - uint8_t in_buff[16] = {}; - - const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); - - if (num_bytes < 16) { - std::cout << "Received short packet: " << num_bytes << std::endl; - continue; - } - - global_regs_transaction_t *in = - reinterpret_cast<global_regs_transaction_t *>(in_buff); - - if(uhd::ntohx<uint32_t>(in->is_poke)) { - regs->poke32(uhd::ntohx<uint32_t>(in->addr), uhd::ntohx<uint32_t>(in->data)); - } - else { - in->data = uhd::htonx<uint32_t>(regs->peek32(uhd::ntohx<uint32_t>(in->addr))); - socket->send_to(asio::buffer(in_buff, 16), *endpoint); - } - } - } - catch(const std::exception &ex) - { - UHD_LOGGER_ERROR("E300") << "e300_gregs_tunnel exit " << name << " " << ex.what() ; - } - catch(...) - { - UHD_LOGGER_ERROR("E300") << "e300_gregs_tunnel exit " << name ; - } - UHD_LOGGER_INFO("E300") << "e300_gregs_tunnel exit " << name; - *running = false; -} - -static void e300_sensor_tunnel( - const std::string &name, - boost::shared_ptr<asio::ip::udp::socket> socket, - e300_sensor_manager::sptr sensor_manager, - asio::ip::udp::endpoint *endpoint, - bool *running -) -{ - asio::ip::udp::endpoint _endpoint; - try - { - while (*running) - { - uint8_t in_buff[128] = {}; - - const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); - - if (num_bytes < sizeof(sensor_transaction_t)) { - std::cout << "Received short packet: " << num_bytes << std::endl; - continue; - } - - uhd::usrp::e300::sensor_transaction_t *in = - reinterpret_cast<uhd::usrp::e300::sensor_transaction_t *>(in_buff); - - if (uhd::ntohx(in->which) == ZYNQ_TEMP) { - sensor_value_t temp = sensor_manager->get_mb_temp(); - // TODO: This is ugly ... use proper serialization - in->value = uhd::htonx<uint32_t>( - e300_sensor_manager::pack_float_in_uint32_t(temp.to_real())); - } else if (uhd::ntohx(in->which) == REF_LOCK) { - in->value = uhd::htonx<uint32_t>( - sensor_manager->get_ref_lock().to_bool() ? 1 : 0); - } else - UHD_LOGGER_INFO("E300") << "Got unknown request?!"; - - socket->send_to(asio::buffer(in_buff, sizeof(sensor_transaction_t)), *endpoint); - } - } - catch(const std::exception &ex) - { - UHD_LOGGER_ERROR("E300") << "e300_sensor_tunnel exit " << name << " " << ex.what() ; - } - catch(...) - { - UHD_LOGGER_ERROR("E300") << "e300_sensor_tunnel exit " << name ; - } - UHD_LOGGER_INFO("E300") << "e300_sensor_tunnel exit " << name; - *running = false; -} - -static void e300_i2c_tunnel( - const std::string &name, - boost::shared_ptr<asio::ip::udp::socket> socket, - uhd::usrp::e300::i2c::sptr i2c, - asio::ip::udp::endpoint *endpoint, - bool *running -) -{ - UHD_ASSERT_THROW(i2c); - asio::ip::udp::endpoint _endpoint; - try - { - while (*running) - { - uint8_t in_buff[sizeof(uhd::usrp::e300::i2c_transaction_t)]; - - const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); - - if (num_bytes < sizeof(uhd::usrp::e300::i2c_transaction_t)) { - std::cout << "Received short packet: " << num_bytes << std::endl; - continue; - } - - uhd::usrp::e300::i2c_transaction_t *in = - reinterpret_cast<uhd::usrp::e300::i2c_transaction_t *>(in_buff); - - // byte addressed accesses go through here - if(in->type & i2c::ONEBYTE) { - if(in->type & i2c::WRITE) { - i2c->set_i2c_reg8( - in->addr, - uhd::ntohx<uint16_t>(in->reg), in->data); - } else { - in->data = i2c->get_i2c_reg8(in->addr, uhd::ntohx<uint16_t>(in->reg)); - socket->send_to(asio::buffer(in_buff, sizeof(in_buff)), *endpoint); - } - - // 2 byte addressed accesses go through here - } else if (in->type & i2c::TWOBYTE) { - if(in->type & i2c::WRITE) { - i2c->set_i2c_reg16( - in->addr, - uhd::ntohx<uint16_t>(in->reg), in->data); - } else { - in->data = i2c->get_i2c_reg16(in->addr, uhd::ntohx<uint16_t>(in->reg)); - socket->send_to(asio::buffer(in_buff, sizeof(in_buff)), *endpoint); - } - - } else { - UHD_LOGGER_ERROR("E300") << "e300_i2c_tunnel could not handle message." ; - } - } - } - catch(const std::exception &ex) - { - UHD_LOGGER_ERROR("E300") << "e300_i2c_tunnel exit " << name << " " << ex.what() ; - } - catch(...) - { - UHD_LOGGER_ERROR("E300") << "e300_i2c_tunnel exit " << name ; - } - UHD_LOGGER_INFO("E300") << "e300_i2c_tunnel exit " << name; - *running = false; -} - - - - -class network_server_impl : public network_server -{ -public: - network_server_impl(const uhd::device_addr_t &device_addr); - virtual ~network_server_impl(void); - void run(void); - -private: - struct xports_t - { - uhd::transport::zero_copy_if::sptr send_ctrl_xport; - uhd::transport::zero_copy_if::sptr recv_ctrl_xport; - uhd::transport::zero_copy_if::sptr tx_data_xport; - uhd::transport::zero_copy_if::sptr tx_flow_xport; - uhd::transport::zero_copy_if::sptr rx_data_xport; - uhd::transport::zero_copy_if::sptr rx_flow_xport; - }; - -private: - void _run_server( - const std::string &port, - const std::string &what, - const size_t fe); - -private: - boost::shared_ptr<e300_fifo_interface> _fifo_iface; - xports_t _xports[2]; - boost::shared_ptr<ad9361_ctrl> _codec_ctrl; - boost::shared_ptr<global_regs> _global_regs; - boost::shared_ptr<e300_sensor_manager> _sensor_manager; - boost::shared_ptr<e300_eeprom_manager> _eeprom_manager; -}; - -network_server_impl::~network_server_impl(void) -{ -} - -/*********************************************************************** - * The UDP server itself - **********************************************************************/ -void network_server_impl::_run_server( - const std::string &port, - const std::string &what, - const size_t fe) -{ - asio::io_service io_service; - asio::ip::udp::resolver resolver(io_service); - asio::ip::udp::resolver::query query(asio::ip::udp::v4(), "0.0.0.0", port); - asio::ip::udp::endpoint endpoint = *resolver.resolve(query); - - //boost::shared_ptr<asio::ip::udp::acceptor> acceptor(new asio::ip::udp::acceptor(io_service, endpoint)); - while (not boost::this_thread::interruption_requested()) - { - UHD_LOGGER_INFO("E300") << "e300 run server on port " << port << " for " << what; - try - { - //while (not wait_for_recv_ready(acceptor->native(), 100)) - //{ - // if (boost::this_thread::interruption_requested()) return; - //} - boost::shared_ptr<asio::ip::udp::socket> socket; - socket.reset(new asio::ip::udp::socket(io_service, endpoint)); - //acceptor->accept(*socket); - UHD_LOGGER_INFO("E300") << "e300 socket accept on port " << port << " for " << what; - //asio::ip::udp::no_delay option(true); - //socket->set_option(option); - boost::thread_group tg; - bool running = true; - xports_t &perif = _xports[fe]; - if (what == "RX") { - tg.create_thread(boost::bind(&e300_recv_tunnel, "RX data tunnel", perif.rx_data_xport, socket, &endpoint, &running)); - tg.create_thread(boost::bind(&e300_send_tunnel, "RX flow tunnel", socket, perif.rx_flow_xport, &endpoint, &running)); - } - if (what == "TX") { - tg.create_thread(boost::bind(&e300_recv_tunnel, "TX flow tunnel", perif.tx_flow_xport, socket, &endpoint, &running)); - tg.create_thread(boost::bind(&e300_send_tunnel, "TX data tunnel", socket, perif.tx_data_xport, &endpoint, &running)); - } - if (what == "CTRL") { - tg.create_thread(boost::bind(&e300_recv_tunnel, "response tunnel", perif.recv_ctrl_xport, socket, &endpoint, &running)); - tg.create_thread(boost::bind(&e300_send_tunnel, "control tunnel", socket, perif.send_ctrl_xport, &endpoint, &running)); - } - if (what == "CODEC") { - tg.create_thread(boost::bind(&e300_codec_ctrl_tunnel, "CODEC tunnel", socket, _codec_ctrl, &endpoint, &running)); - } - if (what == "I2C") { - tg.create_thread(boost::bind(&e300_i2c_tunnel, "I2C tunnel", socket, _eeprom_manager->get_i2c_sptr(), &endpoint, &running)); - } - if (what == "GREGS") { - tg.create_thread(boost::bind(&e300_global_regs_tunnel, "GREGS tunnel", socket, _global_regs, &endpoint, &running)); - } - if (what == "SENSOR") { - tg.create_thread(boost::bind(&e300_sensor_tunnel, "SENSOR tunnel", socket, _sensor_manager, &endpoint, &running)); - } - - tg.join_all(); - socket->close(); - socket.reset(); - } - catch(...){} - } -} - -void network_server_impl::run() -{ - for(;;) - { - boost::thread_group tg; - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_RX_PORT0, "RX",0)); - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_TX_PORT0, "TX",0)); - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CTRL_PORT0, "CTRL",0)); - - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_RX_PORT1, "RX",1)); - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_TX_PORT1, "TX",1)); - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CTRL_PORT1, "CTRL",1)); - - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_SENSOR_PORT, "SENSOR", 0 /*don't care */)); - - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CODEC_PORT, "CODEC", 0 /*don't care */)); - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_GREGS_PORT, "GREGS", 0 /*don't care */)); - tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_I2C_PORT, "I2C", 0 /*don't care */)); - tg.join_all(); - } -} -network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr) -{ - _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); - if (not device_addr.has_key("no_reload_fpga")) { - // Load FPGA image if provided via args - if (device_addr.has_key("fpga")) { - common::load_fpga_image(device_addr["fpga"]); - // Else load the FPGA image based on the product ID - } else { - //extract the FPGA path for the e300 - const uint16_t pid = boost::lexical_cast<uint16_t>( - _eeprom_manager->get_mb_eeprom()["product"]); - std::string fpga_image; - switch(e300_eeprom_manager::get_mb_type(pid)) { - case e300_eeprom_manager::USRP_E310_SG1_MB: - fpga_image = find_image_path(E310_SG1_FPGA_FILE_NAME); - break; - case e300_eeprom_manager::USRP_E310_SG3_MB: - fpga_image = find_image_path(E310_SG3_FPGA_FILE_NAME); - break; - case e300_eeprom_manager::USRP_E300_MB: - fpga_image = find_image_path(E300_FPGA_FILE_NAME); - break; - case e300_eeprom_manager::UNKNOWN: - default: - UHD_LOGGER_WARNING("E300") << "Unknown motherboard type, loading e300 image." - ; - fpga_image = find_image_path(E300_FPGA_FILE_NAME); - break; - } - common::load_fpga_image(fpga_image); - } - } - - uhd::transport::zero_copy_xport_params ctrl_xport_params; - ctrl_xport_params.recv_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; - ctrl_xport_params.num_recv_frames = e300::DEFAULT_CTRL_NUM_FRAMES; - ctrl_xport_params.send_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; - ctrl_xport_params.num_send_frames = e300::DEFAULT_CTRL_NUM_FRAMES; - - uhd::transport::zero_copy_xport_params data_xport_params; - data_xport_params.recv_frame_size = device_addr.cast<size_t>("recv_frame_size", e300::DEFAULT_RX_DATA_FRAME_SIZE); - data_xport_params.num_recv_frames = device_addr.cast<size_t>("num_recv_frames", e300::DEFAULT_RX_DATA_NUM_FRAMES); - data_xport_params.send_frame_size = device_addr.cast<size_t>("send_frame_size", e300::DEFAULT_TX_DATA_FRAME_SIZE); - data_xport_params.num_send_frames = device_addr.cast<size_t>("num_send_frames", e300::DEFAULT_TX_DATA_NUM_FRAMES); - // until we figure out why this goes wrong we'll keep this hack around - data_xport_params.recv_frame_size = - std::min(e300::MAX_NET_RX_DATA_FRAME_SIZE, data_xport_params.recv_frame_size); - data_xport_params.send_frame_size = - std::min(e300::MAX_NET_TX_DATA_FRAME_SIZE, data_xport_params.send_frame_size); - - - e300_fifo_config_t fifo_cfg; - try { - fifo_cfg = e300_read_sysfs(); - } catch (uhd::lookup_error &e) { - throw uhd::runtime_error("Failed to get driver parameters from sysfs."); - } - _fifo_iface = e300_fifo_interface::make(fifo_cfg); - _global_regs = global_regs::make(_fifo_iface->get_global_regs_base()); - - // static mapping, boooohhhhhh - _xports[0].send_ctrl_xport = _fifo_iface->make_send_xport(E300_R0_CTRL_STREAM, ctrl_xport_params); - _xports[0].recv_ctrl_xport = _fifo_iface->make_recv_xport(E300_R0_CTRL_STREAM, ctrl_xport_params); - _xports[0].tx_data_xport = _fifo_iface->make_send_xport(E300_R0_TX_DATA_STREAM, data_xport_params); - _xports[0].tx_flow_xport = _fifo_iface->make_recv_xport(E300_R0_TX_DATA_STREAM, ctrl_xport_params); - _xports[0].rx_data_xport = _fifo_iface->make_recv_xport(E300_R0_RX_DATA_STREAM, data_xport_params); - _xports[0].rx_flow_xport = _fifo_iface->make_send_xport(E300_R0_RX_DATA_STREAM, ctrl_xport_params); - - _xports[1].send_ctrl_xport = _fifo_iface->make_send_xport(E300_R1_CTRL_STREAM, ctrl_xport_params); - _xports[1].recv_ctrl_xport = _fifo_iface->make_recv_xport(E300_R1_CTRL_STREAM, ctrl_xport_params); - _xports[1].tx_data_xport = _fifo_iface->make_send_xport(E300_R1_TX_DATA_STREAM, data_xport_params); - _xports[1].tx_flow_xport = _fifo_iface->make_recv_xport(E300_R1_TX_DATA_STREAM, ctrl_xport_params); - _xports[1].rx_data_xport = _fifo_iface->make_recv_xport(E300_R1_RX_DATA_STREAM, data_xport_params); - _xports[1].rx_flow_xport = _fifo_iface->make_send_xport(E300_R1_RX_DATA_STREAM, ctrl_xport_params); - - ad9361_params::sptr client_settings = boost::make_shared<e300_ad9361_client_t>(); - _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1); - // This is horrible ... why do I have to sleep here? - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - _sensor_manager = e300_sensor_manager::make_local(_global_regs); -} - -}}} // namespace - -using namespace uhd::usrp::e300; - -network_server::sptr network_server::make(const uhd::device_addr_t &device_addr) -{ - return sptr(new network_server_impl(device_addr)); -} - -#else - -using namespace uhd::usrp::e300; - -network_server::sptr network_server::make(const uhd::device_addr_t &) -{ - throw uhd::assertion_error("network_server::make() !E300_NATIVE"); -} -#endif diff --git a/host/lib/usrp/e300/e300_network.hpp b/host/lib/usrp/e300/e300_network.hpp deleted file mode 100644 index caa25b0ad..000000000 --- a/host/lib/usrp/e300/e300_network.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_NETWORK_HPP -#define INCLUDED_E300_NETWORK_HPP - -#include <string> -#include <uhd/utils/noncopyable.hpp> - -#include <uhd/device.hpp> - - -static const std::string E310_FPGA_FILE_NAME = "usrp_e310_fpga.bit"; -static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit"; - -namespace uhd { namespace usrp { namespace e300 { - -class UHD_API network_server : uhd::noncopyable -{ -public: - typedef boost::shared_ptr<network_server> sptr; - virtual void run(void) = 0; - - static sptr make(const uhd::device_addr_t &device_addr); -}; - - -}}} -#endif // INCLUDED_E300_NETWORK_HPP diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp deleted file mode 100644 index 88d58907b..000000000 --- a/host/lib/usrp/e300/e300_regs.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright 2012-2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_REGS_HPP -#define INCLUDED_E300_REGS_HPP - -#include <stdint.h> -#include <uhd/config.hpp> - -static const uint32_t VCRX_V2 = 15; -static const uint32_t VCRX_V1 = 14; -static const uint32_t VCTXRX_V2 = 13; -static const uint32_t VCTXRX_V1 = 12; -static const uint32_t TX_ENABLEB = 11; -static const uint32_t TX_ENABLEA = 10; -static const uint32_t RXC_BANDSEL = 8; -static const uint32_t RXB_BANDSEL = 6; -static const uint32_t RX_BANDSEL = 3; -static const uint32_t TX_BANDSEL = 0; - -#endif /* INCLUDED_E300_REGS_HPP */ diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp deleted file mode 100644 index 5692a138e..000000000 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp +++ /dev/null @@ -1,285 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_remote_codec_ctrl.hpp" - -#include <stdint.h> -#include <uhd/exception.hpp> -#include <uhd/utils/byteswap.hpp> -#include <cstring> -#include <iostream> - -namespace uhd { namespace usrp { namespace e300 { - -class e300_remote_codec_ctrl_impl : public e300_remote_codec_ctrl -{ -public: - e300_remote_codec_ctrl_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport) - { - } - - virtual ~e300_remote_codec_ctrl_impl(void) - { - } - - - double set_gain(const std::string &which, const double value) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_GAIN); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.gain = value; - - _transact(); - return _retval.gain; - } - - double set_clock_rate(const double rate) - { - _clear(); - _args.action = uhd::htonx<uint32_t>( - transaction_t::ACTION_SET_CLOCK_RATE); - _args.which = uhd::htonx<uint32_t>( - transaction_t::CHAIN_NONE); /*Unused*/ - _args.rate = rate; - - _transact(); - return _retval.gain; - } - - void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) - { - _clear(); - _args.action = uhd::htonx<uint32_t>( - transaction_t::ACTION_SET_ACTIVE_CHANS); - /*Unused*/ - _args.which = uhd::htonx<uint32_t>( - transaction_t::CHAIN_NONE); - _args.bits = uhd::htonx<uint32_t>( - (tx1 ? (1<<0) : 0) | - (tx2 ? (1<<1) : 0) | - (rx1 ? (1<<2) : 0) | - (rx2 ? (1<<3) : 0)); - - _transact(); - } - - double tune(const std::string &which, const double value) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_TUNE); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.freq = value; - - _transact(); - return _retval.freq; - } - - double get_freq(const std::string &which) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_GET_FREQ); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - - _transact(); - return _retval.freq; - } - - void data_port_loopback(const bool on) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_LOOPBACK); - _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/ - _args.bits = uhd::htonx<uint32_t>(on ? 1 : 0); - - _transact(); - } - - sensor_value_t get_rssi(const std::string &which) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_GET_RSSI); - if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.bits = uhd::htonx<uint32_t>(0); - - _transact(); - return sensor_value_t("RSSI", _retval.rssi, "dB"); - } - - sensor_value_t get_temperature() - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_GET_TEMPERATURE); - _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/ - _args.bits = uhd::htonx<uint32_t>(0); - - _transact(); - return sensor_value_t("temp", _retval.temp, "C"); - } - - void set_dc_offset_auto(const std::string &which, const bool on) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_DC_OFFSET_AUTO); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.use_dc_correction = on ? 1 : 0; - - _transact(); - } - - void set_iq_balance_auto(const std::string &which, const bool on) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_IQ_BALANCE_AUTO); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.use_iq_correction = on ? 1 : 0; - - _transact(); - } - - void set_agc(const std::string &which, bool enable) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_AGC); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.use_agc = enable ? 1 : 0; - - _transact(); - } - - void set_agc_mode(const std::string &which, const std::string &mode) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_AGC_MODE); - - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - - if(mode == "slow") { - _args.agc_mode = 0; - } else if (mode == "fast") { - _args.agc_mode = 1; - } else { - throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect agc mode."); - } - - _transact(); - } - - //! set the filter bandwidth for the frontend's analog low pass - double set_bw_filter(const std::string &which, const double bw) - { - _clear(); - _args.action = uhd::htonx<uint32_t>(transaction_t::ACTION_SET_BW); - if (which == "TX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX1); - else if (which == "TX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_TX2); - else if (which == "RX1") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX1); - else if (which == "RX2") _args.which = uhd::htonx<uint32_t>(transaction_t::CHAIN_RX2); - else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); - _args.bw = bw; - - _transact(); - return _retval.bw; - } - - //! List all available filters by name - std::vector<std::string> get_filter_names(const std::string &) - { - return std::vector<std::string>(); - } - - //! Return a list of all filters - filter_info_base::sptr get_filter(const std::string &, const std::string &) - { - UHD_THROW_INVALID_CODE_PATH(); - } - - //! Write back a filter - void set_filter(const std::string &, const std::string &, const filter_info_base::sptr) - { - UHD_LOGGER_WARNING("E300") << "Attempting to set filter on E300 in network mode." ; - } - - void output_digital_test_tone(UHD_UNUSED(bool enb)) - { - UHD_THROW_INVALID_CODE_PATH(); - } - - void set_timing_mode(UHD_UNUSED(const std::string &timing_mode)) - { - UHD_THROW_INVALID_CODE_PATH(); - } - -private: - void _transact() { - { - uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < sizeof(_args)) - throw std::runtime_error("e300_remote_codec_ctrl_impl send timeout"); - std::memcpy(buff->cast<void *>(), &_args, sizeof(_args)); - buff->commit(sizeof(_args)); - } - { - uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); - if (not buff or buff->size() < sizeof(_retval)) - throw std::runtime_error("e300_remote_codec_ctrl_impl recv timeout"); - std::memcpy(&_retval, buff->cast<const void *>(), sizeof(_retval)); - } - - if (_args.action != _retval.action) - throw std::runtime_error("e300_remote_codec_ctrl_impl transaction failed."); - } - - void _clear() { - _args.action = 0; - _args.which = 0; - _args.bits = 0; - _retval.action = 0; - _retval.which = 0; - _retval.bits = 0; - } - - uhd::transport::zero_copy_if::sptr _xport; - transaction_t _args; - transaction_t _retval; -}; - -ad9361_ctrl::sptr e300_remote_codec_ctrl::make(uhd::transport::zero_copy_if::sptr xport) -{ - return sptr(new e300_remote_codec_ctrl_impl(xport)); -} - -}}}; diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp deleted file mode 100644 index 9c9e16672..000000000 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_REMOTE_CODEC_CTRL_HPP -#define INCLUDED_E300_REMOTE_CODEC_CTRL_HPP - -#include <uhd/transport/zero_copy.hpp> -#include <uhdlib/usrp/common/ad9361_ctrl.hpp> - -namespace uhd { namespace usrp { namespace e300 { - -class e300_remote_codec_ctrl : public uhd::usrp::ad9361_ctrl -{ -public: - struct transaction_t { - uint32_t action; - uint32_t which; - union { - double rate; - double gain; - double freq; - double rssi; - double temp; - double bw; - uint32_t use_dc_correction; - uint32_t use_iq_correction; - uint32_t use_agc; - uint32_t agc_mode; - uint64_t bits; - }; - - //Actions - static const uint32_t ACTION_SET_GAIN = 10; - static const uint32_t ACTION_SET_CLOCK_RATE = 11; - static const uint32_t ACTION_SET_ACTIVE_CHANS = 12; - static const uint32_t ACTION_TUNE = 13; - static const uint32_t ACTION_SET_LOOPBACK = 14; - static const uint32_t ACTION_GET_RSSI = 15; - static const uint32_t ACTION_GET_TEMPERATURE = 16; - static const uint32_t ACTION_SET_DC_OFFSET_AUTO = 17; - static const uint32_t ACTION_SET_IQ_BALANCE_AUTO = 18; - static const uint32_t ACTION_SET_AGC = 19; - static const uint32_t ACTION_SET_AGC_MODE = 20; - static const uint32_t ACTION_SET_BW = 21; - static const uint32_t ACTION_GET_FREQ = 22; - - //Values for "which" - static const uint32_t CHAIN_NONE = 0; - static const uint32_t CHAIN_TX1 = 1; - static const uint32_t CHAIN_TX2 = 2; - static const uint32_t CHAIN_RX1 = 3; - static const uint32_t CHAIN_RX2 = 4; - }; - - static sptr make(uhd::transport::zero_copy_if::sptr xport); -}; - -}}}; - -#endif /* INCLUDED_E300_REMOTE_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp deleted file mode 100644 index fda1f3f80..000000000 --- a/host/lib/usrp/e300/e300_sensor_manager.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "e300_sensor_manager.hpp" - -#include <boost/thread.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/format.hpp> - -#include <cstring> -#include <uhd/exception.hpp> -#include <uhd/utils/byteswap.hpp> - -namespace uhd { namespace usrp { namespace e300 { - -class e300_sensor_proxy : public e300_sensor_manager -{ -public: - e300_sensor_proxy( - uhd::transport::zero_copy_if::sptr xport) : _xport(xport) - { - } - - std::vector<std::string> get_sensors() - { - return boost::assign::list_of("temp")("ref_locked"); - } - - uhd::sensor_value_t get_sensor(const std::string &key) - { - if (key == "temp") - return get_mb_temp(); - else if (key == "ref_locked") - return get_ref_lock(); - else - throw uhd::lookup_error( - str(boost::format("Invalid sensor %s requested.") % key)); - } - - uhd::sensor_value_t get_mb_temp(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<uint32_t>(ZYNQ_TEMP); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<uint32_t>(transaction.which) == ZYNQ_TEMP); - // TODO: Use proper serialization here ... - return sensor_value_t( - "temp", - e300_sensor_manager::unpack_float_from_uint32_t( - uhd::ntohx(transaction.value)), - "C"); - } - - uhd::sensor_value_t get_ref_lock(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<uint32_t>(REF_LOCK); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<uint32_t>(transaction.which) == REF_LOCK); - // TODO: Use proper serialization here ... - return sensor_value_t("Ref", (uhd::ntohx(transaction.value) > 0), "locked", "unlocked"); - } - -private: - uhd::transport::zero_copy_if::sptr _xport; - boost::mutex _mutex; -}; - -}}} // namespace - -using namespace uhd::usrp::e300; - -e300_sensor_manager::sptr e300_sensor_manager::make_proxy( - uhd::transport::zero_copy_if::sptr xport) -{ - return sptr(new e300_sensor_proxy(xport)); -} - -#ifdef E300_NATIVE -#include "e300_fifo_config.hpp" - -namespace uhd { namespace usrp { namespace e300 { - -static const std::string E300_TEMP_SYSFS = "iio:device0"; - -class e300_sensor_local : public e300_sensor_manager -{ -public: - e300_sensor_local(global_regs::sptr global_regs) : - _global_regs(global_regs) - { - } - - std::vector<std::string> get_sensors() - { - return boost::assign::list_of("temp")("ref_locked"); - } - - uhd::sensor_value_t get_sensor(const std::string &key) - { - if (key == "temp") - return get_mb_temp(); - else if (key == "ref_locked") - return get_ref_lock(); - else - throw uhd::lookup_error( - str(boost::format("Invalid sensor %s requested.") % key)); - } - - uhd::sensor_value_t get_mb_temp(void) - { - double scale = std::stod( - e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_scale")); - unsigned long raw = std::stoul( - e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_raw")); - unsigned long offset = std::stoul( - e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_offset")); - return sensor_value_t("temp", (raw + offset) * scale / 1000, "C"); - } - - uhd::sensor_value_t get_ref_lock(void) - { - //PPSLOOP_LOCKED_MASK is asserted in the following cases: - //- (Time source = GPS or External) AND (Loop is locked and is in fine adj mode) - //- Time source is Internal - static const uint32_t PPSLOOP_LOCKED_MASK = 0x04; - static const uint32_t REFPLL_LOCKED_MASK = 0x20; - - const uint32_t status = - _global_regs->peek32(global_regs::RB32_CORE_MISC); - bool ref_locked = (status & PPSLOOP_LOCKED_MASK) && (status & REFPLL_LOCKED_MASK); - - return sensor_value_t("Ref", ref_locked, "locked", "unlocked"); - } - -private: - global_regs::sptr _global_regs; -}; -}}} - -using namespace uhd::usrp::e300; -e300_sensor_manager::sptr e300_sensor_manager::make_local( - global_regs::sptr global_regs) -{ - return sptr(new e300_sensor_local(global_regs)); -} - -#else -using namespace uhd::usrp::e300; -e300_sensor_manager::sptr e300_sensor_manager::make_local( - global_regs::sptr) -{ - throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE"); -} -#endif diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp deleted file mode 100644 index 8ed09b1eb..000000000 --- a/host/lib/usrp/e300/e300_sensor_manager.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright 2014-2015 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include <uhd/utils/noncopyable.hpp> -#include <stdint.h> - -#include <uhd/transport/zero_copy.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include "e300_global_regs.hpp" - -#ifndef INCLUDED_E300_SENSOR_MANAGER_HPP -#define INCLUDED_E300_SENSOR_MANAGER_HPP - -namespace uhd { namespace usrp { namespace e300 { - -struct sensor_transaction_t { - uint32_t which; - union { - uint32_t value; - uint32_t value64; - }; -}; - - - -enum sensor {ZYNQ_TEMP=0, REF_LOCK=4}; - -class e300_sensor_manager : uhd::noncopyable -{ -public: - typedef boost::shared_ptr<e300_sensor_manager> sptr; - - virtual ~e300_sensor_manager() {}; - - virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0; - virtual std::vector<std::string> get_sensors(void) = 0; - - virtual uhd::sensor_value_t get_mb_temp(void) = 0; - virtual uhd::sensor_value_t get_ref_lock(void) = 0; - - - static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport); - static sptr make_local(global_regs::sptr global_regs); - - // Note: This is a hack - static uint32_t pack_float_in_uint32_t(const float &v) - { - const uint32_t *cast = reinterpret_cast<const uint32_t*>(&v); - return *cast; - } - - static float unpack_float_from_uint32_t(const uint32_t &v) - { - const float *cast = reinterpret_cast<const float*>(&v); - return *cast; - } -}; - - -}}} // namespace - -#endif // INCLUDED_E300_SENSOR_MANAGER_HPP diff --git a/host/lib/usrp/e300/e300_spi.cpp b/host/lib/usrp/e300/e300_spi.cpp deleted file mode 100644 index e0f25c22f..000000000 --- a/host/lib/usrp/e300/e300_spi.cpp +++ /dev/null @@ -1,119 +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/config.hpp> -#include <uhd/exception.hpp> -#include "e300_spi.hpp" - -#ifdef E300_NATIVE -#include <boost/thread.hpp> -#include <boost/format.hpp> - -#include <fcntl.h> -#include <sys/ioctl.h> -#include <linux/types.h> -#include <linux/spi/spidev.h> - -namespace uhd { namespace usrp { namespace e300 { - -class spidev_impl : public spi -{ -public: - - spidev_impl(const std::string &device) - : _mode(SPI_CPHA), - _speed(2000000), - _bits(8), - _delay(0) - { - int ret; - _fd = open(device.c_str(), O_RDWR); - if (_fd < 0) - throw uhd::runtime_error(str(boost::format("Could not open spidev device %s") % device)); - - ret = ioctl(_fd, SPI_IOC_WR_MODE, &_mode); - if (ret == -1) - throw uhd::runtime_error("Could not set spidev mode"); - - ret = ioctl(_fd, SPI_IOC_RD_MODE, &_mode); - if (ret == -1) - throw uhd::runtime_error("Could not get spidev mode"); - - ret = ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits); - if (ret == -1) - throw uhd::runtime_error("Could not set spidev bits per word"); - - ret = ioctl(_fd, SPI_IOC_RD_BITS_PER_WORD, &_bits); - if (ret == -1) - throw uhd::runtime_error("Could not get spidev bits per word"); - - ret = ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_speed); - if (ret == -1) - throw uhd::runtime_error("Could not set spidev max speed"); - - ret = ioctl(_fd, SPI_IOC_RD_MAX_SPEED_HZ, &_speed); - if (ret == -1) - throw uhd::runtime_error("Could not get spidev max speed"); - } - - virtual ~spidev_impl() - { - close(_fd); - } - - uint32_t transact_spi(int, const uhd::spi_config_t &, - uint32_t data, size_t num_bits, - bool) - { - int ret(0); - struct spi_ioc_transfer tr; - - uint8_t *tx_data = reinterpret_cast<uint8_t *>(&data); - - - UHD_ASSERT_THROW(num_bits == 24); - uint8_t tx[] = {tx_data[2], tx_data[1], tx_data[0]}; - - uint8_t rx[3]; - tr.tx_buf = (unsigned long) &tx[0]; - tr.rx_buf = (unsigned long) &rx[0]; - tr.len = num_bits >> 3; - tr.bits_per_word = _bits; - tr.tx_nbits = 1; - tr.rx_nbits = 1; - tr.speed_hz = _speed; - tr.delay_usecs = _delay; - - ret = ioctl(_fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - throw uhd::runtime_error("Could not send spidev message"); - - return rx[2]; - } - -private: - int _fd; - uint8_t _mode; - uint32_t _speed; - uint8_t _bits; - uint16_t _delay; -}; - -spi::sptr spi::make(const std::string &device) -{ - return spi::sptr(new spidev_impl(device)); -} -}}}; -#else -namespace uhd { namespace usrp { namespace e300 { - -spi::sptr spi::make(const std::string &) -{ - throw uhd::assertion_error("spi::make() !E300_NATIVE"); -} -}}}; -#endif //E300_NATIVE diff --git a/host/lib/usrp/e300/e300_spi.hpp b/host/lib/usrp/e300/e300_spi.hpp deleted file mode 100644 index 0341ae9c4..000000000 --- a/host/lib/usrp/e300/e300_spi.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_E300_SPI_HPP -#define INCLUDED_E300_SPI_HPP - -#include <uhd/types/serial.hpp> - -namespace uhd { namespace usrp { namespace e300 { - -class spi : public virtual uhd::spi_iface -{ -public: - typedef boost::shared_ptr<spi> sptr; - static sptr make(const std::string &device); -}; - -}}}; - -#endif /* INCLUDED_E300_SPI_HPP */ diff --git a/host/lib/usrp/e300/e300_sysfs_hooks.cpp b/host/lib/usrp/e300/e300_sysfs_hooks.cpp deleted file mode 100644 index 81dcff0cb..000000000 --- a/host/lib/usrp/e300/e300_sysfs_hooks.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifdef E300_NATIVE - -#include <cstdio> -#include <cstdlib> -#include <string> -#include <fcntl.h> -#include <unistd.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mman.h> - -#include <libudev.h> - -#include <boost/format.hpp> -#include <boost/lexical_cast.hpp> - -#include <uhd/utils/log.hpp> -#include <uhd/exception.hpp> - -static const std::string E300_AXI_FPGA_SYSFS = "40000000.axi-fpga"; -static const std::string E300_XDEV_SYSFS = "f8007000.ps7-dev-cfg"; - -std::string e300_get_sysfs_attr(const std::string &node, const std::string &attr) -{ - udev *udev; - udev_enumerate *enumerate; - udev_list_entry *devices, *dev_list_entry; - udev_device *dev; - std::string retstring; - - udev = udev_new(); - - if (!udev) { - throw uhd::lookup_error("Failed to get udev handle."); - } - - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_sysname(enumerate, node.c_str()); - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - - udev_list_entry_foreach(dev_list_entry, devices) - { - const char *path; - - path = udev_list_entry_get_name(dev_list_entry); - dev = udev_device_new_from_syspath(udev, path); - - retstring = udev_device_get_sysattr_value(dev, attr.c_str()); - - udev_device_unref(dev); - - if (retstring.size()) - break; - } - - udev_enumerate_unref(enumerate); - udev_unref(udev); - - return retstring; -} - -static bool e300_fpga_loaded_successfully(void) -{ - return boost::lexical_cast<bool>(e300_get_sysfs_attr(E300_XDEV_SYSFS, "prog_done")); -} - -#include "e300_fifo_config.hpp" -#include <uhd/exception.hpp> - -e300_fifo_config_t e300_read_sysfs(void) -{ - - if (not e300_fpga_loaded_successfully()) - { - throw uhd::runtime_error("E300 FPGA load failed!"); - } - - e300_fifo_config_t config; - - config.buff_length = std::stoul( - e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "buffer_length")); - config.ctrl_length = std::stoul( - e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "control_length")); - config.phys_addr = std::stoul( - e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "phys_addr")); - - return config; -} - -#else //E300_NATIVE - -#include "e300_fifo_config.hpp" -#include <uhd/exception.hpp> - -e300_fifo_config_t e300_read_sysfs(void) -{ - throw uhd::assertion_error("e300_read_sysfs() !E300_NATIVE"); -} - -std::string e300_get_sysfs_attr(const std::string &, const std::string &) -{ - throw uhd::assertion_error("e300_sysfs_attr() !E300_NATIVE"); -} - -#endif //E300_NATIVE diff --git a/host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp b/host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp deleted file mode 100644 index 787faf6df..000000000 --- a/host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp +++ /dev/null @@ -1,770 +0,0 @@ -// -// Copyright 2015-2016 Ettus Research -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include "e3xx_radio_ctrl_impl.hpp" -#include "e300_defaults.hpp" -#include "e300_regs.hpp" -#include <boost/make_shared.hpp> -#include <uhd/usrp/dboard_iface.hpp> -#include <uhd/rfnoc/node_ctrl_base.hpp> -#include <uhd/utils/log.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/make_shared.hpp> - -using namespace uhd; -using namespace uhd::rfnoc; -using namespace uhd::usrp::e300; -using uhd::usrp::dboard_iface; - -//! mapping of frontend to radio perif index -static const size_t FE0 = 1; -static const size_t FE1 = 0; - -/**************************************************************************** - * Structors - ***************************************************************************/ -UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(e3xx_radio_ctrl) -{ - UHD_RFNOC_BLOCK_TRACE() << "e3xx_radio_ctrl_impl::ctor() " << std::endl; - - //////////////////////////////////////////////////////////////////// - // Set up peripherals - //////////////////////////////////////////////////////////////////// - for (size_t i = 0; i < _get_num_radios(); i++) { - if (i == 0) { - _spi = spi_core_3000::make(_get_ctrl(i), regs::sr_addr(regs::SPI), regs::RB_SPI); - _spi->set_divider(6); - } - _e3xx_perifs[i].atr = usrp::gpio_atr::gpio_atr_3000::make_write_only(_get_ctrl(i), regs::sr_addr(regs::GPIO)); - _e3xx_perifs[i].leds = usrp::gpio_atr::gpio_atr_3000::make_write_only(_get_ctrl(i), regs::sr_addr(regs::LEDS)); - _e3xx_perifs[i].leds->set_atr_mode(usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); - } - - //////////////////////////////////////////////////////////////////// - // Time source - //////////////////////////////////////////////////////////////////// - _tree->create<std::string>("time_source/value") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::_update_time_source, this, _1)) - .set(DEFAULT_TIME_SRC); -#ifdef E300_GPSD - static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo"); -#else - static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external"); -#endif - _tree->create<std::vector<std::string> >("time_source/options").set(time_sources); - - //////////////////////////////////////////////////////////////////// - // create RF frontend interfacing - //////////////////////////////////////////////////////////////////// - { - const fs_path codec_path = fs_path("rx_codecs") / "A"; - _tree->create<std::string>(codec_path / "name").set("E3x0 RX dual ADC"); - _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend - } - { - const fs_path codec_path = fs_path("tx_codecs") / "A"; - _tree->create<std::string>(codec_path / "name").set("E3x0 TX dual DAC"); - _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend - } - - //////////////////////////////////////////////////////////////////// - // internal gpios - //////////////////////////////////////////////////////////////////// - UHD_RFNOC_BLOCK_TRACE() << " Creating internal GPIOs..." << std::endl; - - usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio = usrp::gpio_atr::gpio_atr_3000::make( - get_ctrl_iface(0), - regs::sr_addr(regs::FP_GPIO), - regs::RB_FP_GPIO - ); - for (const auto& attr : usrp::gpio_atr::gpio_attr_map) { - switch (attr.first) { - case usrp::gpio_atr::GPIO_SRC: - _tree->create<std::vector<std::string>>(fs_path("gpio") / "INT0" / attr.second) - .set(std::vector<std::string>(32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber([this](const std::vector<std::string>&){ - throw uhd::runtime_error("This device does not support setting the GPIO_SRC attribute."); - }); - break; - case usrp::gpio_atr::GPIO_CTRL: - case usrp::gpio_atr::GPIO_DDR: - _tree->create<std::vector<std::string>>(fs_path("gpio") / "INT0" / attr.second) - .set(std::vector<std::string>(32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber([this, fp_gpio, attr](const std::vector<std::string> str_val){ - uint32_t val = 0; - for(size_t i = 0 ; i < str_val.size() ; i++){ - val += usrp::gpio_atr::gpio_attr_value_pair.at(attr.second).at(str_val[i])<<i; - } - fp_gpio->set_gpio_attr(attr.first, val); - }); - break; - case usrp::gpio_atr::GPIO_READBACK: - _tree->create<uint8_t>(fs_path("gpio") / "INT0" / "READBACK") - .set_publisher([this, fp_gpio](){ - return fp_gpio->read_gpio(); - }); - break; - default: - _tree->create<uint32_t>(fs_path("gpio") / "INT0" / attr.second) - .set(0) - .add_coerced_subscriber([this, fp_gpio, attr](const uint32_t val){ - fp_gpio->set_gpio_attr(attr.first, val); - }); - } - } - - //////////////////////////////////////////////////////////////////// - // Tick rate - //////////////////////////////////////////////////////////////////// - UHD_RFNOC_BLOCK_TRACE() << " Setting tick rate..." << std::endl; - _tree->access<double>("tick_rate") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rate, this, _1)) - .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::get_rate, this)) - ; -} - -e3xx_radio_ctrl_impl::~e3xx_radio_ctrl_impl() -{ - const std::string _radio_slot = "A"; - // Tear down our part of the tree: - _tree->remove(fs_path("rx_codecs" / _radio_slot)); - _tree->remove(fs_path("tx_codecs" / _radio_slot)); - for (size_t i = 0; i < _get_num_radios(); i++) { - if (_tree->exists(fs_path("tx_dsps") / i)) { - _tree->remove(fs_path("tx_dsps") / i); - } - if (_tree->exists(fs_path("rx_dsps") / i)) { - _tree->remove(fs_path("rx_dsps") / i); - } - } - for (const auto attr : usrp::gpio_atr::gpio_attr_map) { - const auto gpio_fs_path = fs_path("gpio") / "INT0" / attr.second; - if (_tree->exists(gpio_fs_path)) { - _tree->remove(gpio_fs_path); - } - } -} - -/**************************************************************************** - * API calls - ***************************************************************************/ -double e3xx_radio_ctrl_impl::set_rate(double rate) -{ - //UHD_LOGGER_DEBUG("E300") << "Setting SPI divider to " << ceil(rate/AD9361_SPI_RATE) << "\n"; - //_spi->set_divider(ceil(rate/AD9361_SPI_RATE)); // ceil() to prevent less than 1 rounding to 0 - UHD_LOGGER_DEBUG("E300") << "Asking for clock rate " << rate/1e6 << " MHz\n"; - double actual_tick_rate = _codec_ctrl->set_clock_rate(rate); - UHD_LOGGER_DEBUG("E300") << "Actually got clock rate " << actual_tick_rate/1e6 << " MHz\n"; - - actual_tick_rate = radio_ctrl_impl::set_rate(actual_tick_rate); - - if (not check_radio_config()) { - throw std::runtime_error(str( - boost::format("[%s]: Invalid radio configuration.") - % unique_id() - )); - } - - return actual_tick_rate; -} - -/*! Select TX antenna \p for channel \p chan. - */ -void e3xx_radio_ctrl_impl::set_tx_antenna(const std::string &ant, const size_t chan) -{ - std::lock_guard<std::mutex> lock(_mutex); - if (ant != "TX/RX") - throw uhd::value_error("Unknown TX antenna option: " + ant); - - radio_ctrl_impl::set_tx_antenna(ant, chan); -} - -/*! Select RX antenna \p for channel \p chan. - */ -void e3xx_radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t chan) -{ - std::lock_guard<std::mutex> lock(_mutex); - if (ant != "TX/RX" and ant != "RX2") - throw uhd::value_error("Unknown RX antenna option: " + ant); - - radio_ctrl_impl::set_rx_antenna(ant, chan); - this->_update_atrs(); - this->_update_atr_leds(_e3xx_perifs[chan].leds, ant); -} - -double e3xx_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t) -{ - return _tree->access<double>(fs_path("dboards/A/tx_frontends/A/freq/value")).set(freq).get(); -} - -double e3xx_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t) -{ - return _tree->access<double>(fs_path("dboards/A/rx_frontends/A/freq/value")).set(freq).get(); -} - -double e3xx_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) -{ - const std::string fe_side = (chan == 0) ? "A" : "B"; - double new_gain = _tree->access<double>(fs_path("dboards/A/tx_frontends/" + fe_side + "/gains/PGA/value")).set(gain).get(); - return radio_ctrl_impl::set_tx_gain(new_gain, chan); -} - -double e3xx_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) -{ - const std::string fe_side = (chan == 0) ? "A" : "B"; - double new_gain = _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/gains/PGA/value")).set(gain).get(); - return radio_ctrl_impl::set_rx_gain(new_gain, chan); -} - -double e3xx_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) -{ - const std::string fe_side = (chan == 0) ? "A" : "B"; - double new_bw = _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/bandwidth/value")).set(bandwidth).get(); - return radio_ctrl_impl::set_rx_bandwidth(new_bw, chan); -} - -double e3xx_radio_ctrl_impl::get_tx_gain(const size_t chan) -{ - const std::string fe_side = (chan == 0) ? "A" : "B"; - return _tree->access<double>(fs_path("dboards/A/tx_frontends/" + fe_side + "/gains/PGA/value")).get(); -} - -double e3xx_radio_ctrl_impl::get_rx_gain(const size_t chan) -{ - const std::string fe_side = (chan == 0) ? "A" : "B"; - return _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/gains/PGA/value")).get(); -} - -double e3xx_radio_ctrl_impl::get_rx_bandwidth(const size_t chan) -{ - const std::string fe_side = (chan == 0) ? "A" : "B"; - return _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/bandwidth/value")).get(); -} - -std::vector<std::string> e3xx_radio_ctrl_impl::get_gpio_banks() const -{ - std::vector<std::string> banks = boost::assign::list_of("INT0"); - return banks; -} - -void e3xx_radio_ctrl_impl::set_gpio_attr( - const std::string &bank, - const std::string &attr, - const uint32_t value, - const uint32_t mask -) { - if (bank == "INT0") { - const uint32_t current = _tree->access<uint32_t>(fs_path("gpio") / bank / attr).get(); - const uint32_t new_value = (current & ~mask) | (value & mask); - _tree->access<uint32_t>(fs_path("gpio") / bank / attr).set(new_value); - return; - } -} - -uint32_t e3xx_radio_ctrl_impl::get_gpio_attr( - const std::string &bank, - const std::string &attr -) { - if (bank == "INT0") { - return uint32_t(_tree->access<uint64_t>(fs_path("gpio") / bank / attr).get()); - } - return 0; -} - -size_t e3xx_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const direction_t) -{ - return (fe == "A") ? 0 : 1; -} - -std::string e3xx_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const direction_t) -{ - return (chan == 0) ? "A" : "B"; -} - -/**************************************************************************** - * Radio control and setup - ***************************************************************************/ -void e3xx_radio_ctrl_impl::setup_radio(uhd::usrp::ad9361_ctrl::sptr safe_codec_ctrl) -{ - { - std::lock_guard<std::mutex> lock(_mutex); - if (_codec_ctrl) { - throw std::runtime_error("Attempting to set up radio twice!"); - } - } - - //////////////////////////////////////////////////////////////////// - // Create timed interface - //////////////////////////////////////////////////////////////////// - _codec_ctrl = safe_codec_ctrl; - _codec_mgr = uhd::usrp::ad936x_manager::make(_codec_ctrl, _get_num_radios()); - - //////////////////////////////////////////////////////////////////// - // setup radios - //////////////////////////////////////////////////////////////////// - for (size_t chan = 0; chan < _get_num_radios(); chan++) { - _setup_radio_channel(chan); - } - // Loopback test - for (size_t chan = 0; chan < _get_num_radios(); chan++) { - _codec_mgr->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); - } - ); - } - - this->_update_enables(); -} - -void e3xx_radio_ctrl_impl::_setup_radio_channel(const size_t chan) -{ - const fs_path rx_dsp_path = fs_path("rx_dsps") / chan; - _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") - .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::issue_stream_cmd, this, _1, chan)); - - //////////////////////////////////////////////////////////////////// - // add some dummy nodes on the prop tree (FIXME remove these) - //////////////////////////////////////////////////////////////////// - const fs_path tx_dsp_path = fs_path("tx_dsps") / chan; - _tree->create<double>(tx_dsp_path / "freq/value").set(0.0); - _tree->create<meta_range_t>(tx_dsp_path / "freq/range").set(meta_range_t(0.0, 0.0, 0.0)); - _tree->create<double>(rx_dsp_path / "freq/value").set(0.0); - _tree->create<meta_range_t>(rx_dsp_path / "freq/range").set(meta_range_t(0.0, 0.0, 0.0)); - _tree->create<double>(tx_dsp_path / "rate/value") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rate, this, _1)) - .set_publisher(boost::bind(&radio_ctrl_impl::get_rate, this)) - ; - _tree->create<double>(rx_dsp_path / "rate/value") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rate, this, _1)) - .set_publisher(boost::bind(&radio_ctrl_impl::get_rate, this)) - ; - - //////////////////////////////////////////////////////////////////// - // create RF frontend interfacing - //////////////////////////////////////////////////////////////////// - static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); - BOOST_FOREACH(direction_t dir, dirs) { - const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx"; - const std::string key = boost::to_upper_copy(x) + std::string(((chan == FE0)? "1" : "2")); - const fs_path rf_fe_path - = fs_path("dboards") / "A" / (x + "_frontends") / ((chan == 0) ? "A" : "B"); - - // This will connect all the AD936x-specific items - _codec_mgr->populate_frontend_subtree( - _tree->subtree(rf_fe_path), key, dir - ); - - // This will connect all the e3xx_radio_ctrl-specific items - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") - .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION)) - ; - const double freq = _tree->access<double>(rf_fe_path / "freq" / "value") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::_update_fe_lo_freq, this, key, _1)).get() - ; - // Set frequency in parent (to be used to update ATR values later) - if (dir == RX_DIRECTION) { - radio_ctrl_impl::set_rx_frequency(freq, chan); - } else { - radio_ctrl_impl::set_tx_frequency(freq, chan); - } - - // Antenna Setup - if (dir == RX_DIRECTION) { - static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); - _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); - _tree->create<std::string>(rf_fe_path / "antenna" / "value") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rx_antenna, this, _1, chan)) - .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::get_rx_antenna, this, chan)); - // Set default in parent (to be used to update ATR values later) - radio_ctrl_impl::set_rx_antenna("RX2", chan); - // Set up LEDs for default antenna - _update_atr_leds(_e3xx_perifs[chan].leds, "RX2"); - } - else if (dir == TX_DIRECTION) { - static const std::vector<std::string> ants(1, "TX/RX"); - _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); - _tree->create<std::string>(rf_fe_path / "antenna" / "value") - .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_tx_antenna, this, _1, chan)) - .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::get_tx_antenna, this, chan)) - .set("TX/RX"); - } - } -} - -void e3xx_radio_ctrl_impl::_reset_radio(void) -{ - std::lock_guard<std::mutex> lock(_mutex); - _misc.radio_rst = 1; - _update_gpio_state(); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - _misc.radio_rst = 0; - _update_gpio_state(); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); -} - -/**************************************************************************** - * Helpers - ***************************************************************************/ -bool e3xx_radio_ctrl_impl::check_radio_config() -{ - 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); - _enforce_tick_rate_limits( - std::max(num_rx, num_tx), - get_tick_rate() - ); - - this->_update_enables(); - return true; -} - -void e3xx_radio_ctrl_impl::_enforce_tick_rate_limits( - const size_t chan_count, - const double tick_rate -) { - const size_t max_chans = 2; - if (chan_count > max_chans) { - throw uhd::value_error(boost::str( - boost::format("cannot not setup %d channels per direction (maximum is %d)") - % chan_count - % max_chans - )); - } else { - const double max_tick_rate = uhd::usrp::ad9361_device_t::AD9361_MAX_CLOCK_RATE / ((chan_count <= 1) ? 1 : 2); - if (tick_rate - max_tick_rate >= 1.0) - { - throw uhd::value_error(boost::str( - boost::format("current master clock rate (%.6f MHz) exceeds maximum possible master clock rate (%.6f MHz) when using %d channels") - % (tick_rate/1e6) - % (max_tick_rate/1e6) - % chan_count - )); - } - // TODO minimum rate check - } -} - -/**************************************************************************** - * Peripheral controls - ***************************************************************************/ -void e3xx_radio_ctrl_impl::_update_fe_lo_freq(const std::string &fe, const double freq) -{ - if (fe[0] == 'R') { - for (size_t i = 0; i < _get_num_radios(); i++) { - radio_ctrl_impl::set_rx_frequency(freq, i); - } - } - if (fe[0] == 'T') { - for (size_t i = 0; i < _get_num_radios(); i++) { - radio_ctrl_impl::set_tx_frequency(freq, i); - } - } - this->_update_atrs(); -} - -void e3xx_radio_ctrl_impl::_update_atrs(void) -{ - for (size_t instance = 0; instance < _get_num_radios(); instance++) - { - // if we're not ready, no point ... - if (not _e3xx_perifs[instance].atr) - return; - - const bool rx_ant_rx2 = get_rx_antenna(instance) == "RX2"; - const double rx_freq = get_rx_frequency(instance); - const double tx_freq = get_tx_frequency(instance); - const bool rx_low_band = rx_freq < 2.6e9; - const bool tx_low_band = tx_freq < 2940.0e6; - - // VCRX - uint32_t vcrx_v1_rxing = 1; - uint32_t vcrx_v2_rxing = 0; - uint32_t vcrx_v1_txing = 1; - uint32_t vcrx_v2_txing = 0; - - if (rx_low_band) { - vcrx_v1_rxing = rx_ant_rx2 ? 0 : 1; - vcrx_v2_rxing = rx_ant_rx2 ? 1 : 0; - vcrx_v1_txing = 0; - vcrx_v2_txing = 1; - } else { - vcrx_v1_rxing = rx_ant_rx2 ? 1 : 0; - vcrx_v2_rxing = rx_ant_rx2 ? 0 : 1; - vcrx_v1_txing = 1; - vcrx_v2_txing = 0; - } - - // VCTX - uint32_t vctxrx_v1_rxing = 0; - uint32_t vctxrx_v2_rxing = 1; - uint32_t vctxrx_v1_txing = 0; - uint32_t vctxrx_v2_txing = 1; - - if (tx_low_band) { - vctxrx_v1_rxing = rx_ant_rx2 ? 1 : 0; - vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1; - vctxrx_v1_txing = 1; - vctxrx_v2_txing = 0; - } else { - vctxrx_v1_rxing = rx_ant_rx2 ? 1 : 0; - vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1; - vctxrx_v1_txing = 1; - vctxrx_v2_txing = 1; - } - - //swapped for routing reasons, reswap it here - if (instance == 1) { - std::swap(vctxrx_v1_rxing, vctxrx_v2_rxing); - std::swap(vctxrx_v1_txing, vctxrx_v2_txing); - } - - uint32_t tx_enable_a = (!tx_low_band) ? 1 : 0; - uint32_t tx_enable_b = (tx_low_band) ? 1 : 0; - - /* Set RX / TX band selects */ - uint32_t rx_band_select_a = 0; - uint32_t rx_band_select_b = 0; - uint32_t rx_band_select_c = 0; - uint32_t tx_band_select = 0; - - if (instance == 0) { - // RX - if (rx_freq < 450e6) { - rx_band_select_a = 5; // 3'b101 - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 1; // 2'b01 - } else if (rx_freq < 700e6) { - rx_band_select_a = 3; // 3'b011 - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 3; // 2'b11 - } else if (rx_freq < 1200e6) { - rx_band_select_a = 1; // 3'b001 - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 2; // 2'b10 - } else if (rx_freq < 1800e6) { - rx_band_select_a = 0; // 3'b000 - rx_band_select_b = 1; // 2'b01 - rx_band_select_c = 0; // 2'bXX -- Don't care - } else if (rx_freq < 2350e6){ - rx_band_select_a = 2; // 3'b010 - rx_band_select_b = 3; // 2'b11 - rx_band_select_c = 0; // 2'bXX -- Don't care - } else if (rx_freq < 2600e6){ - rx_band_select_a = 4; // 3'b100 - rx_band_select_b = 2; // 2'b10 - rx_band_select_c = 0; // 2'bXX -- Don't care - } else { // >= 2600e6 - rx_band_select_a = 5; // 3'bXX -- Don't care - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 1; // 2'bXX -- Don't care - } - } else if (instance == 1) { - if (rx_freq < 450e6) { - rx_band_select_a = 4; // 3'b100 - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 2; // 2'b10 - } else if (rx_freq < 700e6) { - rx_band_select_a = 2; // 3'b010 - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 3; // 2'b11 - } else if (rx_freq < 1200e6) { - rx_band_select_a = 0; // 3'b000 - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 1; // 2'b01 - } else if (rx_freq < 1800e6) { - rx_band_select_a = 1; // 3'b001 - rx_band_select_b = 2; // 2'b10 - rx_band_select_c = 0; // 2'bXX -- Don't care - } else if (rx_freq < 2350e6){ - rx_band_select_a = 3; // 3'b011 - rx_band_select_b = 3; // 2'b11 - rx_band_select_c = 0; // 2'bXX -- Don't care - } else if (rx_freq < 2600e6){ - rx_band_select_a = 5; // 3'b101 - rx_band_select_b = 1; // 2'b01 - rx_band_select_c = 0; // 2'bXX -- Don't care - } else { // >= 2600e6 - rx_band_select_a = 5; // 3'bXX -- Don't care - rx_band_select_b = 0; // 2'bXX -- Don't care - rx_band_select_c = 1; // 2'bXX -- Don't care - } - } else { - UHD_THROW_INVALID_CODE_PATH(); - } - - // TX band selects are the same for both radio frontends - if (tx_freq < 117.7e6) - tx_band_select = 7; // 3'b111 - else if (tx_freq < 178.2e6) - tx_band_select = 6; // 3'b110 - else if (tx_freq < 284.3e6) - tx_band_select = 5; // 3'b101 - else if (tx_freq < 453.7e6) - tx_band_select = 4; // 3'b100 - else if (tx_freq < 723.8e6) - tx_band_select = 3; // 3'b011 - else if (tx_freq < 1154.9e6) - tx_band_select = 2; // 3'b010 - else if (tx_freq < 1842.6e6) - tx_band_select = 1; // 3'b001 - else if (tx_freq < 2940.0e6) - tx_band_select = 0; // 3'b000 - else // > 2940.0e6 - tx_band_select = 7; // 3'bXXX -- Don't care, set to lowest band - - const uint32_t rx_selects = 0 - | (vcrx_v1_rxing << VCRX_V1) - | (vcrx_v2_rxing << VCRX_V2) - | (vctxrx_v1_rxing << VCTXRX_V1) - | (vctxrx_v2_rxing << VCTXRX_V2) - ; - const uint32_t tx_selects = 0 - | (vcrx_v1_txing << VCRX_V1) - | (vcrx_v2_txing << VCRX_V2) - | (vctxrx_v1_txing << VCTXRX_V1) - | (vctxrx_v2_txing << VCTXRX_V2) - ; - const uint32_t tx_enables = 0 - | (tx_enable_a << TX_ENABLEA) - | (tx_enable_b << TX_ENABLEB) - ; - const uint32_t rxtx_band_selects = 0 - | (rx_band_select_a << RX_BANDSEL) - | (rx_band_select_b << RXB_BANDSEL) - | (rx_band_select_c << RXC_BANDSEL) - | (tx_band_select << TX_BANDSEL) - ; - - // Form register values; - uint32_t oo_reg = rx_selects | rxtx_band_selects; - uint32_t rx_reg = rx_selects | rxtx_band_selects; - uint32_t tx_reg = tx_selects | tx_enables | rxtx_band_selects; - uint32_t fd_reg = tx_selects | tx_enables | rxtx_band_selects; //tx selects dominate in fd mode - - //add tx enables based on fe enable - tx_reg |= tx_enables; - fd_reg |= tx_enables; - - usrp::gpio_atr::gpio_atr_3000::sptr atr = _e3xx_perifs[instance].atr; - atr->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, oo_reg); - atr->set_atr_reg(usrp::gpio_atr::ATR_REG_RX_ONLY, rx_reg); - atr->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_reg); - atr->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, fd_reg); - } -} - -void e3xx_radio_ctrl_impl::_update_atr_leds(usrp::gpio_atr::gpio_atr_3000::sptr leds, const std::string &rx_ant) -{ - const bool is_txrx = (rx_ant == "TX/RX"); - const int rx_led = (1 << 2); - const int tx_led = (1 << 1); - const int txrx_led = (1 << 0); - using namespace uhd::usrp::gpio_atr; - leds->set_atr_reg(ATR_REG_IDLE, 0); - leds->set_atr_reg(ATR_REG_RX_ONLY, is_txrx ? txrx_led : rx_led); - leds->set_atr_reg(ATR_REG_TX_ONLY, tx_led); - leds->set_atr_reg(ATR_REG_FULL_DUPLEX, rx_led | tx_led); -} - -void e3xx_radio_ctrl_impl::_update_gpio_state(void) -{ - UHD_RFNOC_BLOCK_TRACE() << "e3xx_radio_ctrl_impl::_update_gpio_state() " << std::endl; - uint32_t misc_reg = 0 - | (_misc.pps_sel << gpio_t::PPS_SEL) - | (_misc.mimo << gpio_t::MIMO) - | (_misc.radio_rst << gpio_t::RADIO_RST); - _tree->access<uint32_t>("global_regs/misc").set(misc_reg); -} - -void e3xx_radio_ctrl_impl::_update_enables(void) -{ - std::lock_guard<std::mutex> lock(_mutex); - UHD_RFNOC_BLOCK_TRACE() << "e3xx_radio_ctrl_impl::_update_enables() " << std::endl; - if (not _codec_ctrl) { - UHD_LOGGER_WARNING("E300") << "Attempting to access CODEC controls before setting up the radios." << std::endl; - return; - } - - 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); - - const bool mimo = (num_rx == 2) or (num_tx == 2); - - // This currently doesn't work with GNU Radio, so leave it uncommented - //if ((num_tx + num_rx) == 3) - // throw uhd::runtime_error("e300: 2 RX 1 TX and 1 RX 2 TX configurations not possible"); - - //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 - _codec_ctrl->set_active_chains(false, false, true, false); - } else { - _codec_ctrl->set_active_chains( - _is_streamer_active(TX_DIRECTION, FE0), - _is_streamer_active(TX_DIRECTION, FE1), - _is_streamer_active(RX_DIRECTION, FE0), - _is_streamer_active(RX_DIRECTION, FE1) - ); - } - - // Set radio data direction register cleared due to reset - for (size_t instance = 0; instance < _get_num_radios(); instance++) - { - _e3xx_perifs[instance].atr->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, 0xFFFFFFFF); - } - - //figure out if mimo is enabled based on new state - _misc.mimo = (mimo) ? 1 : 0; - _update_gpio_state(); - - //atrs change based on enables - _update_atrs(); -} - -void e3xx_radio_ctrl_impl::_update_time_source(const std::string &source) -{ - std::lock_guard<std::mutex> lock(_mutex); - UHD_LOGGER_DEBUG("E300") << boost::format("Setting time source to %s") % source << std::endl; - if (source == "none" or source == "internal") { - _misc.pps_sel = global_regs::PPS_INT; -#ifdef E300_GPSD - } else if (source == "gpsdo") { - _misc.pps_sel = global_regs::PPS_GPS; -#endif - } else if (source == "external") { - _misc.pps_sel = global_regs::PPS_EXT; - } else { - throw uhd::key_error("update_time_source: unknown source: " + source); - } - _update_gpio_state(); -} - -uhd::sensor_value_t e3xx_radio_ctrl_impl::_get_fe_pll_lock(const bool is_tx) -{ - const uint32_t st = _tree->access<uint32_t>("global_regs/pll").get(); - const bool locked = is_tx ? ((st & 0x1) > 0) : ((st & 0x2) > 0); - return sensor_value_t("LO", locked, "locked", "unlocked"); -} - -/**************************************************************************** - * Register block - ***************************************************************************/ -UHD_RFNOC_BLOCK_REGISTER(e3xx_radio_ctrl, "E3XXRadio"); diff --git a/host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp b/host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp deleted file mode 100644 index 390b0929c..000000000 --- a/host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp +++ /dev/null @@ -1,149 +0,0 @@ -// -// Copyright 2015-2016 Ettus Research -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP -#define INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP - -#include "e300_global_regs.hpp" -#include <uhdlib/usrp/cores/spi_core_3000.hpp> -#include <uhdlib/usrp/common/ad9361_ctrl.hpp> -#include <uhdlib/usrp/common/ad936x_manager.hpp> -#include <uhdlib/rfnoc/radio_ctrl_impl.hpp> -#include <uhd/usrp/gpio_defs.hpp> - -namespace uhd { - namespace rfnoc { - -/*! \brief Provide access to an E3XX radio. - */ -class e3xx_radio_ctrl_impl : public radio_ctrl_impl -{ -public: - /************************************************************************ - * Structors - ***********************************************************************/ - UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(e3xx_radio_ctrl) - virtual ~e3xx_radio_ctrl_impl(); - - /************************************************************************ - * API calls - ***********************************************************************/ - double set_rate(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_gain(const double gain, const size_t chan); - double set_rx_gain(const double gain, const size_t chan); - double set_rx_bandwidth(const double bandwidth, const size_t chan); - - double get_tx_gain(const size_t chan); - double get_rx_gain(const size_t chan); - double get_rx_bandwidth(const size_t chan); - - std::vector<std::string> get_gpio_banks() const; - void set_gpio_attr(const std::string &bank, const std::string &attr, const uint32_t value, const uint32_t mask); - uint32_t get_gpio_attr(const std::string &bank, const std::string &attr); - - size_t get_chan_from_dboard_fe(const std::string &fe, const direction_t); - std::string get_dboard_fe_from_chan(const size_t chan, const direction_t); - - /************************************************************************ - * RFIC setup and control - ***********************************************************************/ - /*! Set up the radio. No API calls may be made before this one. - */ - void setup_radio(uhd::usrp::ad9361_ctrl::sptr safe_codec_ctrl); - -private: - void _setup_radio_channel(const size_t chan); - void _reset_radio(void); - -protected: - /************************************************************************ - * Helpers - ***********************************************************************/ - virtual bool check_radio_config(); - void _enforce_tick_rate_limits(const size_t chans, const double tick_rate); - -private: - /************************************************************************ - * Peripheral controls - ***********************************************************************/ - void _update_fe_lo_freq(const std::string &fe, const double freq); - void _update_atrs(void); - void _update_atr_leds(uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds, const std::string &rx_ant); - - void _update_gpio_state(void); - void _update_enables(void); - - void _update_time_source(const std::string &source); - - // get frontend lock sensor - uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); - - /************************************************************************ - * Internal GPIO control - ***********************************************************************/ - struct gpio_t - { - gpio_t() : pps_sel(uhd::usrp::e300::global_regs::PPS_INT), - mimo(0), radio_rst(0), tx_bandsels(0), - rx_bandsel_a(0), rx_bandsel_b(0), rx_bandsel_c(0) - {} - - uint32_t pps_sel; - uint32_t mimo; - uint32_t radio_rst; - - uint32_t tx_bandsels; - uint32_t rx_bandsel_a; - uint32_t rx_bandsel_b; - uint32_t rx_bandsel_c; - - static const size_t PPS_SEL = 0; - static const size_t MIMO = 2; - static const size_t RADIO_RST = 3; - static const size_t TX_BANDSEL = 4; - static const size_t RX_BANDSELA = 7; - static const size_t RX_BANDSELB = 13; - static const size_t RX_BANDSELC = 17; - }; - uint8_t _get_internal_gpio(uhd::usrp::gpio_atr::gpio_atr_3000::sptr); - -private: // members - struct e3xx_perifs_t - { - usrp::gpio_atr::gpio_atr_3000::sptr atr; - uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds; - }; - //! SPI to talk to the AD936x - spi_core_3000::sptr _spi; - //! One ATR per channel - std::map<size_t, e3xx_perifs_t> _e3xx_perifs; - //! AD936x controls - uhd::usrp::ad9361_ctrl::sptr _codec_ctrl; - uhd::usrp::ad936x_manager::sptr _codec_mgr; - gpio_t _misc; - -}; /* class radio_ctrl_impl */ - -}} /* namespace uhd::rfnoc */ - -#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */ diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 4e8d8e5f0..58eb842e1 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -18,13 +18,6 @@ set(util_runtime_sources uhd_cal_tx_iq_balance.cpp ) -find_package(UDev) -if(ENABLE_E300 AND NOT E300_FORCE_NETWORK) - if(UDEV_FOUND) - list(APPEND util_runtime_sources usrp_e3x0_network_mode.cpp) - endif(UDEV_FOUND) -endif(ENABLE_E300 AND NOT E300_FORCE_NETWORK) - #for each source: build an executable and install foreach(util_source ${util_runtime_sources}) get_filename_component(util_name ${util_source} NAME_WE) diff --git a/images/image_package_mapping.py b/images/image_package_mapping.py index 95a251dbc..da76dc989 100644 --- a/images/image_package_mapping.py +++ b/images/image_package_mapping.py @@ -8,21 +8,33 @@ Container for the list of image package targets, and the information about them """ PACKAGE_MAPPING = { - "e310": { + "e310_sg1": { "type": "e3xx", - "package_name": "e3xx_e310_fpga_default-g{}.zip", - "files": ["usrp_e310_fpga.bin", - "usrp_e310_fpga_sg3.bin", - "usrp_e3xx_fpga_idle.bin", - "usrp_e3xx_fpga_idle_sg3.bin", - "usrp_e310_fpga.bit", - "usrp_e310_fpga_sg3.bit", - "usrp_e3xx_fpga_idle.bit", - "usrp_e3xx_fpga_idle_sg3.bit", - "usrp_e310_fpga.rpt", - "usrp_e310_fpga_sg3.rpt", - "usrp_e3xx_fpga_idle.rpt", - "usrp_e3xx_fpga_idle_sg3.rpt"] + "package_name": "e3xx_e310_sg1_fpga_default-g{}.zip", + "files": ['usrp_e310_sg1_fpga.bit', + 'usrp_e310_sg1_fpga.bit.md5', + 'usrp_e310_sg1_fpga.dts', + 'usrp_e310_sg1_fpga.dts.md5', + 'usrp_e310_sg1_fpga.rpt', + 'usrp_e310_sg1_idle_fpga.bit', + 'usrp_e310_sg1_idle_fpga.bit.md5', + 'usrp_e310_sg1_idle_fpga.dts', + 'usrp_e310_sg1_idle_fpga.dts.md5', + 'usrp_e310_sg1_idle_fpga.rpt'] + }, + "e310_sg3": { + "type": "e3xx", + "package_name": "e3xx_e310_sg3_fpga_default-g{}.zip", + "files": ['usrp_e310_sg3_fpga.bit', + 'usrp_e310_sg3_fpga.bit.md5', + 'usrp_e310_sg3_fpga.dts', + 'usrp_e310_sg3_fpga.dts.md5', + 'usrp_e310_sg3_fpga.rpt', + 'usrp_e310_sg3_idle_fpga.bit', + 'usrp_e310_sg3_idle_fpga.bit.md5', + 'usrp_e310_sg3_idle_fpga.dts', + 'usrp_e310_sg3_idle_fpga.dts.md5', + 'usrp_e310_sg3_idle_fpga.rpt'] }, "e320": { "type": "e3xx", diff --git a/images/manifest.txt b/images/manifest.txt index fcf874362..7b5acf6c8 100644 --- a/images/manifest.txt +++ b/images/manifest.txt @@ -8,8 +8,8 @@ x3xx_x300_fpga_default fpga-4bc2c6f x3xx/fpga-4bc2c6f/x3xx_x300_f #dboard_ubx_cpld_default example_target # E-Series -e3xx_e310_fpga_default fpga-4bc2c6f e3xx/fpga-4bc2c6f/e3xx_e310_fpga_default-g4bc2c6f.zip be090f7cd187eb020e0cc8a8a7094b9691c4b7fa3dde82ca242030287a577475 -e3xx_e310_fpga_rfnoc fpga-d6a878b e3xx/fpga-d6a878b/e3xx_e310_fpga_rfnoc-gd6a878b.zip 5c9b89fb6293423644868c22e914de386a9af39ff031da6800a1cf39a90ea73b +e3xx_e310_sg1_fpga_default fpga-f52a643 e3xx/fpga-f52a643/e3xx_e310_sg1_fpga_default-gf52a643.zip 03450918a7c312d53926f3318ea91a57162c545ada4058b9e83a4e0efd4755a4 +e3xx_e310_sg3_fpga_default fpga-f52a643 e3xx/fpga-f52a643/e3xx_e310_sg3_fpga_default-gf52a643.zip e8264dd48c3c3f6e65c8e5ef34a3629aa79a3f17ba845659e553bdcf3dfac303 e3xx_e320_fpga_default fpga-4bc2c6f e3xx/fpga-4bc2c6f/e3xx_e320_fpga_default-g4bc2c6f.zip 0def19fda1041866273c09d3bacc7e2dba916b8848c8a17a85fecd04009bab73 # E320 Filesystems, etc e3xx_e320_sdk_default meta-ettus-v3.14.0.0-rc1 e3xx/meta-ettus-v3.14.0.0-rc1/e3xx_e320_sdk_default-v3.14.0.0-rc1.zip 0 diff --git a/mpm/CMakeLists.txt b/mpm/CMakeLists.txt index 4f6c3b651..78759cc0b 100644 --- a/mpm/CMakeLists.txt +++ b/mpm/CMakeLists.txt @@ -123,7 +123,7 @@ if(_has_no_psabi) message(STATUS "Disabling psABI warnings.") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") endif(_has_no_psabi) -set(MPM_ALL_DEVICES n3xx e320 tests) +set(MPM_ALL_DEVICES n3xx e320 e31x tests) set(MPM_DEVICE "n3xx" CACHE STRING "Choose an MPM device to build") set_property(CACHE MPM_DEVICE PROPERTY STRINGS ${MPM_ALL_DEVICES}) # Validate MPM_DEVICE @@ -139,12 +139,15 @@ if(MPM_DEVICE STREQUAL "n3xx") set(ENABLE_MAGNESIUM ON) elseif(MPM_DEVICE STREQUAL "e320") set(ENABLE_E320 ON) +elseif(MPM_DEVICE STREQUAL "e31x") + set(ENABLE_E300 ON) endif() MPM_REGISTER_COMPONENT("LibMPM" ENABLE_LIBMPM ON "Boost_FOUND" OFF ON) MPM_REGISTER_COMPONENT("Mykonos" ENABLE_MYKONOS ON "ENABLE_LIBMPM" OFF OFF) MPM_REGISTER_COMPONENT("Magnesium" ENABLE_MAGNESIUM ON "ENABLE_MYKONOS" OFF OFF) MPM_REGISTER_COMPONENT("E320" ENABLE_E320 ON "ENABLE_LIBMPM" OFF OFF) +MPM_REGISTER_COMPONENT("E300" ENABLE_E300 ON "ENABLE_LIBMPM" OFF OFF) add_subdirectory(include) include_directories( diff --git a/mpm/include/mpm/CMakeLists.txt b/mpm/include/mpm/CMakeLists.txt index e5b7193c6..f3637c913 100644 --- a/mpm/include/mpm/CMakeLists.txt +++ b/mpm/include/mpm/CMakeLists.txt @@ -11,7 +11,7 @@ install(FILES if(ENABLE_MYKONOS) add_subdirectory(ad937x) -elseif(ENABLE_E320) +elseif(ENABLE_E320 OR ENABLE_E300) add_subdirectory(ad9361) endif(ENABLE_MYKONOS) diff --git a/mpm/include/mpm/ad9361/CMakeLists.txt b/mpm/include/mpm/ad9361/CMakeLists.txt index 94ea655a7..2b2ea03ec 100644 --- a/mpm/include/mpm/ad9361/CMakeLists.txt +++ b/mpm/include/mpm/ad9361/CMakeLists.txt @@ -8,5 +8,6 @@ install(FILES ad9361_ctrl.hpp e320_defaults.hpp + e31x_defaults.hpp DESTINATION ${INCLUDE_DIR}/mpm/catalina ) diff --git a/mpm/include/mpm/ad9361/ad9361_ctrl.hpp b/mpm/include/mpm/ad9361/ad9361_ctrl.hpp index 7673d7460..2405317fd 100644 --- a/mpm/include/mpm/ad9361/ad9361_ctrl.hpp +++ b/mpm/include/mpm/ad9361/ad9361_ctrl.hpp @@ -14,11 +14,16 @@ #include <functional> #include <string> #include <vector> +#include <future> namespace mpm { namespace chips { using uhd::usrp::ad9361_ctrl; }}; // namespace mpm::chips +//! Async calls +std::future<double> handle_tune; +std::future<double> handle_set_clock_rate; + // TODO: pull in filter_info_base #ifdef LIBMPM_PYTHON void export_catalina(py::module& top_module) @@ -58,6 +63,48 @@ void export_catalina(py::module& top_module) .def("_get_filter", &ad9361_ctrl::get_filter) .def("set_filter", &ad9361_ctrl::set_filter) .def("output_digital_test_tone", &ad9361_ctrl::output_digital_test_tone); + + m.def("async__tune", +[]( + ad9361_ctrl& catalina, + const std::string &which, + const double value + ){ + handle_tune = std::async(std::launch::async, + &ad9361_ctrl::tune, + &catalina, + which, + value + ); + }); + m.def("await__tune", +[]( + )->bool{ + if (handle_tune.wait_for(std::chrono::seconds(0)) + == std::future_status::ready){ + handle_tune.get(); + return true; + } + return false; + }); + m.def("async__set_clock_rate", +[]( + ad9361_ctrl& catalina, + const double value + ){ + handle_set_clock_rate = std::async(std::launch::async, + &ad9361_ctrl::set_clock_rate, + &catalina, + value + ); + }); + m.def("await__set_clock_rate", +[]( + )->bool{ + if (handle_set_clock_rate.wait_for(std::chrono::seconds(0)) + == std::future_status::ready){ + handle_set_clock_rate.get(); + return true; + } + return false; + }); + } #endif diff --git a/mpm/include/mpm/ad9361/e31x_defaults.hpp b/mpm/include/mpm/ad9361/e31x_defaults.hpp new file mode 100644 index 000000000..c2188af8b --- /dev/null +++ b/mpm/include/mpm/ad9361/e31x_defaults.hpp @@ -0,0 +1,45 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_E31X_DEFAULTS_HPP +#define INCLUDED_E31X_DEFAULTS_HPP + +#include "ad9361_ctrl.hpp" + +namespace mpm { namespace types { namespace e31x { + +using namespace uhd::usrp; + +class e31x_ad9361_client_t : public uhd::usrp::ad9361_params { +public: + ~e31x_ad9361_client_t() {} + double get_band_edge(frequency_band_t band) { + switch (band) { + case AD9361_RX_BAND0: return 1.2e9; + case AD9361_RX_BAND1: return 2.6e9; + case AD9361_TX_BAND0: return 2940.0e6; + default: return 0; + } + } + clocking_mode_t get_clocking_mode() { + return clocking_mode_t::AD9361_XTAL_N_CLK_PATH; + } + digital_interface_mode_t get_digital_interface_mode() { + return AD9361_DDR_FDD_LVCMOS; + } + digital_interface_delays_t get_digital_interface_timing() { + digital_interface_delays_t delays; + delays.rx_clk_delay = 0; + delays.rx_data_delay = 0xF; + delays.tx_clk_delay = 0; + delays.tx_data_delay = 0xF; + return delays; + } +}; + +}}} // namespace + +#endif // INCLUDED_E31X_DEFAULTS_HPP diff --git a/mpm/include/mpm/dboards/CMakeLists.txt b/mpm/include/mpm/dboards/CMakeLists.txt index 03a5404bc..d1dd569d7 100644 --- a/mpm/include/mpm/dboards/CMakeLists.txt +++ b/mpm/include/mpm/dboards/CMakeLists.txt @@ -13,4 +13,9 @@ elseif(ENABLE_E320) neon_manager.hpp DESTINATION ${INCLUDE_DIR}/mpm/dboards ) +elseif(ENABLE_E300) + install(FILES + e31x_db_manager.hpp + DESTINATION ${INCLUDE_DIR}/mpm/dboards + ) endif(ENABLE_MAGNESIUM) diff --git a/mpm/include/mpm/dboards/e31x_db_manager.hpp b/mpm/include/mpm/dboards/e31x_db_manager.hpp new file mode 100644 index 000000000..8777778e9 --- /dev/null +++ b/mpm/include/mpm/dboards/e31x_db_manager.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <mpm/ad9361/ad9361_ctrl.hpp> +#include <mpm/types/lockable.hpp> +#include <mpm/types/regs_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <memory> +#include <mutex> + +namespace mpm { namespace dboards { + class e31x_db_manager + { + public: + e31x_db_manager(const std::string &catalina_spidev); + + /*! Return a reference to the radio chip controls + */ + mpm::chips::ad9361_ctrl::sptr get_radio_ctrl(){ return _catalina_ctrl; } + + private: + mpm::chips::ad9361_ctrl::sptr _catalina_ctrl; + }; + +}}; /* namespace mpm::dboards */ + +#ifdef LIBMPM_PYTHON +void export_e31x_db(py::module& top_module) +{ + using namespace mpm::dboards; + auto m = top_module.def_submodule("dboards"); + + py::class_<mpm::dboards::e31x_db_manager>(m, "e31x_db_manager") + .def(py::init<std::string>()) + .def("get_radio_ctrl", &mpm::dboards::e31x_db_manager::get_radio_ctrl); +} +#endif diff --git a/mpm/lib/CMakeLists.txt b/mpm/lib/CMakeLists.txt index 10f8f0d9d..82a5d8e16 100644 --- a/mpm/lib/CMakeLists.txt +++ b/mpm/lib/CMakeLists.txt @@ -14,7 +14,7 @@ add_subdirectory(types) if(ENABLE_MYKONOS) add_subdirectory(mykonos) -elseif(ENABLE_E320) +elseif(ENABLE_E320 OR ENABLE_E300) add_subdirectory(catalina) endif(ENABLE_MYKONOS) diff --git a/mpm/lib/dboards/CMakeLists.txt b/mpm/lib/dboards/CMakeLists.txt index 0760d9fd1..4a13528eb 100644 --- a/mpm/lib/dboards/CMakeLists.txt +++ b/mpm/lib/dboards/CMakeLists.txt @@ -16,4 +16,8 @@ elseif(ENABLE_E320) USRP_PERIPHS_ADD_OBJECT(dboards neon_manager.cpp ) +elseif(ENABLE_E300) + USRP_PERIPHS_ADD_OBJECT(dboards + e31x_db_manager.cpp + ) endif(ENABLE_MAGNESIUM) diff --git a/mpm/lib/dboards/e31x_db_manager.cpp b/mpm/lib/dboards/e31x_db_manager.cpp new file mode 100644 index 000000000..b433dde30 --- /dev/null +++ b/mpm/lib/dboards/e31x_db_manager.cpp @@ -0,0 +1,79 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <mpm/ad9361/e31x_defaults.hpp> +#include <mpm/dboards/e31x_db_manager.hpp> +#include <mpm/spi/spi_iface.hpp> +#include <mpm/spi/spi_regs_iface.hpp> +#include <mpm/types/regs_iface.hpp> +#include <boost/make_shared.hpp> +#include <memory> + +using namespace mpm::dboards; +using namespace mpm::chips; +using namespace mpm::types; +using namespace mpm::types::e31x; + +namespace { /*anon*/ + +constexpr uint32_t AD9361_SPI_WRITE_CMD = 0x00800000; +constexpr uint32_t AD9361_SPI_READ_CMD = 0x00000000; +constexpr uint32_t AD9361_SPI_ADDR_MASK = 0x003FFF00; +constexpr uint32_t AD9361_SPI_ADDR_SHIFT = 8; +constexpr uint32_t AD9361_SPI_DATA_MASK = 0x000000FF; +constexpr uint32_t AD9361_SPI_DATA_SHIFT = 0; +constexpr uint32_t AD9361_SPI_NUM_BITS = 24; +constexpr uint32_t AD9361_SPI_SPEED_HZ = 2000000; +constexpr int AD9361_SPI_MODE = 1; + +} // namespace /*anon*/ + +/*! MPM-style E310 SPI Iface for AD9361 CTRL + * + */ +class e310_ad9361_io_spi : public ad9361_io +{ +public: + e310_ad9361_io_spi(regs_iface::sptr regs_iface, uint32_t slave_num) : + _regs_iface(regs_iface), _slave_num(slave_num) { } + + ~e310_ad9361_io_spi() {/*nop*/} + + uint8_t peek8(uint32_t reg) + { + return _regs_iface->peek8(reg); + } + + void poke8(uint32_t reg, uint8_t val) + { + _regs_iface->poke8(reg, val); + } + +private: + regs_iface::sptr _regs_iface; + uint32_t _slave_num; +}; + +e31x_db_manager::e31x_db_manager(const std::string &catalina_spidev) +{ + // Make the MPM-style low level SPI Regs iface + auto spi_iface = mpm::spi::make_spi_regs_iface( + mpm::spi::spi_iface::make_spidev(catalina_spidev, AD9361_SPI_SPEED_HZ, AD9361_SPI_MODE), + AD9361_SPI_ADDR_SHIFT, + AD9361_SPI_DATA_SHIFT, + AD9361_SPI_READ_CMD, + AD9361_SPI_WRITE_CMD); + // Make the SPI interface + auto spi_io_iface = std::make_shared<e310_ad9361_io_spi>(spi_iface, 0); + // Translate from a std shared_ptr to Boost (for legacy compatability) + auto spi_io_iface_boost = boost::shared_ptr<e310_ad9361_io_spi>( + spi_io_iface.get(), + [spi_io_iface](...) mutable { spi_io_iface.reset(); }); + // Make the actual Catalina Ctrl object + _catalina_ctrl = ad9361_ctrl::make_spi( + boost::make_shared<e31x_ad9361_client_t>(), + spi_io_iface_boost); +} diff --git a/mpm/python/CMakeLists.txt b/mpm/python/CMakeLists.txt index c78dc60b1..3a5b30d4e 100644 --- a/mpm/python/CMakeLists.txt +++ b/mpm/python/CMakeLists.txt @@ -12,6 +12,8 @@ if(MPM_DEVICE STREQUAL "n3xx") add_library(pyusrp_periphs SHARED pyusrp_periphs/n3xx/pyusrp_periphs.cpp) elseif(MPM_DEVICE STREQUAL "e320") add_library(pyusrp_periphs SHARED pyusrp_periphs/e320/pyusrp_periphs.cpp) +elseif(MPM_DEVICE STREQUAL "e31x") + add_library(pyusrp_periphs SHARED pyusrp_periphs/e31x/pyusrp_periphs.cpp) endif(MPM_DEVICE STREQUAL "n3xx") target_include_directories(pyusrp_periphs PUBLIC diff --git a/mpm/python/pyusrp_periphs/e31x/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs/e31x/pyusrp_periphs.cpp new file mode 100644 index 000000000..b7971407c --- /dev/null +++ b/mpm/python/pyusrp_periphs/e31x/pyusrp_periphs.cpp @@ -0,0 +1,30 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <pybind11/pybind11.h> +namespace py = pybind11; +#define LIBMPM_PYTHON + +// Allow boost::shared_ptr<T> to be a holder class of an object (PyBind11 +// supports std::shared_ptr and std::unique_ptr out of the box) +#include <boost/shared_ptr.hpp> +PYBIND11_DECLARE_HOLDER_TYPE(T, boost::shared_ptr<T>); + +#include <mpm/ad9361/ad9361_ctrl.hpp> +#include <mpm/dboards/e31x_db_manager.hpp> +#include <mpm/spi/spi_python.hpp> +#include <mpm/types/types_python.hpp> +#include <mpm/xbar_iface.hpp> + +PYBIND11_MODULE(libpyusrp_periphs, m) +{ + export_types(m); + export_spi(m); + export_xbar(m); + export_catalina(m); + export_e31x_db(m); +} diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt index 1b85c8561..6b6d99a30 100644 --- a/mpm/python/usrp_mpm/CMakeLists.txt +++ b/mpm/python/usrp_mpm/CMakeLists.txt @@ -15,6 +15,7 @@ set(USRP_MPM_TOP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/components.py ${CMAKE_CURRENT_SOURCE_DIR}/discovery.py ${CMAKE_CURRENT_SOURCE_DIR}/eeprom.py + ${CMAKE_CURRENT_SOURCE_DIR}/e31x_legacy_eeprom.py ${CMAKE_CURRENT_SOURCE_DIR}/ethtable.py ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.py ${CMAKE_CURRENT_SOURCE_DIR}/liberiotable.py diff --git a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt index 650a21aab..3e4f6ba76 100644 --- a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt @@ -19,6 +19,7 @@ set(USRP_MPM_DBMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/dac_rh.py ${CMAKE_CURRENT_SOURCE_DIR}/eiscat.py ${CMAKE_CURRENT_SOURCE_DIR}/neon.py + ${CMAKE_CURRENT_SOURCE_DIR}/e31x_db.py ${CMAKE_CURRENT_SOURCE_DIR}/gain_rh.py ${CMAKE_CURRENT_SOURCE_DIR}/gaintables_rh.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk_eiscat.py diff --git a/mpm/python/usrp_mpm/dboard_manager/__init__.py b/mpm/python/usrp_mpm/dboard_manager/__init__.py index 70e7881db..77fd84436 100644 --- a/mpm/python/usrp_mpm/dboard_manager/__init__.py +++ b/mpm/python/usrp_mpm/dboard_manager/__init__.py @@ -10,6 +10,7 @@ from .base import DboardManagerBase from .magnesium import Magnesium from .rhodium import Rhodium from .neon import Neon +from .e31x_db import E31x_db from .eiscat import EISCAT from .test import test from .unknown import unknown diff --git a/mpm/python/usrp_mpm/dboard_manager/e31x_db.py b/mpm/python/usrp_mpm/dboard_manager/e31x_db.py new file mode 100644 index 000000000..358b69f88 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/e31x_db.py @@ -0,0 +1,212 @@ +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +E310 dboard (RF and control) implementation module +""" + +import threading +import time +from six import iterkeys, iteritems +from usrp_mpm import lib # Pulls in everything from C++-land +from usrp_mpm.bfrfs import BufferFS +from usrp_mpm.dboard_manager import DboardManagerBase +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.sys_utils.udev import get_eeprom_paths +from usrp_mpm.sys_utils.uio import UIO +from usrp_mpm.periph_manager.e31x_periphs import MboardRegsControl +from usrp_mpm.mpmutils import async_exec + +############################################################################### +# Main dboard control class +############################################################################### +class E31x_db(DboardManagerBase): + """ + Holds all dboard specific information and methods of the E31x_db dboard + """ + ######################################################################### + # Overridables + # + # See DboardManagerBase for documentation on these fields + ######################################################################### + pids = [0x0110] + rx_sensor_callback_map = { + 'ad9361_temperature': 'get_catalina_temp_sensor', + 'rssi' : 'get_rssi_sensor', + 'lo_lock' : 'get_lo_lock_sensor', + } + tx_sensor_callback_map = { + 'ad9361_temperature': 'get_catalina_temp_sensor', + } + # Maps the chipselects to the corresponding devices: + spi_chipselect = {"catalina": 0, + } + + default_master_clock_rate = 16e6 + MIN_MASTER_CLK_RATE = 220e3 + MAX_MASTER_CLK_RATE = 61.44e6 + + def __init__(self, slot_idx, **kwargs): + super(E31x_db, self).__init__(slot_idx, **kwargs) + self.log = get_logger("E31x_db-{}".format(slot_idx)) + self.log.trace("Initializing e31x daughterboard, slot index %d", + self.slot_idx) + self.rev = int(self.device_info['rev']) + self.log.trace("This is a rev: {}".format(chr(65 + self.rev))) + # These will get updated during init() + self.master_clock_rate = None + # Predeclare some attributes to make linter happy: + self.catalina = None + self.eeprom_fs = None + self.eeprom_path = None + # Now initialize all peripherals. If that doesn't work, put this class + # into a non-functional state (but don't crash, or we can't talk to it + # any more): + try: + self._init_periphs() + self._periphs_initialized = True + except Exception as ex: + self.log.error("Failed to initialize peripherals: %s", + str(ex)) + self._periphs_initialized = False + + def _init_periphs(self): + """ + Initialize power and peripherals that don't need user-settings + """ + self.log.debug("Loading C++ drivers...") + # Setup Catalina / the E31x_db Manager + self._device = lib.dboards.e31x_db_manager( + self._spi_nodes['catalina'] + ) + self.catalina = self._device.get_radio_ctrl() + self.log.trace("Loaded C++ drivers.") + self._init_cat_api(self.catalina) + + def _init_cat_api(self, cat): + """ + Propagate the C++ Catalina API into Python land. + """ + def export_method(obj, method): + " Export a method object, including docstring " + meth_obj = getattr(obj, method) + def func(*args): + " Functor for storing docstring too " + return meth_obj(*args) + func.__doc__ = meth_obj.__doc__ + return func + self.log.trace("Forwarding AD9361 methods to E31x_db class...") + for method in [ + x for x in dir(self.catalina) + if not x.startswith("_") and \ + callable(getattr(self.catalina, x))]: + self.log.trace("adding {}".format(method)) + setattr(self, method, export_method(cat, method)) + + def init(self, args): + if not self._periphs_initialized: + error_msg = "Cannot run init(), peripherals are not initialized!" + self.log.error(error_msg) + raise RuntimeError(error_msg) + master_clock_rate = \ + float(args.get('master_clock_rate', + self.default_master_clock_rate)) + assert self.MIN_MASTER_CLK_RATE <= master_clock_rate <= self.MAX_MASTER_CLK_RATE, \ + "Invalid master clock rate: {:.02f} MHz".format( + master_clock_rate / 1e6) + master_clock_rate_changed = master_clock_rate != self.master_clock_rate + if master_clock_rate_changed: + self.master_clock_rate = master_clock_rate + self.log.debug("Updating master clock rate to {:.02f} MHz!".format( + self.master_clock_rate / 1e6 + )) + # Some default chains on -- needed for setup purposes + self.catalina.set_active_chains(True, False, True, False) + self.set_catalina_clock_rate(self.master_clock_rate) + + return True + + def get_master_clock_rate(self): + " Return master clock rate (== sampling rate) " + return self.master_clock_rate + + ########################################################################## + # Sensors + ########################################################################## + def get_ad9361_lo_lock(self, which): + """ + Return LO lock status (Boolean!) of AD9361. 'which' must be + either 'tx' or 'rx' + """ + self.mboard_regs_label = "mboard-regs" + self.mboard_regs_control = MboardRegsControl( + self.mboard_regs_label, self.log) + if which == "tx": + locked = self. mboard_regs_control.get_ad9361_tx_lo_lock() + elif which == "rx": + locked = self. mboard_regs_control.get_ad9361_rx_lo_lock() + else: + locked = False + return locked + + def get_lo_lock_sensor(self, which): + """ + Get sensor dict with LO lock status + """ + self.log.trace("Reading LO Lock.") + lo_locked = self.get_ad9361_lo_lock(which) + return { + 'name': 'ad9361_lock', + 'type': 'BOOLEAN', + 'unit': 'locked' if lo_locked else 'unlocked', + 'value': str(lo_locked).lower(), + } + + def get_catalina_temp_sensor(self, _): + """ + Get temperature sensor reading of Catalina. + """ + # Note: the unused argument is channel + self.log.trace("Reading Catalina temperature.") + return { + 'name': 'ad9361_temperature', + 'type': 'REALNUM', + 'unit': 'C', + 'value': str(self.catalina.get_temperature()) + } + + def get_rssi_val(self, which): + """ + Return the current RSSI of `which` chain in Catalina + """ + return self.catalina.get_rssi(which) + + def get_rssi_sensor(self, chan): + """ + Return a sensor dictionary containing the current RSSI of `which` chain in Catalina + """ + which = 'RX' + str(chan+1) + return { + 'name': 'rssi', + 'type': 'REALNUM', + 'unit': 'dB', + 'value': str(self.get_rssi_val(which)), + } + + def set_catalina_clock_rate(self, rate): + """ + Async call to catalina set_clock_rate + """ + self.log.trace("Setting Clock rate to {}".format(rate)) + async_exec(lib.ad9361, "set_clock_rate", self.catalina, rate) + return rate + + def catalina_tune(self, which, freq): + """ + Async call to catalina tune + """ + self.log.trace("Tuning {} {}".format(which, freq)) + async_exec(lib.ad9361, "tune", self.catalina, which, freq) + return self.catalina.get_freq(which) diff --git a/mpm/python/usrp_mpm/dboard_manager/neon.py b/mpm/python/usrp_mpm/dboard_manager/neon.py index c0eb10eae..00438503d 100644 --- a/mpm/python/usrp_mpm/dboard_manager/neon.py +++ b/mpm/python/usrp_mpm/dboard_manager/neon.py @@ -18,7 +18,7 @@ from usrp_mpm.mpmlog import get_logger from usrp_mpm.sys_utils.udev import get_eeprom_paths from usrp_mpm.sys_utils.uio import UIO from usrp_mpm.periph_manager.e320_periphs import MboardRegsControl - +from usrp_mpm.mpmutils import async_exec ############################################################################### # Main dboard control class @@ -186,7 +186,7 @@ class Neon(DboardManagerBase): )) # Some default chains on -- needed for setup purposes self.catalina.set_active_chains(True, False, True, False) - self.catalina.set_clock_rate(self.master_clock_rate) + self.set_catalina_clock_rate(self.master_clock_rate) return True @@ -321,3 +321,18 @@ class Neon(DboardManagerBase): 'value': str(self.get_rssi_val(which)), } + def set_catalina_clock_rate(self, rate): + """ + Async call to catalina set_clock_rate + """ + self.log.trace("Setting Clock rate to {}".format(rate)) + async_exec(lib.ad9361, "set_clock_rate", self.catalina, rate) + return rate + + def catalina_tune(self, which, freq): + """ + Async call to catalina tune + """ + self.log.trace("Tuning {} {}".format(which, freq)) + async_exec(lib.ad9361, "tune", self.catalina, which, freq) + return self.catalina.get_freq(which) diff --git a/mpm/python/usrp_mpm/e31x_legacy_eeprom.py b/mpm/python/usrp_mpm/e31x_legacy_eeprom.py new file mode 100644 index 000000000..4c0fb19a5 --- /dev/null +++ b/mpm/python/usrp_mpm/e31x_legacy_eeprom.py @@ -0,0 +1,103 @@ +# +# Copyright 2017 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +EEPROM management code +""" + +import struct +import zlib +from builtins import zip +from builtins import object + + +class MboardEEPROM(object): + """ + Given a nvmem path, read out EEPROM values from the motherboard's EEPROM. + The format of data in the EEPROM must follow the following standard: + + E310 Legacy EEPROM Format + + - 2 bytes data_version_major + - 2 bytes data_version_minor + - 6 bytes MAC address + - 2 bytes hw_pid + - 2 bytes hw_rev + - 8 bytes serial number (zero-terminated string of 7 characters) + - 12 bytes padding + - 8 bytes user_name + + MAC addresses are ignored here; they are read elsewhere. If we really need + to know the MAC address of an interface, we can fish it out the raw data, + or ask the system. + """ + + # Refer e300_eeprom_manager.hpp. + eeprom_header_format = "<H H 6s H H 7s 12s 8s" + eeprom_header_keys = ('data_version_major', 'data_version_minor', 'mac_address', 'pid', 'rev', 'serial', 'pad', 'user_name') + +class DboardEEPROM(object): + """ + Given a nvmem path, read out EEPROM values from the daughterboard's EEPROM. + The format of data in the EEPROM must follow the following standard: + + E310 Legacy EEPROM Format + + - 2 bytes data_version_major + - 2 bytes data_version_minor + - 2 bytes hw_pid + - 2 bytes hw_rev + - 8 bytes serial number (zero-terminated string of 7 characters) + - 12 bytes padding + """ + + # Refer e300_eeprom_manager.hpp. + eeprom_header_format = "<H H H H 7s 12s" + eeprom_header_keys = ('data_version_major', 'data_version_minor', 'pid', 'rev', 'serial', 'pad') + +def read_eeprom( + isMotherboard, + nvmem_path, + offset, + eeprom_header_format, + eeprom_header_keys, + max_size=None, +): + """ + Read the EEPROM located at nvmem_path and return a tuple (header, data) + Header is already parsed in the common header fields + Data contains the full eeprom data structure + + nvmem_path -- Path to readable file (typically something in sysfs) + eeprom_header_format -- List of header formats, by version + eeprom_header_keys -- List of keys for the entries in the EEPROM + max_size -- Max number of bytes to be read. If omitted, will read the full file. + """ + + max_size = max_size or -1 + with open(nvmem_path, "rb") as nvmem_file: + data = nvmem_file.read(max_size)[offset:] + eeprom_parser = struct.Struct(eeprom_header_format) + eeprom_keys = eeprom_header_keys + parsed_data = eeprom_parser.unpack_from(data) + + if isMotherboard: # E310 MB. + # Rectify the PID and REV parsing. Reverse the bytes. + # PID and REV are the 4th and 5th elements in the tuple. + parsed_data_list = list(parsed_data) + parsed_data_list[3] = struct.unpack("<H", struct.pack(">H", parsed_data_list[3]))[0] + parsed_data_list[4] = struct.unpack("<H", struct.pack(">H", parsed_data_list[4]))[0] + parsed_data = tuple(parsed_data_list) + + else: # E310 DB. + # Rectify the PID and REV parsing. Reverse the bytes. + # PID and REV are the 3rd and 4th elements in the tuple. + parsed_data_list = list(parsed_data) + parsed_data_list[2] = struct.unpack("<H", struct.pack(">H", parsed_data_list[2]))[0] + parsed_data_list[3] = struct.unpack("<H", struct.pack(">H", parsed_data_list[3]))[0] + parsed_data = tuple(parsed_data_list) + + ret_val = (dict(list(zip(eeprom_keys, parsed_data))),data) + return ret_val
\ No newline at end of file diff --git a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt index 994b1d64a..987bc184b 100644 --- a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt @@ -15,6 +15,8 @@ set(USRP_MPM_PERIPHMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/n3xx_periphs.py ${CMAKE_CURRENT_SOURCE_DIR}/e320.py ${CMAKE_CURRENT_SOURCE_DIR}/e320_periphs.py + ${CMAKE_CURRENT_SOURCE_DIR}/e31x.py + ${CMAKE_CURRENT_SOURCE_DIR}/e31x_periphs.py ) list(APPEND USRP_MPM_FILES ${USRP_MPM_PERIPHMGR_FILES}) set(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE) diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py index 2dc69e908..e297effbd 100644 --- a/mpm/python/usrp_mpm/periph_manager/base.py +++ b/mpm/python/usrp_mpm/periph_manager/base.py @@ -131,6 +131,12 @@ class PeriphManagerBase(object): # specific implementation. Each PeriphManagerBase-derived class should list # information required to update the component, like a callback function updateable_components = {} + # The RPC server checks this value to determine if it needs to clear + # the RPC method registry. This is typically to remove stale references + # to RPC methods caused by removal of overlay on unclaim() by peripheral + # manager. Additionally the RPC server will re-register all methods on + # a claim(). Override and set to True in the derived class if desired. + clear_rpc_method_registry_on_unclaim = False @staticmethod def generate_device_info(eeprom_md, mboard_info, dboard_infos): diff --git a/mpm/python/usrp_mpm/periph_manager/e31x.py b/mpm/python/usrp_mpm/periph_manager/e31x.py new file mode 100644 index 000000000..722f8d7a9 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/e31x.py @@ -0,0 +1,744 @@ +# +# Copyright 2018-2019 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +E310 implementation module +""" + +from __future__ import print_function +import bisect +import copy +import re +import threading +from six import iteritems, itervalues +from usrp_mpm.components import ZynqComponents +from usrp_mpm.dboard_manager import E31x_db +from usrp_mpm.mpmtypes import SID +from usrp_mpm.mpmutils import assert_compat_number, str2bool +from usrp_mpm.periph_manager import PeriphManagerBase +from usrp_mpm.rpc_server import no_rpc +from usrp_mpm.sys_utils import dtoverlay +from usrp_mpm.sys_utils.sysfs_thermal import read_sysfs_sensors_value +from usrp_mpm.sys_utils.udev import get_spidev_nodes +from usrp_mpm.xports import XportMgrLiberio +from usrp_mpm.periph_manager.e31x_periphs import MboardRegsControl +from usrp_mpm.sys_utils.udev import get_eeprom_paths +from usrp_mpm import e31x_legacy_eeprom + +E310_DEFAULT_CLOCK_SOURCE = 'internal' +E310_DEFAULT_TIME_SOURCE = 'internal' +E310_DEFAULT_ENABLE_FPGPIO = True +E310_FPGA_COMPAT = (1,0) +E310_DBOARD_SLOT_IDX = 0 + +############################################################################### +# Transport managers +############################################################################### + +class E310XportMgrLiberio(XportMgrLiberio): + " E310-specific Liberio configuration " + max_chan = 4 + xbar_dev = "/dev/crossbar0" + xbar_port = 0 + +############################################################################### +# Main Class +############################################################################### +class e31x(ZynqComponents, PeriphManagerBase): + """ + Holds E310 specific attributes and methods + """ + ######################################################################### + # Overridables + # + # See PeriphManagerBase for documentation on these fields + ######################################################################### + description = "E300-Series Device" + # 0x77d2 and 0x77d3 + pids = {0x77D2: 'e310_sg1', #sg1 + 0x77D3: 'e310_sg3'} #sg3 + mboard_eeprom_addr = "e0004000.i2c" + mboard_eeprom_offset = 0 + mboard_eeprom_max_len = 64 + # We have two nvem paths on the E310. + # This one ensures that we get the right path for the MB. + mboard_eeprom_path_index = 1 + mboard_info = {"type": "e3xx"} + mboard_sensor_callback_map = { + 'ref_locked': 'get_ref_lock_sensor', + 'temp_fpga' : 'get_fpga_temp_sensor', + 'temp_mb' : 'get_mb_temp_sensor', + } + dboard_eeprom_addr = "e0004000.i2c" + + # Actual DB EEPROM bytes are just 28. Reading just a couple more. + # Refer e300_eeprom_manager.hpp + dboard_eeprom_max_len = 32 + + max_num_dboards = 1 + + # We're on a Zynq target, so the following two come from the Zynq standard + # device tree overlay (tree/arch/arm/boot/dts/zynq-7000.dtsi) + dboard_spimaster_addrs = ["e0006000.spi"] + # E310-specific settings + # Label for the mboard UIO + mboard_regs_label = "mboard-regs" + # Override the list of updateable components + updateable_components = { + 'fpga': { + 'callback': "update_fpga", + 'path': '/lib/firmware/{}.bin', + 'reset': True, + }, + 'dts': { + 'callback': "update_dts", + 'path': '/lib/firmware/{}.dts', + 'output': '/lib/firmware/{}.dtbo', + 'reset': False, + }, + } + # This class removes the overlay in tear_down() resulting + # in stale references to methods in the RPC server. Setting + # this to True ensures that the RPC server clears all registered + # methods on unclaim() and registers them on the following claim(). + clear_rpc_method_registry_on_unclaim = True + + @classmethod + def generate_device_info(cls, eeprom_md, mboard_info, dboard_infos): + """ + Generate dictionary describing the device. + """ + # Add the default PeriphManagerBase information first + device_info = super().generate_device_info( + eeprom_md, mboard_info, dboard_infos) + # Then add E31x-specific information + mb_pid = eeprom_md.get('pid') + device_info['product'] = cls.pids.get(mb_pid, 'unknown') + return device_info + + @staticmethod + def list_required_dt_overlays(device_info): + """ + Lists device tree overlays that need to be applied before this class can + be used. List of strings. + Are applied in order. + + eeprom_md -- Dictionary of info read out from the mboard EEPROM + device_args -- Arbitrary dictionary of info, typically user-defined + """ + return [device_info['product']] + + + @staticmethod + def get_idle_dt_overlay(device_info): + """ + Overlay to be applied to enter low power idle state. + """ + # e.g. e310_sg3_idle + idle_overlay = device_info['product'] + '_idle' + return idle_overlay + + ########################################################################### + # Ctor and device initialization tasks + ########################################################################### + def __init__(self, args): + """ + Does partial initialization which loads low power idle image + """ + super(e31x, self).__init__() + # Start clean by removing MPM-owned overlays. + active_overlays = self.list_active_overlays() + mpm_overlays = self.list_owned_overlays() + for overlay in active_overlays: + if overlay in mpm_overlays: + dtoverlay.rm_overlay(overlay) + # Apply idle overlay on boot to save power until + # an application tries to use the device. + self.args_cached = args + self.apply_idle_overlay() + self._device_initialized = False + + def _init_normal(self): + """ + Does full initialization + """ + if self._device_initialized: + return + if self.is_idle(): + self.remove_idle_overlay() + self.overlay_apply() + self.init_dboards(self.args_cached) + if not self._device_initialized: + # Don't try and figure out what's going on. Just give up. + return + # Initialize _do_not_reload with value from _default_args (mpm.conf) + self._do_not_reload = str2bool(self._default_args.get("no_reload_fpga", "False")) + self._tear_down = False + self._clock_source = None + self._time_source = None + self._available_endpoints = list(range(256)) + self.dboard = self.dboards[E310_DBOARD_SLOT_IDX] + try: + self._init_peripherals(self.args_cached) + except Exception as ex: + self.log.error("Failed to initialize motherboard: %s", str(ex)) + self._initialization_status = str(ex) + self._device_initialized = False + + def _init_dboards(self, dboard_infos, override_dboard_pids, default_args): + """ + Initialize all the daughterboards + + dboard_infos -- List of dictionaries as returned from + PeriphManagerBase._get_dboard_eeprom_info() + override_dboard_pids -- List of dboard PIDs to force + default_args -- Default args + """ + # Override the base class's implementation in order to avoid initializing our one "dboard" + # in the same way that, for example, N310's dboards are initialized. Specifically, + # - skip dboard EEPROM setup (we don't have one) + # - change the way we handle SPI devices + if override_dboard_pids: + self.log.warning("Overriding daughterboard PIDs with: {}" + .format(override_dboard_pids)) + raise NotImplementedError("Can't override dboard pids") + # We have only one dboard + dboard_info = dboard_infos[0] + # Set up the SPI nodes + spi_nodes = [] + for spi_addr in self.dboard_spimaster_addrs: + for spi_node in get_spidev_nodes(spi_addr): + bisect.insort(spi_nodes, spi_node) + + self.log.trace("Found spidev nodes: {0}".format(spi_nodes)) + + if not spi_nodes: + self.log.warning("No SPI nodes for dboard %d.", E310_DBOARD_SLOT_IDX) + else: + dboard_info.update({ + 'spi_nodes': spi_nodes, + 'default_args': default_args, + }) + + self.dboards.append(E31x_db(E310_DBOARD_SLOT_IDX, **dboard_info)) + self.log.info("Found %d daughterboard(s).", len(self.dboards)) + + def _check_fpga_compat(self): + " Throw an exception if the compat numbers don't match up " + actual_compat = self.mboard_regs_control.get_compat_number() + self.log.debug("Actual FPGA compat number: {:d}.{:d}".format( + actual_compat[0], actual_compat[1] + )) + assert_compat_number( + E310_FPGA_COMPAT, + self.mboard_regs_control.get_compat_number(), + component="FPGA", + fail_on_old_minor=True, + log=self.log + ) + + def _init_ref_clock_and_time(self, default_args): + """ + Initialize clock and time sources. After this function returns, the + reference signals going to the FPGA are valid. + """ + if not self.dboards: + self.log.warning( + "No dboards found, skipping setting clock and time source " + "configuration." + ) + self._clock_source = E310_DEFAULT_CLOCK_SOURCE + self._time_source = E310_DEFAULT_TIME_SOURCE + else: + self.set_clock_source( + default_args.get('clock_source', E310_DEFAULT_CLOCK_SOURCE) + ) + self.set_time_source( + default_args.get('time_source', E310_DEFAULT_TIME_SOURCE) + ) + + def _init_peripherals(self, args): + """ + Turn on all peripherals. This may throw an error on failure, so make + sure to catch it. + + Peripherals are initialized in the order of least likely to fail, to most + likely. + """ + # Sanity checks + assert self.mboard_info.get('product') in self.pids.values(), \ + "Device product could not be determined!" + # Init Mboard Regs + self.mboard_regs_control = MboardRegsControl( + self.mboard_regs_label, self.log) + self.mboard_regs_control.get_git_hash() + self.mboard_regs_control.get_build_timestamp() + self._check_fpga_compat() + self._update_fpga_type() + self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport() + self.log.debug("crossbar base port: {}".format(self.crossbar_base_port)) + + # Init clocking + self._init_ref_clock_and_time(args) + # Init CHDR transports + self._xport_mgrs = { + 'liberio': E310XportMgrLiberio(self.log.getChild('liberio')), + } + # Init complete. + self.log.debug("mboard info: {}".format(self.mboard_info)) + + def _read_mboard_eeprom(self): + """ + Read out mboard EEPROM. + Returns a tuple: (eeprom_dict, eeprom_rawdata), where the the former is + a de-serialized dictionary representation of the data, and the latter + is a binary string with the raw data. + + If no EEPROM is defined, returns empty values. + """ + if len(self.mboard_eeprom_addr): + (eeprom_head, eeprom_rawdata) = e31x_legacy_eeprom.read_eeprom( + True, # isMotherboard + get_eeprom_paths(self.mboard_eeprom_addr)[self.mboard_eeprom_path_index], + self.mboard_eeprom_offset, + e31x_legacy_eeprom.MboardEEPROM.eeprom_header_format, + e31x_legacy_eeprom.MboardEEPROM.eeprom_header_keys, + self.mboard_eeprom_max_len + ) + self.log.trace("Found EEPROM metadata: `{}'" + .format(str(eeprom_head))) + self.log.trace("Read {} bytes of EEPROM data." + .format(len(eeprom_rawdata))) + return eeprom_head, eeprom_rawdata + # Nothing defined? Return defaults. + self.log.trace("No mboard EEPROM path defined. " + "Skipping mboard EEPROM readout.") + return {}, b'' + + def _get_dboard_eeprom_info(self): + """ + Read back EEPROM info from the daughterboards + """ + if self.dboard_eeprom_addr is None: + self.log.debug("No dboard EEPROM addresses given.") + return [] + dboard_eeprom_addrs = self.dboard_eeprom_addr \ + if isinstance(self.dboard_eeprom_addr, list) \ + else [self.dboard_eeprom_addr] + dboard_eeprom_paths = [] + self.log.trace("Identifying dboard EEPROM paths from addrs `{}'..." + .format(",".join(dboard_eeprom_addrs))) + for dboard_eeprom_addr in dboard_eeprom_addrs: + self.log.trace("Resolving %s...", dboard_eeprom_addr) + dboard_eeprom_paths += get_eeprom_paths(dboard_eeprom_addr) + self.log.trace("Found dboard EEPROM paths: {}" + .format(",".join(dboard_eeprom_paths))) + if len(dboard_eeprom_paths) > self.max_num_dboards: + self.log.warning("Found more EEPROM paths than daughterboards. " + "Ignoring some of them.") + dboard_eeprom_paths = dboard_eeprom_paths[:self.max_num_dboards] + dboard_info = [] + for dboard_idx, dboard_eeprom_path in enumerate(dboard_eeprom_paths): + self.log.debug("Reading EEPROM info for dboard %d...", dboard_idx) + dboard_eeprom_md, dboard_eeprom_rawdata = e31x_legacy_eeprom.read_eeprom( + False, # is not motherboard. + dboard_eeprom_path, + self.dboard_eeprom_offset, + e31x_legacy_eeprom.DboardEEPROM.eeprom_header_format, + e31x_legacy_eeprom.DboardEEPROM.eeprom_header_keys, + self.dboard_eeprom_max_len + ) + self.log.trace("Found dboard EEPROM metadata: `{}'" + .format(str(dboard_eeprom_md))) + self.log.trace("Read %d bytes of dboard EEPROM data.", + len(dboard_eeprom_rawdata)) + db_pid = dboard_eeprom_md.get('pid') + if db_pid is None: + self.log.warning("No dboard PID found in dboard EEPROM!") + else: + self.log.debug("Found dboard PID in EEPROM: 0x{:04X}" + .format(db_pid)) + dboard_info.append({ + 'eeprom_md': dboard_eeprom_md, + 'eeprom_rawdata': dboard_eeprom_rawdata, + 'pid': db_pid, + }) + return dboard_info + + ########################################################################### + # Session init and deinit + ########################################################################### + def claim(self): + """ + Fully initializes a device when the rpc_server claim() + gets called to revive the device from idle state to be used + by an UHD application + """ + super(e31x, self).claim() + try: + self._init_normal() + except Exception as ex: + self.log.error("e31x claim() failed: %s", str(ex)) + + def init(self, args): + """ + Calls init() on the parent class, and then programs the Ethernet + dispatchers accordingly. + """ + if not self._device_initialized: + self.log.warning( + "Cannot run init(), device was never fully initialized!") + return False + if args.get("clock_source", "") != "": + self.set_clock_source(args.get("clock_source")) + if args.get("time_source", "") != "": + self.set_time_source(args.get("time_source")) + if "no_reload_fpga" in args: + self._do_not_reload = str2bool(args.get("no_reload_fpga")) or args.get("no_reload_fpga") == "" + result = super(e31x, self).init(args) + for xport_mgr in itervalues(self._xport_mgrs): + xport_mgr.init(args) + return result + + + def apply_idle_overlay(self): + """ + Load all overlays required to go into idle power savings mode. + """ + idle_overlay = self.get_idle_dt_overlay(self.device_info) + self.log.debug("Motherboard requests device tree overlay for Idle power savings mode: {}".format( + idle_overlay + )) + dtoverlay.apply_overlay_safe(idle_overlay) + + def remove_idle_overlay(self): + """ + Remove idle mode overlay. + """ + idle_overlay = self.get_idle_dt_overlay(self.device_info) + self.log.trace("Removing Idle overlay: {}".format( + idle_overlay + )) + dtoverlay.rm_overlay(idle_overlay) + + def list_owned_overlays(self): + """ + Lists all overlays that can be possibly applied by MPM. + """ + all_overlays = self.list_required_dt_overlays(self.device_info) + all_overlays.append(self.get_idle_dt_overlay(self.device_info)) + return all_overlays + + def deinit(self): + """ + Clean up after a UHD session terminates. + """ + if not self._device_initialized: + self.log.warning( + "Cannot run deinit(), device was never fully initialized!") + return + super(e31x, self).deinit() + for xport_mgr in itervalues(self._xport_mgrs): + xport_mgr.deinit() + self.log.trace("Resetting SID pool...") + self._available_endpoints = list(range(256)) + if not self._do_not_reload: + self.tear_down() + # Reset back to value from _default_args (mpm.conf) + self._do_not_reload = str2bool(self._default_args.get("no_reload_fpga", "False")) + + def tear_down(self): + """ + Tear down all members that need to be specially handled before + deconstruction. + For E310, this means the overlay. + """ + self.log.trace("Tearing down E310 device...") + self._tear_down = True + self.dboards = [] + self.dboard = None + self.mboard_regs_control = None + self._device_initialized = False + active_overlays = self.list_active_overlays() + self.log.trace("E310 has active device tree overlays: {}".format( + active_overlays + )) + for overlay in active_overlays: + dtoverlay.rm_overlay(overlay) + self.apply_idle_overlay() + + def is_idle(self): + """ + Determine if the device is in the idle state. + """ + active_overlays = self.list_active_overlays() + idle_overlay = self.get_idle_dt_overlay(self.device_info) + is_idle = idle_overlay in active_overlays + if is_idle: + self.log.trace("Found idle overlay: %s", idle_overlay) + return is_idle + + ########################################################################### + # Transport API + ########################################################################### + def request_xport( + self, + dst_address, + suggested_src_address, + xport_type + ): + """ + See PeriphManagerBase.request_xport() for docs. + """ + # Try suggested address first, then just pick the first available one: + src_address = suggested_src_address + if src_address not in self._available_endpoints: + if not self._available_endpoints: + raise RuntimeError( + "Depleted pool of SID endpoints for this device!") + else: + src_address = self._available_endpoints[0] + sid = SID(src_address << 16 | dst_address) + # Note: This SID may change its source address! + self.log.trace( + "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \ + "operating on temporary SID: %s", + dst_address, suggested_src_address, str(xport_type), str(sid)) + assert self.mboard_info['rpc_connection'] in ('local') + if self.mboard_info['rpc_connection'] == 'local': + return self._xport_mgrs['liberio'].request_xport( + sid, + xport_type, + ) + + def commit_xport(self, xport_info): + """ + See PeriphManagerBase.commit_xport() for docs. + + Reminder: All connections are incoming, i.e. "send" or "TX" means + remote device to local device, and "receive" or "RX" means this local + device to remote device. "Remote device" can be, for example, a UHD + session. + """ + ## Go, go, go + assert self.mboard_info['rpc_connection'] in ('local') + sid = SID(xport_info['send_sid']) + self._available_endpoints.remove(sid.src_ep) + self.log.debug("Committing transport for SID %s, xport info: %s", + str(sid), str(xport_info)) + if self.mboard_info['rpc_connection'] == 'local': + return self._xport_mgrs['liberio'].commit_xport(sid, xport_info) + + ########################################################################### + # Device info + ########################################################################### + def get_device_info_dyn(self): + """ + Append the device info with current IP addresses. + """ + if not self._device_initialized: + return {} + device_info = {} + device_info.update({ + 'fpga_version': "{}.{}".format( + *self.mboard_regs_control.get_compat_number()), + 'fpga_version_hash': "{:x}.{}".format( + *self.mboard_regs_control.get_git_hash()), + 'fpga': self.updateable_components.get('fpga', {}).get('type', ""), + }) + return device_info + + ########################################################################### + # Clock/Time API + ########################################################################### + def get_clock_sources(self): + " Lists all available clock sources. " + self.log.trace("Listing available clock sources...") + return ('internal',) + + def get_clock_source(self): + " Returns the currently selected clock source " + return self._clock_source + + def set_clock_source(self, *args): + """ + Switch reference clock. + + Throws if clock_source is not a valid value. + """ + clock_source = args[0] + assert clock_source in self.get_clock_sources() + self.log.debug("Setting clock source to `{}'".format(clock_source)) + if clock_source == self.get_clock_source(): + self.log.trace("Nothing to do -- clock source already set.") + return + self._clock_source = clock_source + self.mboard_regs_control.set_clock_source(clock_source) + + def get_time_sources(self): + " Returns list of valid time sources " + return ['internal', 'external', 'gpsdo'] + + def get_time_source(self): + " Return the currently selected time source " + return self._time_source + + def set_time_source(self, time_source): + " Set a time source " + assert time_source in self.get_time_sources() + if time_source == self.get_time_source(): + self.log.trace("Nothing to do -- time source already set.") + return + self._time_source = time_source + self.mboard_regs_control.set_time_source(time_source) + + ########################################################################### + # Hardware peripheral controls + ########################################################################### + + def set_fp_gpio_master(self, value): + """set driver for front panel GPIO + Arguments: + value {unsigned} -- value is a single bit bit mask of 12 pins GPIO + """ + self.mboard_regs_control.set_fp_gpio_master(value) + + def get_fp_gpio_master(self): + """get "who" is driving front panel gpio + The return value is a bit mask of 8 pins GPIO. + 0: means the pin is driven by PL + 1: means the pin is driven by PS + """ + return self.mboard_regs_control.get_fp_gpio_master() + + def set_fp_gpio_radio_src(self, value): + """set driver for front panel GPIO + Arguments: + value {unsigned} -- value is 2-bit bit mask of 8 pins GPIO + 00: means the pin is driven by radio 0 + 01: means the pin is driven by radio 1 + """ + self.mboard_regs_control.set_fp_gpio_radio_src(value) + + def get_fp_gpio_radio_src(self): + """get which radio is driving front panel gpio + The return value is 2-bit bit mask of 8 pins GPIO. + 00: means the pin is driven by radio 0 + 01: means the pin is driven by radio 1 + """ + return self.mboard_regs_control.get_fp_gpio_radio_src() + + def set_channel_mode(self, channel_mode): + "Set channel mode in FPGA and select which tx channel to use" + self.mboard_regs_control.set_channel_mode(channel_mode) + + ########################################################################### + # Sensors + ########################################################################### + def get_ref_lock_sensor(self): + """ + #TODO: Where is ref lock signal coming from? + """ + self.log.trace("Querying ref lock status.") + lock_status = bool(self.mboard_regs_control.get_refclk_lock()) + return { + 'name': 'ref_locked', + 'type': 'BOOLEAN', + 'unit': 'locked' if lock_status else 'unlocked', + 'value': str(lock_status).lower(), + } + + def get_mb_temp_sensor(self): + """ + Get temperature sensor reading of the E310. + """ + self.log.trace("Reading temperature.") + temp = '-1' + raw_val = {} + data_probes = ['temp1_input'] + try: + for data_probe in data_probes: + raw_val[data_probe] = read_sysfs_sensors_value('jc-42.4-temp', data_probe, 'hwmon', 'name')[0] + temp = str(raw_val['temp1_input'] / 1000) + except ValueError: + self.log.warning("Error when converting temperature value") + except KeyError: + self.log.warning("Can't read temp on thermal_zone".format(sensor)) + return { + 'name': 'temp_mb', + 'type': 'REALNUM', + 'unit': 'C', + 'value': temp + } + + def get_fpga_temp_sensor(self): + """ + Get temperature sensor reading of the E310. + """ + self.log.trace("Reading temperature.") + temp = '-1' + raw_val = {} + data_probes = ['in_temp0_raw', 'in_temp0_scale', 'in_temp0_offset'] + try: + for data_probe in data_probes: + raw_val[data_probe] = read_sysfs_sensors_value('xadc', data_probe, 'iio', 'name')[0] + temp = str((raw_val['in_temp0_raw'] + raw_val['in_temp0_offset']) * raw_val['in_temp0_scale'] / 1000) + except ValueError: + self.log.warning("Error when converting temperature value") + except KeyError: + self.log.warning("Can't read temp on thermal_zone".format(sensor)) + return { + 'name': 'temp_fpga', + 'type': 'REALNUM', + 'unit': 'C', + 'value': temp + } + + ########################################################################### + # EEPROMs + ########################################################################### + def get_mb_eeprom(self): + """ + Return a dictionary with EEPROM contents. + + All key/value pairs are string -> string. + + We don't actually return the EEPROM contents, instead, we return the + mboard info again. This filters the EEPROM contents to what we think + the user wants to know/see. + """ + return self.mboard_info + + def set_mb_eeprom(self, eeprom_vals): + """ + See PeriphManagerBase.set_mb_eeprom() for docs. + """ + self.log.warn("Called set_mb_eeprom(), but not implemented!") + raise NotImplementedError + + def get_db_eeprom(self, dboard_idx): + """ + See PeriphManagerBase.get_db_eeprom() for docs. + """ + if dboard_idx != E310_DBOARD_SLOT_IDX: + self.log.warn("Trying to access invalid dboard index {}. " + "Using the only dboard.".format(dboard_idx)) + db_eeprom_data = copy.copy(self.dboard.device_info) + return db_eeprom_data + + def set_db_eeprom(self, dboard_idx, eeprom_data): + self.log.warn("Called set_db_eeprom(), but not implemented!") + raise NotImplementedError + + ########################################################################### + # Component updating + ########################################################################### + # Note: Component updating functions defined by ZynqComponents + @no_rpc + def _update_fpga_type(self): + """Update the fpga type stored in the updateable components""" + fpga_type = self.mboard_regs_control.get_fpga_type() + self.log.debug("Updating mboard FPGA type info to {}".format(fpga_type)) + self.updateable_components['fpga']['type'] = fpga_type diff --git a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py new file mode 100644 index 000000000..0b166e5bc --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py @@ -0,0 +1,297 @@ +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +E310 peripherals +""" + +import datetime +import math +from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank +from usrp_mpm.sys_utils.uio import UIO + +class FrontpanelGPIO(GPIOBank): + """ + Abstraction layer for the front panel GPIO + """ + EMIO_BASE = 54 + FP_GPIO_OFFSET = 32 # Bit offset within the ps_gpio_* pins + + def __init__(self, ddr): + GPIOBank.__init__( + self, + {'label': 'zynq_gpio'}, + self.FP_GPIO_OFFSET + self.EMIO_BASE, + 0xFF, # use_mask + ddr + ) + +class MboardRegsControl(object): + """ + Control the FPGA Motherboard registers + """ + # Motherboard registers + MB_COMPAT_NUM = 0x0000 + MB_DATESTAMP = 0x0004 + MB_GIT_HASH = 0x0008 + MB_SCRATCH = 0x000C + MB_NUM_CE = 0x0010 + MB_NUM_IO_CE = 0x0014 + MB_CLOCK_CTRL = 0x0018 + MB_XADC_RB = 0x001C + MB_BUS_CLK_RATE = 0x0020 + MB_BUS_COUNTER = 0x0024 + MB_GPIO_MASTER = 0x0030 + MB_GPIO_RADIO_SRC = 0x0034 + MB_GPS_CTRL = 0x0038 + MB_GPS_STATUS = 0x003C + MB_DBOARD_CTRL = 0x0040 + MB_DBOARD_STATUS = 0x0044 + MB_XBAR_BASEPORT = 0x0048 + + # Bitfield locations for the MB_CLOCK_CTRL register. + MB_CLOCK_CTRL_PPS_SEL_INT = 0 + MB_CLOCK_CTRL_PPS_SEL_EXT = 1 + MB_CLOCK_CTRL_REF_CLK_LOCKED = 2 + + # Bitfield locations for the MB_GPS_CTRL register. + #FIXME: Update for E310 + MB_GPS_CTRL_PWR_EN = 0 + MB_GPS_CTRL_RST_N = 1 + MB_GPS_CTRL_INITSURV_N = 2 + + # Bitfield locations for the MB_GPS_STATUS register. + #FIXME: Update for E310 + MB_GPS_STATUS_LOCK = 0 + MB_GPS_STATUS_ALARM = 1 + MB_GPS_STATUS_PHASELOCK = 2 + MB_GPS_STATUS_SURVEY = 3 + MB_GPS_STATUS_WARMUP = 4 + + # Bitfield locations for the MB_DBOARD_CTRL register. + MB_DBOARD_CTRL_MIMO = 0 + MB_DBOARD_CTRL_TX_CHAN_SEL = 1 + + # Bitfield locations for the MB_DBOARD_STATUS register. + MB_DBOARD_STATUS_RX_LOCK = 6 + MB_DBOARD_STATUS_TX_LOCK = 7 + + def __init__(self, label, log): + self.log = log + self.regs = UIO( + label=label, + read_only=False + ) + self.poke32 = self.regs.poke32 + self.peek32 = self.regs.peek32 + + def get_compat_number(self): + """get FPGA compat number + + This function reads back FPGA compat number. + The return is a tuple of + 2 numbers: (major compat number, minor compat number ) + """ + with self.regs: + compat_number = self.peek32(self.MB_COMPAT_NUM) + minor = compat_number & 0xff + major = (compat_number>>16) & 0xff + return (major, minor) + + def set_fp_gpio_master(self, value): + """set driver for front panel GPIO + Arguments: + value {unsigned} -- value is a single bit bit mask of 8 pins GPIO + """ + with self.regs: + return self.poke32(self.MB_GPIO_MASTER, value) + + def get_fp_gpio_master(self): + """get "who" is driving front panel gpio + The return value is a bit mask of 8 pins GPIO. + 0: means the pin is driven by PL + 1: means the pin is driven by PS + """ + with self.regs: + return self.peek32(self.MB_GPIO_MASTER) & 0xfff + + def set_fp_gpio_radio_src(self, value): + """set driver for front panel GPIO + Arguments: + value {unsigned} -- value is 2-bit bit mask of 8 pins GPIO + 00: means the pin is driven by radio 0 + 01: means the pin is driven by radio 1 + """ + with self.regs: + return self.poke32(self.MB_GPIO_RADIO_SRC, value) + + def get_fp_gpio_radio_src(self): + """get which radio is driving front panel gpio + The return value is 2-bit bit mask of 8 pins GPIO. + 00: means the pin is driven by radio 0 + 01: means the pin is driven by radio 1 + """ + with self.regs: + return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff + + def get_build_timestamp(self): + """ + Returns the build date/time for the FPGA image. + The return is datetime string with the ISO 8601 format + (YYYY-MM-DD HH:MM:SS.mmmmmm) + """ + with self.regs: + datestamp_rb = self.peek32(self.MB_DATESTAMP) + if datestamp_rb > 0: + dt_str = datetime.datetime( + year=((datestamp_rb>>17)&0x3F)+2000, + month=(datestamp_rb>>23)&0x0F, + day=(datestamp_rb>>27)&0x1F, + hour=(datestamp_rb>>12)&0x1F, + minute=(datestamp_rb>>6)&0x3F, + second=((datestamp_rb>>0)&0x3F)) + self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) + return str(dt_str) + else: + # Compatibility with FPGAs without datestamp capability + return '' + + def get_git_hash(self): + """ + Returns the GIT hash for the FPGA build. + The return is a tuple of + 2 numbers: (short git hash, bool: is the tree dirty?) + """ + with self.regs: + git_hash_rb = self.peek32(self.MB_GIT_HASH) + git_hash = git_hash_rb & 0x0FFFFFFF + tree_dirty = ((git_hash_rb & 0xF0000000) > 0) + dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' + self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( + git_hash, dirtiness_qualifier)) + return (git_hash, dirtiness_qualifier) + + def set_time_source(self, time_source): + """ + Set time source + """ + pps_sel_val = 0x0 + if time_source == 'internal': + self.log.trace("Setting time source to internal") + pps_sel_val = self.MB_CLOCK_CTRL_PPS_SEL_INT + elif time_source == 'gpsdo': + self.log.debug("Setting time source to gpsdo...") + pps_sel_val = self.MB_CLOCK_CTRL_PPS_SEL_GPS + elif time_source == 'external': + self.log.debug("Setting time source to external...") + pps_sel_val = self.MB_CLOCK_CTRL_PPS_SEL_EXT + else: + assert False, "Cannot set to invalid time source: {}".format(time_source) + with self.regs: + reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFF90 + # prevent glitches by writing a cleared value first, then the final value. + self.poke32(self.MB_CLOCK_CTRL, reg_val) + reg_val = reg_val | (pps_sel_val & 0x6F) + self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) + self.poke32(self.MB_CLOCK_CTRL, reg_val) + + def set_clock_source(self, clock_source): + """ + Set clock source + """ + if clock_source == 'internal': + self.log.trace("Setting clock source to internal") + else: + assert False, "Cannot set to invalid clock source: {}".format(clock_source) + + def get_fpga_type(self): + """ + Reads the type of the FPGA image currently loaded + Returns a string with the type (SG1, SG3) + """ + #TODO: Add SG1 and SG3? + return "" + + def get_gps_status(self): + """ + Get GPS status + """ + mask = 0x1F + with self.regs: + gps_status = self.peek32(self.MB_GPS_STATUS) & mask + return gps_status + + def get_refclk_lock(self): + """ + Check the status of the reference clock in FPGA. + """ + mask = 0b1 << self.MB_CLOCK_CTRL_REF_CLK_LOCKED + with self.regs: + reg_val = self.peek32(self.MB_CLOCK_CTRL) + locked = (reg_val & mask) > 0 + if not locked: + self.log.warning("Reference Clock reporting unlocked. " + "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val)) + else: + self.log.trace("Reference Clock locked!") + return locked + + def set_channel_mode(self, channel_mode): + """ + Set channel mode in FPGA and select which tx channel to use + channel mode = "MIMO" for mimo + channel mode = "SISO_TX1", "SISO_TX0" for siso tx1, tx0 respectively. + """ + with self.regs: + reg_val = self.peek32(self.MB_DBOARD_CTRL) + if channel_mode == "MIMO": + reg_val = (0b1 << self.MB_DBOARD_CTRL_MIMO) + self.log.trace("Setting channel mode in AD9361 interface: {}".format("2R2T" if channel_mode == 2 else "1R1T")) + else: + # Warn if user tries to set either tx0/tx1 in mimo mode + # as both will be set automatically + if channel_mode == "SISO_TX1": + # in SISO mode, Channel 1 + reg_val = (0b1 << self.MB_DBOARD_CTRL_TX_CHAN_SEL) | (0b0 << self.MB_DBOARD_CTRL_MIMO) + self.log.trace("Setting TX channel in AD9361 interface to: TX1") + elif channel_mode == "SISO_TX0": + # in SISO mode, Channel 0 + reg_val = (0b0 << self.MB_DBOARD_CTRL_TX_CHAN_SEL) | (0b0 << self.MB_DBOARD_CTRL_MIMO) + self.log.trace("Setting TX channel in AD9361 interface to: TX0") + self.log.trace("Writing MB_DBOARD_CTRL to 0x{:08X}".format(reg_val)) + self.poke32(self.MB_DBOARD_CTRL, reg_val) + + def get_ad9361_tx_lo_lock(self): + """ + Check the status of TX LO lock from CTRL_OUT pins from Catalina + """ + mask = 0b1 << self.MB_DBOARD_STATUS_TX_LOCK + with self.regs: + reg_val = self.peek32(self.MB_DBOARD_STATUS) + locked = (reg_val & mask) > 0 + if not locked: + self.log.warning("TX RF PLL reporting unlocked. ") + else: + self.log.trace("TX RF PLL locked") + return locked + + def get_ad9361_rx_lo_lock(self): + """ + Check the status of RX LO lock from CTRL_OUT pins from Catalina + """ + mask = 0b1 << self.MB_DBOARD_STATUS_RX_LOCK + with self.regs: + reg_val = self.peek32(self.MB_DBOARD_STATUS) + locked = (reg_val & mask) > 0 + if not locked: + self.log.warning("RX RF PLL reporting unlocked. ") + else: + self.log.trace("RX RF PLL locked") + return locked + + def get_xbar_baseport(self): + "Get the RFNoC crossbar base port" + with self.regs: + return self.peek32(self.MB_XBAR_BASEPORT) diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py index c67bf769b..80b4eb44e 100644 --- a/mpm/python/usrp_mpm/rpc_server.py +++ b/mpm/python/usrp_mpm/rpc_server.py @@ -286,6 +286,8 @@ class MPMServer(RPCServer): self._state.claim_status.value = True self.periph_manager.claimed = True self.periph_manager.claim() + if self.periph_manager.clear_rpc_method_registry_on_unclaim: + self._init_rpc_calls(self.periph_manager) self._state.lock.release() self.session_id = session_id + " ({})".format(self.client_host) self._reset_timer() @@ -337,6 +339,8 @@ class MPMServer(RPCServer): self._state.claim_status.value = False self._state.claim_token.value = b'' self.session_id = None + if self.periph_manager.clear_rpc_method_registry_on_unclaim: + self.clear_method_registry() try: self.periph_manager.claimed = False self.periph_manager.unclaim() @@ -451,19 +455,13 @@ class MPMServer(RPCServer): ########################################################################### # Update components ########################################################################### - def reset_mgr(self): + def clear_method_registry(self): """ - Reset the Peripheral Manager for this RPC server. + Clear all the methods in the RPC server method cache. """ - self.log.info("Resetting peripheral manager.") - self.periph_manager.tear_down() - self.periph_manager = None - self.periph_manager = self._mgr_generator() - self._init_rpc_calls(self.periph_manager) # RPCServer caches RPC methods, but that cache is not accessible here # (because Cython). Re-running `RPCServer.__init__` clears that cache, - # and allows us to register new RPC methods (which we need to do because - # we're resetting the PeriphManager). + # and allows us to register new RPC methods. # A note on maintenance: This has been deemed safe through inspection of # the RPCServer source code. However, this is not typical Python, and # changes in future versions of RPCServer may cause issues. @@ -471,6 +469,19 @@ class MPMServer(RPCServer): pack_params={'use_bin_type': True}, ) + def reset_mgr(self): + """ + Reset the Peripheral Manager for this RPC server. + """ + self.log.info("Resetting peripheral manager.") + self.periph_manager.tear_down() + self.periph_manager = None + self.periph_manager = self._mgr_generator() + self._init_rpc_calls(self.periph_manager) + # Clear the method cache in order to remove stale references to + # methods from the old peripheral manager (the one before reset) + self.clear_method_registry() + def update_component(self, token, file_metadata_l, data_l): """" Updates the device component files specified by the metadata and data diff --git a/mpm/tools/CMakeLists.txt b/mpm/tools/CMakeLists.txt index 05838b897..245720656 100644 --- a/mpm/tools/CMakeLists.txt +++ b/mpm/tools/CMakeLists.txt @@ -13,7 +13,7 @@ install(PROGRAMS set(eeprom_tool_sources) set(eeprom_tool_libs) -if(ENABLE_LIBMPM) +if(ENABLE_LIBMPM AND NOT ENABLE_E300) message(STATUS "Adding MPM EEPROM tools...") set(eeprom_tool_libs eeprom.c) list(APPEND eeprom_tool_sources @@ -23,7 +23,7 @@ if(ENABLE_LIBMPM) eeprom-init.c eeprom-set-flags.c ) -endif(ENABLE_LIBMPM) +endif(ENABLE_LIBMPM AND NOT ENABLE_E300) if(ENABLE_MYKONOS) message(STATUS "Adding N3XX-specific EEPROM tools...") set(eeprom_tool_libs eeprom.c) |