diff options
Diffstat (limited to 'host/lib/usrp')
25 files changed, 165 insertions, 4889 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 0b839a835..f15324608 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -35,7 +35,6 @@ endif(ENABLE_C_API) INCLUDE_SUBDIRECTORY(cores) INCLUDE_SUBDIRECTORY(dboard) INCLUDE_SUBDIRECTORY(common) -INCLUDE_SUBDIRECTORY(device3) INCLUDE_SUBDIRECTORY(mpmd) INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 8ee79103f..2dd4e7e26 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -49,6 +49,3 @@ endif(ENABLE_N300) if(ENABLE_N320) INCLUDE_SUBDIRECTORY(rhodium) endif(ENABLE_N320) -if(ENABLE_MPMD AND ENABLE_EISCAT) - INCLUDE_SUBDIRECTORY(eiscat) -endif(ENABLE_MPMD AND ENABLE_EISCAT) diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp index 29381a53c..bc9ed9169 100644 --- a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp @@ -7,7 +7,6 @@ #include "e3xx_radio_control_impl.hpp" #include "e3xx_constants.hpp" -#include <uhd/transport/chdr.hpp> #include <uhd/types/direction.hpp> #include <uhd/types/eeprom.hpp> #include <uhd/utils/algorithm.hpp> diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp index f97feeb68..6ecf4ff2a 100644 --- a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp +++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp @@ -6,7 +6,6 @@ #include "e3xx_constants.hpp" #include "e3xx_radio_control_impl.hpp" -#include <uhd/transport/chdr.hpp> #include <uhd/types/sensors.hpp> #include <uhd/utils/log.hpp> #include <uhdlib/rfnoc/reg_iface_adapter.hpp> diff --git a/host/lib/usrp/dboard/eiscat/CMakeLists.txt b/host/lib/usrp/dboard/eiscat/CMakeLists.txt deleted file mode 100644 index 076d26916..000000000 --- a/host/lib/usrp/dboard/eiscat/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# -# Copyright 2017 Ettus Research, a National Instruments Company -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -# This file is conditionally included if ENABLE_MPMD and ENABLE_EISCAT are -# set to true. - -list(APPEND EISCAT_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/eiscat_radio_ctrl_impl.cpp -) -LIBUHD_APPEND_SOURCES(${EISCAT_SOURCES}) - diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp deleted file mode 100644 index 73851656b..000000000 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp +++ /dev/null @@ -1,950 +0,0 @@ -// -// Copyright 2017 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "eiscat_radio_ctrl_impl.hpp" - -#include <uhd/utils/log.hpp> -#include <uhd/utils/math.hpp> -#include <uhd/rfnoc/node_ctrl_base.hpp> -#include <uhd/types/ranges.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/make_shared.hpp> -#include <boost/date_time/posix_time/posix_time_io.hpp> -#include <boost/format.hpp> - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::rfnoc; - -namespace { - const size_t SR_ANTENNA_GAIN_BASE = 204; - const size_t SR_ANTENNA_SELECT_BASE = 192; // Note: On other dboards, 192 is DB_GPIO address space - const size_t RB_CHOOSE_BEAMS = 11; - - const double EISCAT_TICK_RATE = 208e6; // Hz - const double EISCAT_RADIO_RATE = 104e6; // Hz - const double EISCAT_CENTER_FREQ = 208e6; // Hz - const double EISCAT_DEFAULT_NULL_GAIN = 0.0; // dB. This is not the digital antenna gain, this a fake stub value. - const double EISCAT_DEFAULT_BANDWIDTH = 104e6; // Hz - const char* EISCAT_DEFAULT_ANTENNA = "BF"; - const size_t EISCAT_NUM_ANTENNAS = 16; - const size_t EISCAT_NUM_BEAMS = 10; - const size_t EISCAT_NUM_PORTS = 5; - const size_t EISCAT_MAX_GAIN_RANGE = 18; // Bits, *signed*. - const size_t EISCAT_UNIT_GAIN_RANGE = 14; // Bits, *signed*. - const int32_t EISCAT_MAX_GAIN = (1<<(EISCAT_MAX_GAIN_RANGE-1))-1; - const int32_t EISCAT_UNIT_GAIN = (1<<(EISCAT_UNIT_GAIN_RANGE-1))-1; - const int32_t EISCAT_MIN_GAIN = -(1<<(EISCAT_MAX_GAIN_RANGE-1)); - const double EISCAT_DEFAULT_NORM_GAIN = 1.0; // Normalized. This is the actual digital gain value. - const size_t EISCAT_BITS_PER_TAP = 18; - const eiscat_radio_ctrl_impl::fir_tap_t EISCAT_MAX_TAP_VALUE = (1<<(EISCAT_BITS_PER_TAP-1))-1; - const eiscat_radio_ctrl_impl::fir_tap_t EISCAT_MIN_TAP_VALUE = -(1<<(EISCAT_BITS_PER_TAP-1)); - const size_t EISCAT_NUM_FIR_TAPS = 10; - const size_t EISCAT_NUM_FIR_SETS = 1024; // BRAM must be at least EISCAT_NUM_FIR_TAPS * EISCAT_NUM_FIR_SETS - const size_t EISCAT_FIR_INDEX_IMPULSE = 1002; - const size_t EISCAT_FIR_INDEX_ZEROS = 1003; - - const uint32_t EISCAT_CONTRIB_LOWER = 0<<0; - const uint32_t EISCAT_CONTRIB_UPPER = 1<<0; - const uint32_t EISCAT_SKIP_NEIGHBOURS = 1<<1; - const uint32_t EISCAT_BYPASS_MATRIX = 1<<2; - const uint32_t EISCAT_OUTPUT_COUNTER = 1<<3; -}; - - -UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) -{ - UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::ctor() "); - _num_ports = get_output_ports().size(); - UHD_LOG_TRACE("EISCAT", "Number of channels: " << _num_ports); - UHD_LOG_TRACE("EISCAT", - "Tick rate is " << EISCAT_TICK_RATE/1e6 << " MHz" - ); - - /**** Configure the radio_ctrl itself ***********************************/ - // This also sets the command tick rate: - radio_ctrl_impl::set_rate(EISCAT_TICK_RATE); - for (size_t chan = 0; chan < _num_ports; chan++) { - radio_ctrl_impl::set_rx_frequency(EISCAT_CENTER_FREQ, chan); - radio_ctrl_impl::set_rx_gain(EISCAT_DEFAULT_NULL_GAIN, chan); - radio_ctrl_impl::set_rx_antenna(EISCAT_DEFAULT_ANTENNA, chan); - radio_ctrl_impl::set_rx_bandwidth(EISCAT_DEFAULT_BANDWIDTH, chan); - // We might get tx async messages from upstream radios, we send them to the - // nevernever by default or they interfere with our streamers or ctrl_iface - // objects. The assumption is that FF:FF is never a valid SID. - this->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, 0xFFFF, chan); - } - - /**** Set up arg-based control API **************************************/ - // None of these properties are defined in the XML file. Some of them have - // non-Noc-Script-compatible types. - _tree->create<bool>(get_arg_path("sysref", 0) / "value") - .set(true) - .add_coerced_subscriber([this](bool){ - try { - this->send_sysref(); - } catch (const uhd::exception &ex) { - UHD_LOGGER_WARNING("EISCAT") - << "Failed to send SYSREF: " << ex.what(); - throw uhd::runtime_error(str( - boost::format("Failed to send SYSREF: %s") - % ex.what() - )); - } - }) - .set_publisher([](){ return true; }) - ; - _tree->create<bool>(get_arg_path("assert_adcs_deframers", 0) / "value") - .set(true) - .set_publisher([this](){ return this->assert_adcs_deframers(); }) - ; - _tree->create<bool>(get_arg_path("assert_deframer_status", 0) / "value") - .set(true) - .set_publisher([this](){ return this->assert_adcs_deframers(); }) - ; - _tree->create<time_spec_t>(get_arg_path("fir_ctrl_time", 0) / "value") - .add_coerced_subscriber([this](time_spec_t switch_time){ - this->set_fir_ctrl_time(switch_time); - }) - .set(time_spec_t(0.0)) - ; - for (size_t beam = 0; beam < EISCAT_NUM_BEAMS; beam++) { - for (size_t ant = 0; ant < EISCAT_NUM_ANTENNAS; ant++) { - const size_t fir_index = beam * EISCAT_NUM_ANTENNAS + ant; - // These are not in the XML file - _tree->create<int>(get_arg_path("fir_select", fir_index) / "value") - .add_coerced_subscriber([beam, ant, this](const size_t ram_idx){ - UHD_ASSERT_THROW(ram_idx < EISCAT_NUM_FIR_SETS); - this->select_filter( - beam, - ant, - ram_idx, - this->get_arg<time_spec_t>("fir_ctrl_time", 0), - false - ); - }) - ; - } - } - for (size_t fir_set = 0; fir_set < EISCAT_NUM_FIR_SETS; fir_set++) { - _tree->create<std::vector<fir_tap_t>>( - get_arg_path("fir_taps", fir_set) / "value") - .add_coerced_subscriber( - [this, fir_set](const std::vector<fir_tap_t> &taps){ - this->write_fir_taps(fir_set, taps); - } - ) - ; - } - - - /**** Add subscribers for our special properties ************************/ - // The difference between this block and the previous that these *are* - // defined in the XML file, and can have defaults set there. - _tree->access<int>(get_arg_path("choose_beams", 0) / "value") - .add_coerced_subscriber([this](int choose_beams){ - this->set_beam_selection(choose_beams); - }) - .update() - ; - _tree->access<bool>(get_arg_path("enable_firs", 0) / "value") - .add_coerced_subscriber([this](int enable){ - this->enable_firs(bool(enable)); - }) - .update() - ; - _tree->access<bool>(get_arg_path("enable_counter", 0) / "value") - .add_coerced_subscriber([this](int enable){ - this->enable_counter(bool(enable)); - }) - .update() - ; - _tree->access<int>(get_arg_path("configure_beams", 0) / "value") - .add_coerced_subscriber([this](int reg_value){ - this->configure_beams(uint32_t(reg_value)); - }) // No update! This would override the previous settings. - .set_publisher([this](){ - return this->user_reg_read32(RB_CHOOSE_BEAMS); - }) - ; - - /**** Configure the digital gain controls *******************************/ - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { - _tree->access<double>(get_arg_path("gain", i) / "value") - .set_coercer([](double gain){ - return std::max(-16.0, std::min(16.0, gain)); - }) - .add_coerced_subscriber([this, i](double gain){ - this->set_antenna_gain(i, gain); - }) - .update() - ; - } - - /**** Set up legacy compatible properties *******************************/ - // For use with multi_usrp APIs etc. - // For legacy prop tree init: - fs_path fe_path = fs_path("dboards") / "A" / "rx_frontends"; - - // The EISCAT dboards have 16 frontends total, but they map to 10 beams - // each through a matrix of FIR filters and summations, and then only 5 of - // those channels go out through the Noc-Shell. - // UHD will thus get much less confused if we create 5 fake frontends (i.e., - // number of Noc-Block-ports). Since we have no control over the frontends, - // nothing is lost here. - for (size_t fe_idx = 0; fe_idx < _num_ports; fe_idx++) { - _tree->create<std::string>(fe_path / fe_idx / "name") - .set(str(boost::format("EISCAT Beam Contributions %d") % fe_idx)) - ; - _tree->create<std::string>(fe_path / fe_idx / "connection") - .set("I") - ; - _tree->create<double>(fe_path / fe_idx / "freq" / "value") - .set_coercer([this](const double freq){ - return this->set_rx_frequency(freq, 0); - }) - .set_publisher([this](){ - return this->get_rx_frequency(0); - }) - ; - _tree->create<meta_range_t>(fe_path / fe_idx / "freq" / "range") - .set(meta_range_t(EISCAT_CENTER_FREQ, EISCAT_CENTER_FREQ)) - ; - _tree->create<double>(fe_path / fe_idx / "gains" / "null" / "value") - .set_coercer([this](const double gain){ - return this->set_rx_gain(gain, 0); - }) - .set_publisher([this](){ - return this->get_rx_gain(0); - }) - ; - _tree->create<meta_range_t>(fe_path / fe_idx / "gains" / "null" / "range") - .set(meta_range_t(EISCAT_DEFAULT_NULL_GAIN, EISCAT_DEFAULT_NULL_GAIN)) - ; - _tree->create<double>(fe_path / fe_idx / "bandwidth" / "value") - .set_coercer([this](const double bw){ - return this->set_rx_bandwidth(bw, 0); - }) - .set_publisher([this](){ - return this->get_rx_bandwidth(0); - }) - ; - _tree->create<meta_range_t>(fe_path / fe_idx / "bandwidth" / "range") - .set(meta_range_t(EISCAT_DEFAULT_BANDWIDTH, EISCAT_DEFAULT_BANDWIDTH)) - ; - _tree->create<bool>(fe_path / fe_idx / "use_lo_offset") - .set(false) - ; - } - - auto antenna_options = std::vector<std::string>{"BF"}; - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { - antenna_options.push_back(str(boost::format("Rx%d") % i)); - antenna_options.push_back(str(boost::format("BF%d") % i)); - } - antenna_options.push_back("FI0"); - antenna_options.push_back("FI250"); - antenna_options.push_back("FI500"); - antenna_options.push_back("FI750"); - for (size_t beam_idx = 0; beam_idx < _num_ports; beam_idx++) { - _tree->create<std::string>(fe_path / beam_idx / "antenna" / "value") - .set(EISCAT_DEFAULT_ANTENNA) - .add_coerced_subscriber([this, beam_idx](const std::string &name){ - this->set_rx_antenna(name, beam_idx); - }) - .set_publisher([this, beam_idx](){ - return this->get_rx_antenna(beam_idx); - }) - ; - _tree->create<std::vector<std::string>>( - fe_path / beam_idx / "antenna" / "options") - .set(antenna_options) - ; - } - - // We can actually stream data to an EISCAT board, so it needs some tx - // frontends too: - fe_path = fs_path("dboards") / "A" / "tx_frontends"; - for (size_t fe_idx = 0; fe_idx < _num_ports; fe_idx++) { - _tree->create<std::string>(fe_path / fe_idx / "name") - .set(str(boost::format("EISCAT Uplink %d") % fe_idx)) - ; - } - - for (size_t i = 0; i < EISCAT_NUM_PORTS; i++) { - _tree->create<uhd::time_spec_t>(get_arg_path("pseudo_stream_cmd", i) / "value") - .add_coerced_subscriber([this, i](uhd::time_spec_t stream_time){ - if (stream_time != uhd::time_spec_t(0.0)) { - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - cmd.stream_now = false; - cmd.time_spec = stream_time; - this->issue_stream_cmd(cmd, i); - } else { - this->issue_stream_cmd( - uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS), - i - ); - } - }) - ; - } - //FIXME elaborate this more, but for now it works. - _tree->create<int>("rx_codecs/A/gains"); - _tree->create<std::string>("rx_codecs/A/name").set("ADS54J66"); - - - // There is only ever one EISCAT radio per mboard, so this should be unset - // when we reach this line: - UHD_ASSERT_THROW(not _tree->exists("tick_rate")); - _tree->create<double>("tick_rate") - .set(EISCAT_TICK_RATE) - .set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rate, this, _1)) - ; -} - -eiscat_radio_ctrl_impl::~eiscat_radio_ctrl_impl() -{ - UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::dtor() "); -} - - -/**************************************************************************** - * Public API calls - ***************************************************************************/ -void eiscat_radio_ctrl_impl::set_tx_antenna(const std::string &, const size_t) -{ - throw uhd::runtime_error("Cannot set Tx antenna on EISCAT daughterboard"); -} - -void eiscat_radio_ctrl_impl::set_rx_antenna( - const std::string &ant, - const size_t port -) { - UHD_ASSERT_THROW(port < EISCAT_NUM_BEAMS); - if (ant == "BF") { - UHD_LOG_TRACE("EISCAT", "Setting antenna to 'BF' (which is a no-op)"); - return; - } - if (ant.size() < 3) { - throw uhd::value_error(str( - boost::format("EISCAT: Invalid antenna selection: %s") - % ant - )); - } - - const std::string ant_mode = ant.substr(0, 2); - const size_t antenna_idx = [&ant](){ - try { - return boost::lexical_cast<size_t>(ant.substr(2)); - } catch (const boost::bad_lexical_cast&) { - throw uhd::value_error(str( - boost::format("EISCAT: Invalid antenna selection: %s") - % ant - )); - } - }(); - - if (ant_mode == "BF") { - int new_choose_beams = - get_arg<int>("choose_beams") | EISCAT_SKIP_NEIGHBOURS; - set_arg<int>("choose_beams", new_choose_beams); - size_t beam_select_offset = - (get_arg<int>("choose_beams") & EISCAT_CONTRIB_UPPER) ? - EISCAT_NUM_PORTS : 0; - const size_t beam_index = port + beam_select_offset; - uhd::time_spec_t send_now(0.0); - UHD_LOG_TRACE("EISCAT", str( - boost::format("Setting block port %d to only receive from beam %d " - "connected to antenna %d via FIR matrix") - % port - % beam_index - % antenna_idx - )); - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { - select_filter( - beam_index, - i, - (i == antenna_idx) ? - EISCAT_FIR_INDEX_IMPULSE : EISCAT_FIR_INDEX_ZEROS, - send_now - ); - } - enable_firs(true); - } else if (ant_mode == "RX" or ant_mode == "Rx") { - int new_choose_beams = - get_arg<int>("choose_beams") | EISCAT_SKIP_NEIGHBOURS; - set_arg<int>("choose_beams", new_choose_beams); - UHD_LOG_TRACE("EISCAT", str( - boost::format("Setting port %d to only receive on antenna %d " - "directly, bypassing neighbours and FIR matrix") - % port % antenna_idx - )); - sr_write(SR_ANTENNA_SELECT_BASE + port, antenna_idx); - enable_firs(false); - } else if (ant_mode == "FI") { - size_t beam_index = port % EISCAT_NUM_PORTS; - UHD_LOG_TRACE("EISCAT", str( - boost::format("Setting port %d to filter index %d on all antennas " - "using beam indices %d and %d.") - % port - % antenna_idx - % beam_index % (beam_index + EISCAT_NUM_PORTS) - )); - // Note: antenna_idx is not indexing a physical antenna in this scenario. - uhd::time_spec_t send_now(0.0); - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { - select_filter( - beam_index, - i, - antenna_idx, - send_now - ); - select_filter( - beam_index + EISCAT_NUM_PORTS, - i, - antenna_idx, - send_now - ); - } - enable_firs(true); - } else if (ant_mode == "CN") { - const size_t beam_index = port % EISCAT_NUM_PORTS; - UHD_LOG_TRACE("EISCAT", str( - boost::format("Setting port %d to filter index %d on all antennas " - "using beam indices %d and %d.") - % port - % antenna_idx - % beam_index % (beam_index + EISCAT_NUM_PORTS) - )); - // Note: antenna_idx is not indexing a physical antenna in this scenario. - uhd::time_spec_t send_now(0.0); - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i+=2) { - select_filter( - beam_index, - i, - 0, - send_now - ); - select_filter( - beam_index + EISCAT_NUM_PORTS, - i, - 0, - send_now - ); - select_filter( - beam_index, - i+1, - antenna_idx, - send_now - ); - select_filter( - beam_index + EISCAT_NUM_PORTS, - i+1, - antenna_idx, - send_now - ); - } - enable_firs(true); - } else { - throw uhd::value_error(str( - boost::format("EISCAT: Invalid antenna selection: %s") - % ant - )); - } -} - -double eiscat_radio_ctrl_impl::get_tx_frequency(const size_t /* chan */) -{ - UHD_LOG_WARNING("EISCAT", "Ignoring attempt to read Tx frequency"); - return 0.0; -} - -double eiscat_radio_ctrl_impl::set_tx_frequency(const double /* freq */, const size_t /* chan */) -{ - throw uhd::runtime_error("Cannot set Tx frequency on EISCAT daughterboard"); -} - -double eiscat_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) -{ - if (freq != get_rx_frequency(chan)) { - UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx frequency"); - } - return get_rx_frequency(chan); -} - -double eiscat_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) -{ - if (bandwidth != get_rx_bandwidth(chan)) { - UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx bandwidth"); - } - return get_rx_bandwidth(chan); -} - - -double eiscat_radio_ctrl_impl::set_tx_gain(const double /* gain */, const size_t /* chan */) -{ - throw uhd::runtime_error("Cannot set Tx gain on EISCAT daughterboard"); -} - -double eiscat_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) -{ - // TODO: Add ability to set digital gain or make it explicit this function is not supported. - if (gain != get_rx_gain(chan)) { - UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx gain."); - } - return get_rx_gain(chan); -} - -double eiscat_radio_ctrl_impl::set_rate(double rate) -{ - if (rate != get_rate()) { - UHD_LOG_WARNING("EISCAT", "Attempting to set sampling rate to invalid value " << rate); - } - return get_rate(); -} - -size_t eiscat_radio_ctrl_impl::get_chan_from_dboard_fe( - const std::string &fe, - const uhd::direction_t /* dir */ -) { - return boost::lexical_cast<size_t>(fe); -} - -std::string eiscat_radio_ctrl_impl::get_dboard_fe_from_chan( - const size_t chan, - const uhd::direction_t /* dir */ -) { - return std::to_string(chan); -} - -double eiscat_radio_ctrl_impl::get_output_samp_rate(size_t /* port */) -{ - return EISCAT_RADIO_RATE; -} - -void eiscat_radio_ctrl_impl::set_rx_streamer(bool active, const size_t port) -{ - UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::set_rx_streamer() " << port << " -> " << active ; - if (port > EISCAT_NUM_PORTS) { - throw uhd::value_error(str( - boost::format("[%s] Can't (un)register RX streamer on port %d (invalid port)") - % unique_id() % port - )); - } - _rx_streamer_active[port] = active; - if (not check_radio_config()) { - throw std::runtime_error(str( - boost::format("[%s]: Invalid radio configuration.") - % unique_id() - )); - } - - if (list_upstream_nodes().empty() or not bool(get_arg<int>("use_prev"))) { - UHD_LOG_DEBUG(unique_id(), "No prevs found, or prevs disabled, not passing on set_rx_streamer"); - } else { - UHD_LOG_DEBUG(unique_id(), "set_rx_streamer(): We have prevs, so passing on set_rx_streamer"); - source_node_ctrl::sptr this_upstream_block_ctrl = - boost::dynamic_pointer_cast<source_node_ctrl>(list_upstream_nodes().at(0).lock()); - if (this_upstream_block_ctrl) { - this_upstream_block_ctrl->set_rx_streamer(active, port); - } else { - UHD_LOG_WARNING(unique_id(), "Oh noes, couldn't lock sptr!"); - } - } -} - -void eiscat_radio_ctrl_impl::issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t chan) -{ - std::lock_guard<std::mutex> lock(_mutex); - - // Turn on/off top ones - if (list_upstream_nodes().empty() or not bool(get_arg<int>("use_prev"))) { - UHD_LOG_DEBUG(unique_id(), "No prevs found, or prevs disabled, not passing on stream cmd"); - } else { - UHD_LOG_DEBUG(unique_id(), "issue_stream_cmd(): We have prevs, so passing on stream command"); - source_node_ctrl::sptr this_upstream_block_ctrl = - boost::dynamic_pointer_cast<source_node_ctrl>(list_upstream_nodes().at(0).lock()); - if (this_upstream_block_ctrl) { - this_upstream_block_ctrl->issue_stream_cmd( - stream_cmd, - chan - ); - } else { - UHD_LOG_WARNING(unique_id(), "Oh noes, couldn't lock sptr!"); - } - } - - // Turn on/off this one - UHD_LOGGER_DEBUG(unique_id()) << "eiscat_radio_ctrl_impl::issue_stream_cmd() " << chan << " " << char(stream_cmd.stream_mode); - if (not _is_streamer_active(uhd::RX_DIRECTION, chan)) { - UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::issue_stream_cmd() called on inactive channel. Skipping." ; - return; - } - UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff); - _continuous_streaming[chan] = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - - if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS && - stream_cmd.stream_now == false) { - UHD_LOG_TRACE("EISCAT", "Stop cmd timed, setting cmd time!"); - set_command_time(stream_cmd.time_spec, chan); - } - - //setup the mode to instruction flags - typedef boost::tuple<bool, bool, bool, bool> inst_t; - static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of - //reload, chain, samps, stop - (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false, false)) - (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false, true)) - (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true, false)) - (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true, false)) - ; - - //setup the instruction flag values - bool inst_reload, inst_chain, inst_samps, inst_stop; - boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode]; - - //calculate the word from flags and length - uint32_t cmd_word = 0; - cmd_word |= uint32_t((stream_cmd.stream_now)? 1 : 0) << 31; - cmd_word |= uint32_t((inst_chain)? 1 : 0) << 30; - cmd_word |= uint32_t((inst_reload)? 1 : 0) << 29; - cmd_word |= uint32_t((inst_stop)? 1 : 0) << 28; - cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1); - - //issue the stream command - const uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(get_rate()); - sr_write(regs::RX_CTRL_CMD, cmd_word, chan); - sr_write(regs::RX_CTRL_TIME_HI, uint32_t(ticks >> 32), chan); - sr_write(regs::RX_CTRL_TIME_LO, uint32_t(ticks >> 0), chan); //latches the command - UHD_LOG_INFO(unique_id(), "issued stream command."); - if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS && - stream_cmd.stream_now == false) { - UHD_LOG_TRACE("EISCAT", "Stop cmd timed, setting cmd time!"); - set_command_time(uhd::time_spec_t(0.0), chan); - } - -} - -bool eiscat_radio_ctrl_impl::check_radio_config() -{ - const uint32_t config_beams = get_arg<int>("configure_beams"); - bool skipping_neighbours = config_beams & EISCAT_SKIP_NEIGHBOURS; - bool upper_contrib = config_beams & EISCAT_CONTRIB_UPPER; - const fs_path rx_fe_path = fs_path("dboards/A/rx_frontends"); - uint32_t chan_enables = 0; - for (const auto &enb: _rx_streamer_active) { - if (enb.second) { - chan_enables |= (1<<enb.first); - } - } - if (not skipping_neighbours) { - chan_enables = chan_enables | (chan_enables << EISCAT_NUM_PORTS); - } else if (upper_contrib) { - chan_enables <<= EISCAT_NUM_PORTS; - } - UHD_LOG_TRACE("EISCAT", str( - boost::format("check_radio_config(): Setting channel enables to 0x%02X" - " Using %s beams, %saccepting neighbour contributions") - % chan_enables - % (upper_contrib ? "upper" : "lower") - % (skipping_neighbours ? "not " : "") - )); - sr_write("SR_RX_STREAM_ENABLE", chan_enables); - - return true; -} - -void eiscat_radio_ctrl_impl::set_rpc_client( - uhd::rpc_client::sptr rpcc, - const uhd::device_addr_t &block_args -) { - _rpcc = rpcc; - _block_args = block_args; - auto dboard_info = - _rpcc->request<std::vector<std::map<std::string, std::string>>>( - "get_dboard_info" - ); - _num_dboards = dboard_info.size(); - UHD_LOG_DEBUG("EISCAT", "Using " << _num_dboards << " daughterboards."); - if (_num_dboards == 1) { - UHD_LOG_WARNING("EISCAT", - "Found 1 dboard, expected 2 for optimal operation." - ); - } else if (_num_dboards > 2) { - UHD_LOG_ERROR("EISCAT", "Detected too many dboards: " << _num_dboards); - throw uhd::runtime_error("Too many dboards detected."); - } - - UHD_LOG_INFO( - "EISCAT", - "Finalizing dboard initialization; initializing JESD cores and ADCs." - ); - - /* Start of the ADC synchronization operation. - * These steps must be repeated if any ADC fails its deframer check - * Changing the sync line from SyncbAB to SyncnCD usually resolves the error - */ - const size_t possible_sync_combinations = 16; // 2 sync lines ^ (2 ADCs * 2 Daughtercards) - for (size_t iteration = 0; iteration < possible_sync_combinations; iteration++) { - UHD_LOG_INFO( - "EISCAT", - "looping to initialize JESD cores and ADCs." - ); - if (not assert_jesd_cores_initialized()) { - throw uhd::runtime_error("Failed to initialize JESD cores and reset ADCs!"); - } - send_sysref(); - - if (not assert_adcs_deframers()) { - throw uhd::runtime_error("Failed to initialize ADCs and JESD deframers!"); - } - send_sysref(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - if (assert_deframer_status()) { - return; - } - } - - // Unable to find a sync line combination which works - throw uhd::runtime_error("Failed to finalize JESD core setup!"); -} - -/**************************************************************************** - * Internal methods - ***************************************************************************/ -void eiscat_radio_ctrl_impl::write_fir_taps( - const size_t fir_idx, - const std::vector<eiscat_radio_ctrl_impl::fir_tap_t> &taps -) { - if (taps.size() > EISCAT_NUM_FIR_TAPS) { - throw uhd::value_error(str( - boost::format("Too many FIR taps for EISCAT filters (%d)") - % taps.size() - )); - } - for (const auto &tap: taps) { - if (tap > EISCAT_MAX_TAP_VALUE or tap < EISCAT_MIN_TAP_VALUE) { - throw uhd::value_error(str( - boost::format("Filter tap for filter_idx %d exceeds dynamic range (%d bits are allowed)") - % fir_idx % EISCAT_BITS_PER_TAP - )); - } - } - - UHD_LOG_TRACE("EISCAT", str( - boost::format("Writing %d filter taps for filter index %d") - % taps.size() % fir_idx - )); - for (size_t i = 0; i < EISCAT_NUM_FIR_TAPS; i++) { - // Payload: - // - bottom 14 bits address, fir_idx * 16 + tap_index - // - top 18 bits are value - uint32_t reg_value = (fir_idx * 16) + i;; - if (taps.size() > i) { - reg_value |= (taps[i] & 0x3FFFF) << 14; - } - sr_write("SR_FIR_BRAM_WRITE_TAPS", reg_value); - } -} - -void eiscat_radio_ctrl_impl::select_filter( - const size_t beam_index, - const size_t antenna_index, - const size_t fir_index, - const uhd::time_spec_t &time_spec, - const bool write_time -) { - if (antenna_index >= EISCAT_NUM_ANTENNAS) { - throw uhd::value_error(str( - boost::format("Antenna index %d out of range. There are %d antennas in EISCAT.") - % antenna_index % EISCAT_NUM_ANTENNAS - )); - } - if (beam_index >= EISCAT_NUM_BEAMS) { - throw uhd::value_error(str( - boost::format("Beam index %d out of range. " - "There are %d beam channels in EISCAT.") - % beam_index - % EISCAT_NUM_BEAMS - )); - } - - UHD_LOGGER_TRACE("EISCAT") - << "Selecting filter " << fir_index - << " for beam " << beam_index - << " and antenna " << antenna_index - ; - bool send_now = (time_spec == uhd::time_spec_t(0.0)); - uint32_t reg_value = 0 - | (fir_index * 16) - | (antenna_index & 0xF) << 14 - | (beam_index & 0xF) << 18 - | send_now << 22 - ; - if (not send_now) { - UHD_LOG_TRACE("EISCAT", str( - boost::format("Filter selection will be applied at " - "time %f (0x%016X == %u). %s") - % time_spec.get_full_secs() - % time_spec.to_ticks(EISCAT_TICK_RATE) - % time_spec.to_ticks(EISCAT_TICK_RATE) - % (write_time ? "Writing time regs now." - : "Assuming time regs already up-to-date.") - )); - if (write_time) { - set_fir_ctrl_time(time_spec); - } - } - sr_write("SR_FIR_COMMANDS_RELOAD", reg_value); -} - -void eiscat_radio_ctrl_impl::set_fir_ctrl_time( - const uhd::time_spec_t &time_spec -) { - const uint64_t cmd_time_ticks = time_spec.to_ticks(EISCAT_TICK_RATE); - sr_write( - "SR_FIR_COMMANDS_CTRL_TIME_LO", - uint32_t(cmd_time_ticks & 0xFFFFFFFF) - ); - sr_write( - "SR_FIR_COMMANDS_CTRL_TIME_HI", - uint32_t((cmd_time_ticks >> 32) & 0xFFFFFFFF) - ); -} - -void eiscat_radio_ctrl_impl::set_antenna_gain( - const size_t antenna_idx, - const double normalized_gain -) { - if (normalized_gain < -16.0 or normalized_gain > 16.0) { - throw uhd::value_error(str( - boost::format("Invalid digital gain value for antenna %d: %f") - % antenna_idx % normalized_gain - )); - } - - const auto fixpoint_gain = std::max<int32_t>( - EISCAT_MIN_GAIN, - std::min( - EISCAT_MAX_GAIN, - int32_t(normalized_gain * EISCAT_UNIT_GAIN) - ) - ); - - UHD_LOG_TRACE("EISCAT", str( - boost::format("Setting digital gain value for antenna %d to %f (%d)") - % antenna_idx % normalized_gain % fixpoint_gain - )); - sr_write(SR_ANTENNA_GAIN_BASE + antenna_idx, fixpoint_gain); -} - -void eiscat_radio_ctrl_impl::configure_beams(uint32_t reg_value) -{ - UHD_LOGGER_TRACE("EISCAT") - << "Selecting " << - ((reg_value & EISCAT_CONTRIB_UPPER) ? "upper" : "lower") << " beams."; - UHD_LOGGER_TRACE("EISCAT") - << ((reg_value & EISCAT_SKIP_NEIGHBOURS) ? "Disabling" : "Enabling") - << " neighbour contributions."; - UHD_LOGGER_TRACE("EISCAT") - << ((reg_value & EISCAT_BYPASS_MATRIX) ? "Disabling" : "Enabling") - << " FIR matrix."; - UHD_LOGGER_TRACE("EISCAT") - << ((reg_value & EISCAT_OUTPUT_COUNTER) ? "Enabling" : "Disabling") - << " counter."; - UHD_LOG_TRACE("EISCAT", str( - boost::format("Setting SR_BEAMS_TO_NEIGHBOR to 0x%08X.") - % reg_value - )); - sr_write("SR_BEAMS_TO_NEIGHBOR", reg_value); -} - -void eiscat_radio_ctrl_impl::set_beam_selection(int beam_selection) -{ - UHD_ASSERT_THROW(beam_selection < 4 and beam_selection >= 0); - const uint32_t old_value = user_reg_read32(RB_CHOOSE_BEAMS); - const uint32_t new_value = - (old_value & (~uint32_t(EISCAT_CONTRIB_UPPER|EISCAT_SKIP_NEIGHBOURS))) - | (uint32_t(beam_selection) - & uint32_t(EISCAT_CONTRIB_UPPER|EISCAT_SKIP_NEIGHBOURS)) - ; - configure_beams(new_value); -} - -void eiscat_radio_ctrl_impl::enable_firs(bool enable) -{ - const uint32_t old_value = user_reg_read32(RB_CHOOSE_BEAMS); - const uint32_t new_value = enable ? - (old_value & ~EISCAT_BYPASS_MATRIX) - : old_value | EISCAT_BYPASS_MATRIX - ; - configure_beams(new_value); -} - -void eiscat_radio_ctrl_impl::send_sysref() -{ - if (_block_args.has_key("use_mpm_sysref")) { - _rpcc->notify_with_token("db_0_send_sysref"); - } else { - // This value needs to be big enough that we actually hit it between - // reading back the time, and applying the command: - const int CMD_DELAY_MS = 100; - auto sysref_time = get_time_now() - + uhd::time_spec_t(double(CMD_DELAY_MS * 1000)); - uint64_t sysref_time_ticks = sysref_time.to_ticks(EISCAT_TICK_RATE); - // The tick value must be even, or we'd still have the 180 degree phase - // ambiguity! The actual value doesn't matter. - sysref_time_ticks += sysref_time_ticks % 2; - set_command_time(uhd::time_spec_t::from_ticks( - sysref_time_ticks, EISCAT_TICK_RATE - )); - this->sr_write("SR_SYSREF", 1); - std::this_thread::sleep_for(std::chrono::milliseconds(CMD_DELAY_MS)); - } -} - -void eiscat_radio_ctrl_impl::enable_counter(bool enable) -{ - const uint32_t old_value = user_reg_read32(RB_CHOOSE_BEAMS); - const uint32_t new_value = enable ? - old_value | EISCAT_OUTPUT_COUNTER - : (old_value & ~EISCAT_OUTPUT_COUNTER) - ; - configure_beams(new_value); -} - -bool eiscat_radio_ctrl_impl::assert_jesd_cores_initialized() -{ - if (_num_dboards == 1) { - return _rpcc->request_with_token<bool>("db_0_init_jesd_core_reset_adcs"); - } - return _rpcc->request_with_token<bool>("db_0_init_jesd_core_reset_adcs") - and _rpcc->request_with_token<bool>("db_1_init_jesd_core_reset_adcs"); -} - -bool eiscat_radio_ctrl_impl::assert_adcs_deframers() -{ - if (_num_dboards == 1) { - return _rpcc->request_with_token<bool>("db_0_init_adcs_and_deframers"); - } - return _rpcc->request_with_token<bool>("db_0_init_adcs_and_deframers") - and _rpcc->request_with_token<bool>("db_1_init_adcs_and_deframers"); -} - -bool eiscat_radio_ctrl_impl::assert_deframer_status() -{ - if (_num_dboards == 1) { - return _rpcc->request_with_token<bool>("db_0_check_deframer_status"); - } - return _rpcc->request_with_token<bool>("db_0_check_deframer_status") - and _rpcc->request_with_token<bool>("db_1_check_deframer_status"); -} - -/**************************************************************************** - * Registry - ***************************************************************************/ -UHD_RFNOC_BLOCK_REGISTER(eiscat_radio_ctrl, "EISCATRadio"); diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp deleted file mode 100644 index 3ce0b48fd..000000000 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp +++ /dev/null @@ -1,295 +0,0 @@ -// -// Copyright 2017 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_LIBUHD_RFNOC_EISCAT_RADIO_CTRL_IMPL_HPP -#define INCLUDED_LIBUHD_RFNOC_EISCAT_RADIO_CTRL_IMPL_HPP - -#include <uhd/types/direction.hpp> -#include <uhdlib/rfnoc/rpc_block_ctrl.hpp> -#include <uhdlib/rfnoc/radio_ctrl_impl.hpp> - -namespace uhd { - namespace rfnoc { - -/*! \brief Provide access to an EISCAT radio, including beamformer. - * - * Note: This will control both daughterboards. Since we have a single RFNoC - * block, we only have one of these per motherboard. - * - * EISCAT radios have a whole bunch of features which don't have APIs provided - * by radio_ctrl. This means the most interesting features are controlled by - * set_arg() and get_arg(). Notable exception is set_rx_antenna(), which is - * heavily abused for all sorts of things. - * - * List of relevant args: - * - sysref (bool): Write to this to trigger a SYSREF pulse to *both* - * daughterboards. Will honor command time. Will always return - * true when read. - * - gain (double): Set the gain for antenna X, where X is the set_arg() `port` - * value. The gain is normalized in [0,1]. Can be read to get - * the current value. Example: `set_arg("gain", 0.5, 5)` will - * set the digital gain for antenna 5 to mid-point. - * - fir_ctrl_time (time_spec_t): This time will be used for following - * fir_select writes. Will return the last value - * that was written. - * - fir_select (int): Will queue a filter for manipulating a specific - * contribution. The value is the filter index in the BRAM. - * The port parameter specifies which filter; filters are - * indexed 0...159 using the equation beam_index * 16 + - * antenna_idx. Example: `set_arg("fir_select", 357, 16)` - * will apply filter number 357 to the zeroth antenna for - * beam number 1 (i.e. the second beam). Returns the last - * value that was written. May be incorrect before written - * for the first time. - * - fir_taps (vector<int32_t>): Updates FIR tap values in the BRAM. Port is - * the filter index. Will always return an impulse - * response, not the actual filter value. - * - assert_adcs_deframers (bool): Writing this does nothing. Reading it back - * will run the initialization of ADCs and - * deframers. Return value is success. - * - assert_deframer_status (bool): Writing this does nothing. Reading it will - * run the final step of the JESD deframer - * initialization routine. Returns success. - * - choose_beams (int): Configures beam selection (upper, lower, are neighbour - * contributions included). See set_beam_selection() for - * details. - * - enable_firs (int): Can be used to disable fir FIR matrix. This routes the - * JESD output directly to the noc_shell. - * - enable_counter (int): If the feature is available in the given FPGA image, - * setting this to true will disable the JESD core - * output and will input a counter signal (ramp) - * instead. - * - configure_beams (int): Danger, danger: Directly writes the - * SR_BEAMS_TO_NEIGHBOR register. Writing this can put - * some of the other properties out of sync, because - * writing to those will also write to this, but not - * vice versa. - * - * - * ## Time-aligned synchronization sequence: - * - * 0. Make sure all devices are getting the same ref clock and PPS! - * 1. Call set_command_time() with the same time on all blocks (make it far - * enough in the future) - * 2. Call set_arg<bool>("sysref") on all blocks. This should SYSREF all dboards - * synchronously. - * 3. On all blocks, call get_arg<bool>("assert_adcs_deframers") and verify it - * returns true. - * 4. Repeat steps 1 and 2 with, obviously, another time that's in the future. - * 5. On all blocks, call get_arg<bool>("assert_deframer_status") and make sure - * it returned true. - */ -class eiscat_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl -{ -public: - using sptr = boost::shared_ptr<eiscat_radio_ctrl_impl>; - using fir_tap_t = int32_t; // See also EISCAT_BITS_PER_TAP - - /************************************************************************ - * Structors - ***********************************************************************/ - UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(eiscat_radio_ctrl) - virtual ~eiscat_radio_ctrl_impl(); - - /************************************************************************ - * API calls - * Note: Tx calls are here mostly to throw errors. - ***********************************************************************/ - //! Returns the actual tick rate. Will display a warning if rate is not that - // value. - double set_rate(double rate); - - //! \throws uhd::runtime_error - void set_tx_antenna(const std::string &ant, const size_t chan); - - /*! Configures FPGA switching for antenna selection - * - * Valid antenna values: - * - BF: This is the default. Will apply the beamforming matrix in whatever - * state it currently is. - * - RX0...RX15: Will mux the antenna signal 0...15 straight to this - * channel. Note that this will disable the FIR matrix entirely, and will - * also disable contributions from other USRPs globally. - * - BF0...BF15: Will configure the FIR filter matrix such that only the - * contributions from antenna 0...15 are passed to this channel. This - * should produce the same signal as RX0..RX15, reduced by 12 dB (because - * the FIR matri needs to account for bit growth from adding 16 channels). - * Will also disable contributions from other channels globally. - * - FI$idx: Here, $idx is a number (the filter index, hence the name). - * This will apply filter index $idx to all input channels. Useful for - * testing actual beamforming applications, when the same signal is - * applied to all inputs. - * - * Note that this is very useful for testing and debugging. For actual - * beamforming operations, this API call won't be enough. Rather, set this - * to 'BF' (or don't do anything) and use the block properties - * - * \throws uhd::value_error if the antenna value was not valid - */ - void set_rx_antenna(const std::string &ant, const size_t chan); - - //! \throws uhd::runtime_error - double set_tx_frequency(const double freq, const size_t chan); - //! \returns Some value in the EISCAT passband - double set_rx_frequency(const double freq, const size_t chan); - //! \returns Width of the EISCAT analog frontend filters - double set_rx_bandwidth(const double bandwidth, const size_t chan); - //! \throws uhd::runtime_error - double get_tx_frequency(const size_t chan); - - //! \throws uhd::runtime_error - double set_tx_gain(const double gain, const size_t chan); - //! \returns zero - double set_rx_gain(const double gain, const size_t chan); - - size_t get_chan_from_dboard_fe(const std::string &fe, const uhd::direction_t dir); - std::string get_dboard_fe_from_chan(const size_t chan, const uhd::direction_t dir); - - //! \returns The EISCAT sampling rate - double get_output_samp_rate(size_t port); - - void set_rx_streamer(bool active, const size_t port); - void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t port); - -protected: - virtual bool check_radio_config(); - - /*! Finalize initialization sequence (ADCs, deframers) etc. - */ - void set_rpc_client( - uhd::rpc_client::sptr rpcc, - const uhd::device_addr_t &block_args - ); - -private: - /************************************************************************* - * Private methods - * To control the dboard (and execute these), take a look at the block - * properties. - ************************************************************************/ - /*! Write filter taps for a specific FIR filter. - * - * Note: If the number of taps is smaller than the number of available - * filter taps, it is padded with zero (i.e., all taps are always written - * and this can't be use to partially update filters). - * - * \param fir_idx The index of the FIR filter we are reprogramming - * \param taps A list of FIR filter taps for this filter. - * - * \throws uhd::value_error if the number of taps is longer than the number - * of taps that the filter can handle, or if any - * tap has more bits than allowed. - */ - void write_fir_taps( - const size_t fir_idx, - const std::vector<fir_tap_t> &taps - ); - - /*! Choose a filter to be applied between an output beam and antenna input - * - * \param beam_index Beam index - * \param antenna_index Antenna index - * \param fir_index The index of the FIR filter taps that get applied - * \param time_spec If non-zero, the taps get applied at this time. - * Otherwise, they get sent out now. - * \param write_time If false, time will never get written *even if* it is - * non-zero. The assumption is that someone else wrote - * the value previously - * \param write_time If false, time will never get written *even if* it is - * non-zero. The assumption is that someone else wrote - * the value previously - */ - void select_filter( - const size_t beam_index, - const size_t antenna_index, - const size_t fir_index, - const uhd::time_spec_t &time_spec, - const bool write_time=true - ); - - - /*! Sets the command time for the next call to select_filter() - * - * \param time_spec This value gets written to the FPGA and is applied to - * *all* subsequent filter selections. To request - * immediate application of filters, set this to zero. - */ - void set_fir_ctrl_time(const uhd::time_spec_t &time_spec); - - /*! Sets the digital gain on a specific antenna - * - * \param antenna_idx Antenna for which this gain setting applies - * \param normalized_gain A value in [0, 1] which gets converted to a - * digital gain value - */ - void set_antenna_gain( - const size_t antenna_idx, - const double normalized_gain - ); - - /*! Directly writes a value to the beam configuration register. - */ - void configure_beams(uint32_t reg_value); - - /*! Controls selection of beams coming from the FIR matrix. - * - * The following values are allowed: - * - 0: We stream the lower 5 beams, plus the neighbours contribution - * - 1: We stream the upper 5 beams, plus the neighbours contribution - * - 2: We stream the lower 5 beams, without the neighbours contribution - * - 3: We stream the upper 5 beams, without the neighbours contribution - */ - void set_beam_selection(int beam_selection); - - /*! Controls if we're using the FIR matrix - * - * If this is false, the beam selection is irrelevant. - */ - void enable_firs(bool enable); - - /*! Enables counter instead of JESD core output - */ - void enable_counter(bool enable); - - //! Sends a SYSREF pulse. Device arg use_mpm_sysref can be used to send it - // via MPM. Default is to send it via CHDR, in which case calling this - // function *will modify the command time!*, but it will ensure that the - // sysref is sent on an even time - void send_sysref(); - - //! Run initialization of JESD cores, put ADCs into reset - bool assert_jesd_cores_initialized(); - - //! Run initialization of ADCs and deframers; returns success status - bool assert_adcs_deframers(); - - //! Run final step of JESD core setup; returns success status - bool assert_deframer_status(); - - /*! The number of channels this block outputs - * - * This is *not* the number of antennas, but the number of streams a single - * block outputs to the crossbar. - */ - size_t _num_ports; - - //! Running with 1 dboard is theoretically possible; thus, store the - // number of active dboards. - size_t _num_dboards = 0; - - //! Additional block args; gets set during set_rpc_client() - uhd::device_addr_t _block_args; - - /*! Reference to the RPC client - */ - uhd::rpc_client::sptr _rpcc; - -}; /* class radio_ctrl_impl */ - -}} /* namespace uhd::rfnoc */ - -#endif /* INCLUDED_LIBUHD_RFNOC_EISCAT_RADIO_CTRL_IMPL_HPP */ - diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp index dc78cee7d..13186e146 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp @@ -1,5 +1,6 @@ // // Copyright 2017 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: GPL-3.0-or-later // @@ -8,9 +9,7 @@ #include "magnesium_constants.hpp" #include "magnesium_gain_table.hpp" #include <uhd/exception.hpp> -#include <uhd/rfnoc/node_ctrl_base.hpp> #include <uhd/rfnoc/registry.hpp> -#include <uhd/transport/chdr.hpp> #include <uhd/types/direction.hpp> #include <uhd/types/eeprom.hpp> #include <uhd/utils/algorithm.hpp> diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp index db2ec9494..d8a1ccba8 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp @@ -6,7 +6,6 @@ #include "magnesium_constants.hpp" #include "magnesium_radio_control.hpp" -#include <uhd/transport/chdr.hpp> #include <uhd/types/eeprom.hpp> #include <uhd/types/sensors.hpp> #include <uhd/utils/log.hpp> diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp index a3b072e74..df2c3aadd 100644 --- a/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp +++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp @@ -9,7 +9,6 @@ #include "rhodium_constants.hpp" #include <uhd/exception.hpp> #include <uhd/rfnoc/registry.hpp> -#include <uhd/transport/chdr.hpp> #include <uhd/types/direction.hpp> #include <uhd/types/eeprom.hpp> #include <uhd/utils/algorithm.hpp> diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp index d6b7afd09..850e5aff3 100644 --- a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp +++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp @@ -7,7 +7,6 @@ #include "rhodium_constants.hpp" #include "rhodium_radio_control.hpp" -#include <uhd/transport/chdr.hpp> #include <uhd/types/eeprom.hpp> #include <uhd/types/sensors.hpp> #include <uhd/utils/algorithm.hpp> diff --git a/host/lib/usrp/device3/CMakeLists.txt b/host/lib/usrp/device3/CMakeLists.txt deleted file mode 100644 index 979225c2c..000000000 --- a/host/lib/usrp/device3/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright 2014 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 -######################################################################## - -LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/device3_impl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/device3_io_impl.cpp -) diff --git a/host/lib/usrp/device3/device3_flow_ctrl.hpp b/host/lib/usrp/device3/device3_flow_ctrl.hpp deleted file mode 100644 index fd445effd..000000000 --- a/host/lib/usrp/device3/device3_flow_ctrl.hpp +++ /dev/null @@ -1,306 +0,0 @@ -// -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_DEVICE3_FLOW_CTRL_HPP -#define INCLUDED_DEVICE3_FLOW_CTRL_HPP - -#include "device3_impl.hpp" -#include <uhd/transport/vrt_if_packet.hpp> -#include <uhd/transport/zero_copy.hpp> -#include <uhd/types/sid.hpp> -#include <uhd/utils/log.hpp> -#include <boost/shared_ptr.hpp> - -namespace uhd { namespace usrp { - -//! Stores the state of RX flow control -struct rx_fc_cache_t -{ - rx_fc_cache_t() - : interval(0) - , last_byte_count(0) - , total_bytes_consumed(0) - , total_packets_consumed(0) - , seq_num(0) - { - } - - //! Flow control interval in bytes - size_t interval; - //! Byte count at last flow control packet - uint32_t last_byte_count; - //! This will wrap around, but that's OK, because math. - uint32_t total_bytes_consumed; - //! This will wrap around, but that's OK, because math. - uint32_t total_packets_consumed; - //! Sequence number of next flow control packet - uint64_t seq_num; - uhd::sid_t sid; - uhd::transport::zero_copy_if::sptr xport; - std::function<uint32_t(uint32_t)> to_host; - std::function<uint32_t(uint32_t)> from_host; - std::function<void( - const uint32_t* packet_buff, uhd::transport::vrt::if_packet_info_t&)> - unpack; - std::function<void(uint32_t* packet_buff, uhd::transport::vrt::if_packet_info_t&)> - pack; -}; - -/*! Send out RX flow control packets. - * - * This function handles updating the counters for the consumed - * bytes and packets, determines if a flow control message is - * is necessary, and sends one if it is. Passing a nullptr for - * the buff parameter will skip the counter update. - * - * \param fc_cache RX flow control state information - * \param buff Receive buffer. Setting to nullptr will - * skip the counter update. - */ -inline bool rx_flow_ctrl( - boost::shared_ptr<rx_fc_cache_t> fc_cache, uhd::transport::managed_buffer::sptr buff) -{ - // If the caller supplied a buffer - if (buff) { - // Unpack the header - uhd::transport::vrt::if_packet_info_t packet_info; - packet_info.num_packet_words32 = buff->size() / sizeof(uint32_t); - const uint32_t* pkt = buff->cast<const uint32_t*>(); - try { - fc_cache->unpack(pkt, packet_info); - } catch (const std::exception& ex) { - // Log and ignore - UHD_LOGGER_ERROR("RX FLOW CTRL") - << "Error unpacking packet: " << ex.what() << std::endl; - return true; - } - - // Update counters assuming the buffer is a consumed packet - if (not packet_info.error) { - const size_t bytes = 4 * (packet_info.num_header_words32 + packet_info.num_payload_words32); - fc_cache->total_bytes_consumed += bytes; - fc_cache->total_packets_consumed++; - } - } - - // Just return if there is no need to send a flow control packet - if (fc_cache->total_bytes_consumed - fc_cache->last_byte_count < fc_cache->interval) { - return true; - } - - // Time to send a flow control packet - // Get a send buffer - uhd::transport::managed_send_buffer::sptr fc_buff = - fc_cache->xport->get_send_buff(0.0); - if (not fc_buff) { - throw uhd::runtime_error("rx_flowctrl timed out getting a send buffer"); - } - uint32_t* pkt = fc_buff->cast<uint32_t*>(); - - // load packet info - uhd::transport::vrt::if_packet_info_t packet_info; - packet_info.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_FC; - packet_info.num_payload_words32 = uhd::usrp::DEVICE3_FC_PACKET_LEN_IN_WORDS32; - packet_info.num_payload_bytes = packet_info.num_payload_words32 * sizeof(uint32_t); - packet_info.packet_count = fc_cache->seq_num++; - packet_info.sob = false; - packet_info.eob = false; - packet_info.error = false; - packet_info.fc_ack = false; - packet_info.sid = fc_cache->sid.get(); - packet_info.has_sid = true; - packet_info.has_cid = false; - packet_info.has_tsi = false; - packet_info.has_tsf = false; - packet_info.has_tlr = false; - - // Load Header: - fc_cache->pack(pkt, packet_info); - // Load Payload: Packet count, and byte count - pkt[packet_info.num_header_words32 + uhd::usrp::DEVICE3_FC_PACKET_COUNT_OFFSET] = - fc_cache->from_host(fc_cache->total_packets_consumed); - pkt[packet_info.num_header_words32 + uhd::usrp::DEVICE3_FC_BYTE_COUNT_OFFSET] = - fc_cache->from_host(fc_cache->total_bytes_consumed); - - // send the buffer over the interface - fc_buff->commit(sizeof(uint32_t) * (packet_info.num_packet_words32)); - - // update byte count - fc_cache->last_byte_count = fc_cache->total_bytes_consumed; - - return true; -} - -/*! Handle RX flow control ACK packets. - * - */ -inline void handle_rx_flowctrl_ack( - boost::shared_ptr<rx_fc_cache_t> fc_cache, const uint32_t* payload) -{ - const uint32_t pkt_count = fc_cache->to_host(payload[0]); - const uint32_t byte_count = fc_cache->to_host(payload[1]); - if (fc_cache->total_bytes_consumed != byte_count) { - UHD_LOGGER_DEBUG("device3") - << "oh noes: byte_count==" << byte_count - << " total_bytes_consumed==" << fc_cache->total_bytes_consumed << std::hex - << " sid==" << fc_cache->sid << std::dec << std::endl; - } - fc_cache->total_bytes_consumed = byte_count; - fc_cache->total_packets_consumed = pkt_count; // guess we need a pkt offset too? - - // This will send a flow control packet if there is a significant discrepancy - rx_flow_ctrl(fc_cache, nullptr); -} - -//! Stores the state of TX flow control -struct tx_fc_cache_t -{ - tx_fc_cache_t(uint32_t capacity) - : last_byte_ack(0) - , last_seq_ack(0) - , byte_count(0) - , pkt_count(0) - , window_size(capacity) - , fc_ack_seqnum(0) - , fc_received(false) - { - } - - uint32_t last_byte_ack; - uint32_t last_seq_ack; - uint32_t byte_count; - uint32_t pkt_count; - uint32_t window_size; - uint32_t fc_ack_seqnum; - bool fc_received; - std::function<uint32_t(uint32_t)> to_host; - std::function<uint32_t(uint32_t)> from_host; - std::function<void( - const uint32_t* packet_buff, uhd::transport::vrt::if_packet_info_t&)> - unpack; - std::function<void(uint32_t* packet_buff, uhd::transport::vrt::if_packet_info_t&)> - pack; -}; - -inline bool tx_flow_ctrl(boost::shared_ptr<tx_fc_cache_t> fc_cache, - uhd::transport::zero_copy_if::sptr xport, - uhd::transport::managed_buffer::sptr buff) -{ - while (true) { - // If there is space - if (fc_cache->window_size - (fc_cache->byte_count - fc_cache->last_byte_ack) - >= buff->size()) { - // All is good - packet will be sent - fc_cache->byte_count += buff->size(); - // Round up to nearest word - if (fc_cache->byte_count % uhd::usrp::DEVICE3_LINE_SIZE) { - fc_cache->byte_count += - uhd::usrp::DEVICE3_LINE_SIZE - - (fc_cache->byte_count % uhd::usrp::DEVICE3_LINE_SIZE); - } - fc_cache->pkt_count++; - return true; - } - - // Look for a flow control message to update the space available in the buffer. - uhd::transport::managed_recv_buffer::sptr buff = xport->get_recv_buff(0.1); - if (buff) { - uhd::transport::vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = buff->size() / sizeof(uint32_t); - const uint32_t* packet_buff = buff->cast<const uint32_t*>(); - try { - fc_cache->unpack(packet_buff, if_packet_info); - } catch (const std::exception& ex) { - UHD_LOGGER_ERROR("TX FLOW CTRL") - << "Error unpacking flow control packet: " << ex.what() << std::endl; - continue; - } - - if (if_packet_info.packet_type - != uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_FC) { - UHD_LOGGER_ERROR("TX FLOW CTRL") - << "Unexpected packet received by flow control handler: " - << if_packet_info.packet_type << std::endl; - continue; - } - - const uint32_t* payload = &packet_buff[if_packet_info.num_header_words32]; - const uint32_t pkt_count = fc_cache->to_host(payload[0]); - const uint32_t byte_count = fc_cache->to_host(payload[1]); - - // update the amount of space - fc_cache->last_byte_ack = byte_count; - fc_cache->last_seq_ack = pkt_count; - - fc_cache->fc_received = true; - } - } - return false; -} - -inline void tx_flow_ctrl_ack(boost::shared_ptr<tx_fc_cache_t> fc_cache, - uhd::transport::zero_copy_if::sptr send_xport, - uhd::sid_t send_sid) -{ - if (not fc_cache->fc_received) { - return; - } - - // Time to send a flow control ACK packet - // Get a send buffer - uhd::transport::managed_send_buffer::sptr fc_buff = send_xport->get_send_buff(0.0); - if (not fc_buff) { - UHD_LOGGER_ERROR("tx_flow_ctrl_ack") << "timed out getting a send buffer"; - return; - } - uint32_t* pkt = fc_buff->cast<uint32_t*>(); - - // Load packet info - uhd::transport::vrt::if_packet_info_t packet_info; - packet_info.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_ACK; - packet_info.num_payload_words32 = uhd::usrp::DEVICE3_FC_PACKET_LEN_IN_WORDS32; - packet_info.num_payload_bytes = packet_info.num_payload_words32 * sizeof(uint32_t); - packet_info.packet_count = fc_cache->fc_ack_seqnum++; - packet_info.sob = false; - packet_info.eob = true; - packet_info.error = false; - packet_info.fc_ack = false; - packet_info.sid = send_sid.get(); - packet_info.has_sid = true; - packet_info.has_cid = false; - packet_info.has_tsi = false; - packet_info.has_tsf = false; - packet_info.has_tlr = false; - - // Load Header: - fc_cache->pack(pkt, packet_info); - - // Update counters to include this packet - size_t fc_ack_pkt_size = sizeof(uint32_t) * (packet_info.num_packet_words32); - fc_cache->byte_count += fc_ack_pkt_size; - // Round up to nearest word - if (fc_cache->byte_count % uhd::usrp::DEVICE3_LINE_SIZE) { - fc_cache->byte_count += uhd::usrp::DEVICE3_LINE_SIZE - - (fc_cache->byte_count % uhd::usrp::DEVICE3_LINE_SIZE); - } - fc_cache->pkt_count++; - - // Load Payload: Packet count, and byte count - pkt[packet_info.num_header_words32 + uhd::usrp::DEVICE3_FC_PACKET_COUNT_OFFSET] = - fc_cache->from_host(fc_cache->pkt_count); - pkt[packet_info.num_header_words32 + uhd::usrp::DEVICE3_FC_BYTE_COUNT_OFFSET] = - fc_cache->from_host(fc_cache->byte_count); - - // Send the buffer over the interface - fc_buff->commit(fc_ack_pkt_size); - - // Reset for next FC - fc_cache->fc_received = false; -} - -}}; // namespace uhd::usrp - -#endif /* INCLUDED_DEVICE3_FLOW_CTRL_HPP */ diff --git a/host/lib/usrp/device3/device3_impl.cpp b/host/lib/usrp/device3/device3_impl.cpp deleted file mode 100644 index bc1cf9002..000000000 --- a/host/lib/usrp/device3/device3_impl.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// -// Copyright 2014-2016 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "device3_impl.hpp" -#include <uhd/rfnoc/block_ctrl_base.hpp> -#include <uhd/utils/log.hpp> -#include <uhdlib/rfnoc/ctrl_iface.hpp> -#include <uhdlib/rfnoc/graph_impl.hpp> -#include <boost/make_shared.hpp> -#include <algorithm> - -using namespace uhd::usrp; - -device3_impl::device3_impl() -{ - _type = uhd::device::USRP; - _async_md.reset(new async_md_type(1000 /*messages deep*/)); - _tree = uhd::property_tree::make(); -}; - -//! Returns true if the integer value stored in lhs is smaller than that in rhs -bool _compare_string_indexes(const std::string& lhs, const std::string& rhs) -{ - return boost::lexical_cast<size_t>(lhs) < boost::lexical_cast<size_t>(rhs); -} - -void device3_impl::merge_channel_defs(const std::vector<uhd::rfnoc::block_id_t>& chan_ids, - const std::vector<uhd::device_addr_t>& chan_args, - const uhd::direction_t dir) -{ - UHD_ASSERT_THROW(chan_ids.size() == chan_args.size()); - if (dir == uhd::DX_DIRECTION) { - merge_channel_defs(chan_ids, chan_args, RX_DIRECTION); - merge_channel_defs(chan_ids, chan_args, TX_DIRECTION); - return; - } - - uhd::fs_path chans_root = - uhd::fs_path("/channels/") / (dir == RX_DIRECTION ? "rx" : "tx"); - // Store the new positions of the channels: - std::vector<size_t> chan_idxs; - - // 1. Get sorted list of currently defined channels - std::vector<std::string> curr_channels; - if (_tree->exists(chans_root)) { - curr_channels = _tree->list(chans_root); - std::sort(curr_channels.begin(), curr_channels.end(), _compare_string_indexes); - } - - // 2. Cycle through existing channels to find out where to merge - // the new channels. Rules are: - // - The order of chan_ids must be preserved - // - All block indices that are in chan_ids may be overwritten in the channel - // definition - // - If the channels in chan_ids are not yet in the property tree channel list, - // they are appended. - for (const std::string& chan_idx : curr_channels) { - if (_tree->exists(chans_root / chan_idx)) { - rfnoc::block_id_t chan_block_id = - _tree->access<rfnoc::block_id_t>(chans_root / chan_idx).get(); - if (std::find(chan_ids.begin(), chan_ids.end(), chan_block_id) - != chan_ids.end()) { - chan_idxs.push_back(boost::lexical_cast<size_t>(chan_idx)); - } - } - } - size_t last_chan_idx = curr_channels.empty() - ? 0 - : (boost::lexical_cast<size_t>(curr_channels.back()) + 1); - while (chan_idxs.size() < chan_ids.size()) { - chan_idxs.push_back(last_chan_idx); - last_chan_idx++; - } - - // 3. Write the new channels - for (size_t i = 0; i < chan_ids.size(); i++) { - if (not _tree->exists(chans_root / chan_idxs[i])) { - _tree->create<rfnoc::block_id_t>(chans_root / chan_idxs[i]); - } - _tree->access<rfnoc::block_id_t>(chans_root / chan_idxs[i]).set(chan_ids[i]); - if (not _tree->exists(chans_root / chan_idxs[i] / "args")) { - _tree->create<uhd::device_addr_t>(chans_root / chan_idxs[i] / "args"); - } - _tree->access<uhd::device_addr_t>(chans_root / chan_idxs[i] / "args") - .set(chan_args[i]); - } -} - -/*********************************************************************** - * RFNoC-Specific - **********************************************************************/ -void device3_impl::enumerate_rfnoc_blocks(size_t device_index, - size_t n_blocks, - size_t base_port, - const uhd::sid_t& base_sid, - uhd::device_addr_t transport_args) -{ - // entries that are already connected to this block - uhd::sid_t ctrl_sid = base_sid; - uhd::property_tree::sptr subtree = - _tree->subtree(uhd::fs_path("/mboards") / device_index); - // 1) Clean property tree entries - // TODO put this back once radios are actual rfnoc blocks!!!!!! - // if (subtree->exists("xbar")) { - // subtree->remove("xbar"); - //} - // 2) Destroy existing block controllers - // TODO: Clear out all the old block control classes - // 3) Create new block controllers - for (size_t i = 0; i < n_blocks; i++) { - // First, make a transport for port number zero, because we always need that: - ctrl_sid.set_dst_xbarport(base_port + i); - ctrl_sid.set_dst_blockport(0); - both_xports_t xport = this->make_transport(ctrl_sid, CTRL, transport_args); - UHD_LOG_TRACE("DEVICE3", - str(boost::format("Setting up NoC-Shell Control for port #0 (SID: %s)...") - % xport.send_sid.to_pp_string_hex())); - uhd::rfnoc::ctrl_iface::sptr ctrl = uhd::rfnoc::ctrl_iface::make(xport, - str(boost::format("CE_%02d_Port_%02X") % i % ctrl_sid.get_dst_endpoint())); - uint64_t noc_id = ctrl->send_cmd_pkt( - uhd::rfnoc::SR_READBACK, uhd::rfnoc::SR_READBACK_REG_ID, true); - UHD_LOG_DEBUG("DEVICE3", - str(boost::format("Port 0x%02X: Found NoC-Block with ID %016X.") - % int(ctrl_sid.get_dst_endpoint()) % noc_id)); - uhd::rfnoc::make_args_t make_args; - uhd::rfnoc::blockdef::sptr block_def = - uhd::rfnoc::blockdef::make_from_noc_id(noc_id); - if (not block_def) { - UHD_LOG_WARNING("DEVICE3", - "No block definition found, using default block configuration " - "for block with NOC ID: " - + str(boost::format("0x%08X") % noc_id)); - block_def = - uhd::rfnoc::blockdef::make_from_noc_id(uhd::rfnoc::DEFAULT_NOC_ID_64); - } - UHD_ASSERT_THROW(block_def); - make_args.ctrl_ifaces[0] = ctrl; - for (const size_t port_number : block_def->get_all_port_numbers()) { - if (port_number == 0) { // We've already set this up - continue; - } - ctrl_sid.set_dst_blockport(port_number); - both_xports_t xport1 = this->make_transport(ctrl_sid, CTRL, transport_args); - UHD_LOG_TRACE("DEVICE3", - str(boost::format("Setting up NoC-Shell Control for port #%d " - "(SID: %s)...") - % port_number % xport1.send_sid.to_pp_string_hex())); - uhd::rfnoc::ctrl_iface::sptr ctrl1 = uhd::rfnoc::ctrl_iface::make(xport1, - str(boost::format("CE_%02d_Port_%02X") % i - % ctrl_sid.get_dst_endpoint())); - make_args.ctrl_ifaces[port_number] = ctrl1; - } - UHD_LOG_TRACE("DEVICE3", - "All control transports successfully created for block with ID " - << str(boost::format("0x%08X") % noc_id)); - - make_args.base_address = xport.send_sid.get_dst(); - make_args.device_index = device_index; - make_args.tree = subtree; - { // Critical section for block_ctrl vector access - boost::lock_guard<boost::mutex> lock(_block_ctrl_mutex); - _rfnoc_block_ctrl.push_back( - uhd::rfnoc::block_ctrl_base::make(make_args, noc_id)); - _rfnoc_block_ctrl.back()->set_graph_update_cb([this]() { - update_rx_streamers(); - update_tx_streamers(); - }); - } - } -} - - -uhd::rfnoc::graph::sptr device3_impl::create_graph(const std::string& name) -{ - // Create an async message handler - UHD_LOGGER_TRACE("DEVICE3") - << "Creating async message handler for graph `" << name << "'..."; - // FIXME: right now this only can only handle source sid of 0 and xbar local addr - // of 2. This is ok for now because that most of our device has xbard local addr - // hardcode to 2. - sid_t async_sid(0); - async_sid.set_dst_addr(2); - both_xports_t async_xports = make_transport(async_sid, - ASYNC_MSG, - // FIXME: only get rx_hints from mb index of 0 - get_rx_hints(0)); - UHD_LOGGER_TRACE("DEVICE3") << " Async transport ready." << std::endl; - uhd::rfnoc::async_msg_handler::sptr async_msg_handler = - uhd::rfnoc::async_msg_handler::make(async_xports.recv, - async_xports.send, - async_xports.send_sid, - async_xports.endianness); - UHD_LOGGER_TRACE("DEVICE3") - << "Async message has address " << async_xports.send_sid << std::endl; - - // Create the graph - UHD_LOGGER_TRACE("DEVICE3") << "Creating graph `" << name << "'..." << std::endl; - uhd::rfnoc::graph::sptr graph = boost::make_shared<uhd::rfnoc::graph_impl>( - name, shared_from_this(), async_msg_handler); - - return graph; -} diff --git a/host/lib/usrp/device3/device3_impl.hpp b/host/lib/usrp/device3/device3_impl.hpp deleted file mode 100644 index 17f6a3f6f..000000000 --- a/host/lib/usrp/device3/device3_impl.hpp +++ /dev/null @@ -1,264 +0,0 @@ -// -// Copyright 2014-2015 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -// Declares the device3_impl class which is a layer between device3 and -// the different 3-rd gen device impls (e.g. x300_impl) - -#ifndef INCLUDED_DEVICE3_IMPL_HPP -#define INCLUDED_DEVICE3_IMPL_HPP - -#include "../../transport/super_recv_packet_handler.hpp" -#include "../../transport/super_send_packet_handler.hpp" -#include <uhd/device3.hpp> -#include <uhd/transport/bounded_buffer.hpp> -#include <uhd/transport/chdr.hpp> -#include <uhd/transport/vrt_if_packet.hpp> -#include <uhd/transport/zero_copy.hpp> -#include <uhd/types/direction.hpp> -#include <uhd/types/endianness.hpp> -#include <uhd/types/metadata.hpp> -#include <uhd/types/sid.hpp> -#include <uhd/utils/tasks.hpp> -#include <uhdlib/rfnoc/rx_stream_terminator.hpp> -#include <uhdlib/rfnoc/tx_stream_terminator.hpp> -#include <uhdlib/rfnoc/xports.hpp> - -namespace uhd { namespace usrp { - -/*********************************************************************** - * Default settings (any device3 may override these) - **********************************************************************/ -static const size_t DEVICE3_RX_FC_REQUEST_FREQ = 32; // per flow-control window -static const size_t DEVICE3_TX_FC_RESPONSE_FREQ = 8; -static const size_t DEVICE3_FC_PACKET_LEN_IN_WORDS32 = 2; -static const size_t DEVICE3_FC_PACKET_COUNT_OFFSET = 0; -static const size_t DEVICE3_FC_BYTE_COUNT_OFFSET = 1; -static const size_t DEVICE3_LINE_SIZE = 8; - -static const size_t DEVICE3_TX_MAX_HDR_LEN = - uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t); // Bytes -static const size_t DEVICE3_RX_MAX_HDR_LEN = - uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t); // Bytes - -// This class manages the lifetime of the TX async message handler task, transports, and -// terminator -class device3_send_packet_streamer : public uhd::transport::sph::send_packet_streamer -{ -public: - device3_send_packet_streamer(const size_t max_num_samps, - const uhd::rfnoc::tx_stream_terminator::sptr terminator, - const both_xports_t data_xport, - const both_xports_t async_msg_xport) - : uhd::transport::sph::send_packet_streamer(max_num_samps) - , _terminator(terminator) - , _data_xport(data_xport) - , _async_msg_xport(async_msg_xport) - { - } - - ~device3_send_packet_streamer() - { - // Make sure the async task is destroyed before the transports - _tx_async_msg_tasks.clear(); - } - - uhd::rfnoc::tx_stream_terminator::sptr get_terminator() - { - return _terminator; - } - - void add_async_msg_task(task::sptr task) - { - _tx_async_msg_tasks.push_back(task); - } - -private: - uhd::rfnoc::tx_stream_terminator::sptr _terminator; - both_xports_t _data_xport; - both_xports_t _async_msg_xport; - std::vector<task::sptr> _tx_async_msg_tasks; -}; - -// This class manages the lifetime of the RX transports and terminator and provides access -// to both -class device3_recv_packet_streamer : public uhd::transport::sph::recv_packet_streamer -{ -public: - device3_recv_packet_streamer(const size_t max_num_samps, - const uhd::rfnoc::rx_stream_terminator::sptr terminator, - const both_xports_t xport) - : uhd::transport::sph::recv_packet_streamer(max_num_samps) - , _terminator(terminator) - , _xport(xport) - { - } - - ~device3_recv_packet_streamer() {} - - both_xports_t get_xport() - { - return _xport; - } - - uhd::rfnoc::rx_stream_terminator::sptr get_terminator() - { - return _terminator; - } - -private: - uhd::rfnoc::rx_stream_terminator::sptr _terminator; - both_xports_t _xport; -}; - -class device3_impl : public uhd::device3, - public boost::enable_shared_from_this<device3_impl> -{ -public: - /*********************************************************************** - * device3-specific Types - **********************************************************************/ - typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; - - //! The purpose of a transport - enum xport_type_t { CTRL = 0, ASYNC_MSG, TX_DATA, RX_DATA }; - - enum xport_t { AXI, ETH, PCIE }; - - //! Stores all streaming-related options - struct stream_options_t - { - //! Max size of the header in bytes for TX - size_t tx_max_len_hdr; - //! Max size of the header in bytes for RX - size_t rx_max_len_hdr; - //! How often we send ACKs to the upstream block per one full FC window - size_t rx_fc_request_freq; - //! How often the downstream block should send ACKs per one full FC window - size_t tx_fc_response_freq; - stream_options_t(void) - : tx_max_len_hdr(DEVICE3_TX_MAX_HDR_LEN) - , rx_max_len_hdr(DEVICE3_RX_MAX_HDR_LEN) - , rx_fc_request_freq(DEVICE3_RX_FC_REQUEST_FREQ) - , tx_fc_response_freq(DEVICE3_TX_FC_RESPONSE_FREQ) - { - } - }; - - /*********************************************************************** - * I/O Interface - **********************************************************************/ - uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t&); - uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t&); - bool recv_async_msg(uhd::async_metadata_t& async_metadata, double timeout); - - /*********************************************************************** - * Other public APIs - **********************************************************************/ - rfnoc::graph::sptr create_graph(const std::string& name = ""); - -protected: - /*********************************************************************** - * Structors - **********************************************************************/ - device3_impl(); - virtual ~device3_impl() {} - - /*********************************************************************** - * Streaming-related - **********************************************************************/ -public: // TODO make these protected again - /*! Update tick rate, samp rate, and scaling on the streamers by querying - * the graph. - */ - void update_rx_streamers(); - /*! Update tick rate, samp rate, and scaling on the streamers by querying - * the graph. - */ - void update_tx_streamers(); - -protected: - /*********************************************************************** - * Transport-related - **********************************************************************/ - stream_options_t stream_options; - - /*! \brief Create a transport to a given endpoint. - * - * \param address The endpoint address of the block we're creating a transport to. - * The source address in this value is not considered, only the - * destination address. - * \param xport_type Specify which kind of transport this is. - * \param args Additional arguments for the transport generation. See \ref - * page_transport for valid arguments. - */ - virtual uhd::both_xports_t make_transport(const uhd::sid_t& address, - const xport_type_t xport_type, - const uhd::device_addr_t& args) = 0; - - virtual uhd::device_addr_t get_tx_hints(size_t) - { - return uhd::device_addr_t(); - } - virtual uhd::device_addr_t get_rx_hints(size_t) - { - return uhd::device_addr_t(); - } - - //! Is called after a streamer is generated - virtual void post_streamer_hooks(uhd::direction_t) {} - - //! get mtu - virtual size_t get_mtu(const size_t, const uhd::direction_t) = 0; - - /*********************************************************************** - * Channel-related - **********************************************************************/ - /*! Merge a list of channels into the existing channel definition. - * - * Intelligently merge the channels described in \p chan_ids - * into the current channel definition. If none of the channels in - * \p chan_ids is in the current definition, they simply get appended. - * Otherwise, they get overwritten in the order of \p chan_ids. - * - * \param chan_ids List of block IDs for the channels. - * \param chan_args New channel args. Must have same length as chan_ids. - * - */ - void merge_channel_defs(const std::vector<rfnoc::block_id_t>& chan_ids, - const std::vector<uhd::device_addr_t>& chan_args, - const uhd::direction_t dir); - - /*********************************************************************** - * RFNoC-Specific - **********************************************************************/ - void enumerate_rfnoc_blocks(size_t device_index, - size_t n_blocks, - size_t base_port, - const uhd::sid_t& base_sid, - uhd::device_addr_t transport_args); - - /*********************************************************************** - * Members - **********************************************************************/ - // TODO: Maybe move these to private - uhd::dict<std::string, boost::weak_ptr<uhd::rx_streamer>> _rx_streamers; - uhd::dict<std::string, boost::weak_ptr<uhd::tx_streamer>> _tx_streamers; - -private: - /*********************************************************************** - * Private Members - **********************************************************************/ - //! Buffer for async metadata - boost::shared_ptr<async_md_type> _async_md; - - //! This mutex locks the get_xx_stream() functions. - boost::mutex _transport_setup_mutex; -}; - -}} /* namespace uhd::usrp */ - -#endif /* INCLUDED_DEVICE3_IMPL_HPP */ diff --git a/host/lib/usrp/device3/device3_io_impl.cpp b/host/lib/usrp/device3/device3_io_impl.cpp deleted file mode 100644 index 800d2f5a8..000000000 --- a/host/lib/usrp/device3/device3_io_impl.cpp +++ /dev/null @@ -1,827 +0,0 @@ -// -// Copyright 2014-2016 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -// Provides streaming-related functions which are used by device3 objects. - -#include "device3_flow_ctrl.hpp" -#include "device3_impl.hpp" -#include <uhd/rfnoc/constants.hpp> -#include <uhd/rfnoc/radio_ctrl.hpp> -#include <uhd/rfnoc/rate_node_ctrl.hpp> -#include <uhd/rfnoc/sink_block_ctrl_base.hpp> -#include <uhd/rfnoc/source_block_ctrl_base.hpp> -#include <uhd/transport/zero_copy_flow_ctrl.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/log.hpp> -#include <uhdlib/rfnoc/rx_stream_terminator.hpp> -#include <uhdlib/rfnoc/tx_stream_terminator.hpp> -#include <uhdlib/usrp/common/async_packet_handler.hpp> -#include <boost/atomic.hpp> - -#define UHD_TX_STREAMER_LOG() UHD_LOGGER_TRACE("STREAMER") -#define UHD_RX_STREAMER_LOG() UHD_LOGGER_TRACE("STREAMER") - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::transport; - -/*********************************************************************** - * Helper functions for get_?x_stream() - **********************************************************************/ -static uhd::stream_args_t sanitize_stream_args(const uhd::stream_args_t& args_) -{ - uhd::stream_args_t args = args_; - if (args.channels.empty()) { - args.channels = std::vector<size_t>(1, 0); - } - - return args; -} - -static void check_stream_sig_compatible( - const rfnoc::stream_sig_t& stream_sig, stream_args_t& args, const std::string& tx_rx) -{ - if (args.otw_format.empty()) { - if (stream_sig.item_type.empty()) { - throw uhd::runtime_error( - str(boost::format("[%s Streamer] No otw_format defined!") % tx_rx)); - } else { - args.otw_format = stream_sig.item_type; - } - } else if (not stream_sig.item_type.empty() - and stream_sig.item_type != args.otw_format) { - throw uhd::runtime_error( - str(boost::format("[%s Streamer] Conflicting OTW types defined: " - "args.otw_format = '%s' <=> stream_sig.item_type = '%s'") - % tx_rx % args.otw_format % stream_sig.item_type)); - } - const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item - if (stream_sig.packet_size) { - if (args.args.has_key("spp")) { - size_t args_spp = args.args.cast<size_t>("spp", 0); - if (args_spp * bpi != stream_sig.packet_size) { - throw uhd::runtime_error( - str(boost::format( - "[%s Streamer] Conflicting packet sizes defined: args yields " - "%d bytes but stream_sig.packet_size is %d bytes") - % tx_rx % (args_spp * bpi) % stream_sig.packet_size)); - } - } else { - args.args["spp"] = str(boost::format("%d") % (stream_sig.packet_size / bpi)); - } - } -} - -/*! \brief Returns a list of rx or tx channels for a streamer. - * - * If the given stream args contain instructions to set up channels, - * those are used. Otherwise, the current device's channel definition - * is consulted. - * - * \param args_ Stream args. - * \param[out] chan_list The list of channels in the correct order. - * \param[out] chan_args Channel args for every channel. `chan_args.size() == - * chan_list.size()` - */ -void generate_channel_list(const uhd::stream_args_t& args_, - std::vector<uhd::rfnoc::block_id_t>& chan_list, - std::vector<device_addr_t>& chan_args) -{ - uhd::stream_args_t args = args_; - std::vector<uhd::rfnoc::block_id_t> chan_list_(args.channels.size()); - std::vector<device_addr_t> chan_args_(args.channels.size()); - - for (size_t i = 0; i < args.channels.size(); i++) { - // Extract block ID - size_t chan_idx = args.channels[i]; - std::string key = str(boost::format("block_id%d") % chan_idx); - if (args.args.has_key(key)) { - chan_list_[i] = args.args.pop(key); - } else if (args.args.has_key("block_id")) { - chan_list_[i] = args.args["block_id"]; - } else { - throw uhd::runtime_error( - str(boost::format( - "Cannot create streamers: No block_id specified for channel %d.") - % chan_idx)); - } - - // Split off known channel specific args - key = str(boost::format("block_port%d") % chan_idx); - if (args.args.has_key(key)) { - chan_args_[i]["block_port"] = args.args.pop(key); - } - key = str(boost::format("radio_id%d") % chan_idx); - if (args.args.has_key(key)) { - chan_args_[i]["radio_id"] = args.args.pop(key); - } - key = str(boost::format("radio_port%d") % chan_idx); - if (args.args.has_key(key)) { - chan_args_[i]["radio_port"] = args.args.pop(key); - } - } - - // Add all remaining args to all channel args - for (device_addr_t& chan_arg : chan_args_) { - chan_arg = chan_arg.to_string() + "," + args.args.to_string(); - } - - chan_list = chan_list_; - chan_args = chan_args_; -} - - -/*********************************************************************** - * RX Flow Control Functions - **********************************************************************/ -/*! Determine the size of the flow control window in number of packets. - * - * This value depends on three things: - * - The packet size (in bytes), P - * - The size of the software buffer (in bytes), B - * - The desired buffer fullness, F - * - * The FC window size is thus X = floor(B*F/P). - * - * \param pkt_size The maximum packet size in bytes - * \param sw_buff_size Software buffer size in bytes - * \param rx_args If this has a key 'recv_buff_fullness', this value will - * be used for said fullness. Must be between 0.01 and 1. - * - * \returns The size of the flow control window in number of packets - */ -static size_t get_rx_flow_control_window( - size_t pkt_size, size_t sw_buff_size, const device_addr_t& rx_args) -{ - double fullness_factor = rx_args.cast<double>( - "recv_buff_fullness", uhd::rfnoc::DEFAULT_FC_RX_SW_BUFF_FULL_FACTOR); - - if (fullness_factor < 0.01 || fullness_factor > 1) { - throw uhd::value_error( - "recv_buff_fullness must be in [0.01, 1] inclusive (1% to 100%)"); - } - - size_t window_in_bytes = (static_cast<size_t>(fullness_factor * sw_buff_size)); - if (rx_args.has_key("max_recv_window")) { - window_in_bytes = std::min( - window_in_bytes, - pkt_size * rx_args.cast<size_t>("max_recv_window", 1) - ); - } - if (window_in_bytes < pkt_size) { - throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size."); - } - UHD_ASSERT_THROW(size_t(sw_buff_size * fullness_factor) >= window_in_bytes); - return window_in_bytes; -} - - -/*********************************************************************** - * TX Async Message Functions - **********************************************************************/ -#define DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL 0 - -struct async_tx_info_t -{ - size_t stream_channel; - size_t device_channel; - boost::shared_ptr<device3_impl::async_md_type> async_queue; - boost::shared_ptr<device3_impl::async_md_type> old_async_queue; -}; - -/*! Handle incoming messages. - * Send them to the async message queue for the user to poll. - * - * This is run inside a uhd::task as long as this streamer lives. - */ -static void handle_tx_async_msgs(boost::shared_ptr<async_tx_info_t> async_info, - zero_copy_if::sptr xport, - uint32_t (*to_host)(uint32_t), - void (*unpack)(const uint32_t* packet_buff, vrt::if_packet_info_t&), - boost::function<double(void)> get_tick_rate) -{ - managed_recv_buffer::sptr buff = xport->get_recv_buff(); - if (not buff) { - return; - } - - // extract packet info - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = buff->size() / sizeof(uint32_t); - const uint32_t* packet_buff = buff->cast<const uint32_t*>(); - - // unpacking can fail - try { - unpack(packet_buff, if_packet_info); - } catch (const std::exception& ex) { - UHD_LOGGER_ERROR("STREAMER") - << "Error parsing async message packet: " << ex.what(); - return; - } - - double tick_rate = get_tick_rate(); - if (tick_rate == rfnoc::tick_node_ctrl::RATE_UNDEFINED) { - tick_rate = 1; - } - - // fill in the async metadata - async_metadata_t metadata; - load_metadata_from_buff(to_host, - metadata, - if_packet_info, - packet_buff, - tick_rate, - async_info->stream_channel); - - // Filter out any flow control messages and cache the rest - if (metadata.event_code == DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) { - UHD_LOGGER_ERROR("TX ASYNC MSG") - << "Unexpected flow control message found in async message handling" - << std::endl; - } else { - async_info->async_queue->push_with_pop_on_full(metadata); - metadata.channel = async_info->device_channel; - async_info->old_async_queue->push_with_pop_on_full(metadata); - standard_async_msg_prints(metadata); - } -} - -bool device3_impl::recv_async_msg(async_metadata_t& async_metadata, double timeout) -{ - return _async_md->pop_with_timed_wait(async_metadata, timeout); -} - -/*********************************************************************** - * Receive streamer - **********************************************************************/ -void device3_impl::update_rx_streamers() -{ - for (const std::string& block_id : _rx_streamers.keys()) { - UHD_RX_STREAMER_LOG() << "updating RX streamer to " << block_id; - boost::shared_ptr<device3_recv_packet_streamer> my_streamer = - boost::dynamic_pointer_cast<device3_recv_packet_streamer>( - _rx_streamers[block_id].lock()); - if (my_streamer) { - double tick_rate = my_streamer->get_terminator()->get_tick_rate(); - if (tick_rate == rfnoc::tick_node_ctrl::RATE_UNDEFINED) { - tick_rate = 1.0; - } - double samp_rate = my_streamer->get_terminator()->get_output_samp_rate(); - if (samp_rate == rfnoc::rate_node_ctrl::RATE_UNDEFINED) { - samp_rate = 1.0; - } - double scaling = my_streamer->get_terminator()->get_output_scale_factor(); - if (scaling == rfnoc::scalar_node_ctrl::SCALE_UNDEFINED) { - scaling = 1 / 32767.; - } - UHD_RX_STREAMER_LOG() - << " New tick_rate == " << tick_rate - << " New samp_rate == " << samp_rate << " New scaling == " << scaling; - - my_streamer->set_tick_rate(tick_rate); - my_streamer->set_samp_rate(samp_rate); - my_streamer->set_scale_factor(scaling); - } - } -} - -rx_streamer::sptr device3_impl::get_rx_stream(const stream_args_t& args_) -{ - boost::mutex::scoped_lock lock(_transport_setup_mutex); - stream_args_t args = sanitize_stream_args(args_); - - // I. Generate the channel list - std::vector<uhd::rfnoc::block_id_t> chan_list; - std::vector<device_addr_t> chan_args; - generate_channel_list(args, chan_list, chan_args); - // Note: All 'args.args' are merged into chan_args now. - - // II. Iterate over all channels - boost::shared_ptr<device3_recv_packet_streamer> my_streamer; - // The terminator's lifetime is coupled to the streamer. - // There is only one terminator. If the streamer has multiple channels, - // it will be connected to each upstream block. - rfnoc::rx_stream_terminator::sptr recv_terminator = - rfnoc::rx_stream_terminator::make(); - for (size_t stream_i = 0; stream_i < chan_list.size(); stream_i++) { - // First, configure blocks and create transport - - // Get block ID and mb index - uhd::rfnoc::block_id_t block_id = chan_list[stream_i]; - UHD_RX_STREAMER_LOG() << "chan " << stream_i << " connecting to " << block_id; - // Update args so args.args is always valid for this particular channel: - args.args = chan_args[stream_i]; - size_t mb_index = block_id.get_device_no(); - size_t suggested_block_port = - args.args.cast<size_t>("block_port", rfnoc::ANY_PORT); - - // Access to this channel's block control - uhd::rfnoc::source_block_ctrl_base::sptr blk_ctrl = - boost::dynamic_pointer_cast<uhd::rfnoc::source_block_ctrl_base>( - get_block_ctrl(block_id)); - - // Connect the terminator with this channel's block. - size_t block_port = blk_ctrl->connect_downstream( - recv_terminator, suggested_block_port, args.args); - const size_t terminator_port = recv_terminator->connect_upstream(blk_ctrl); - blk_ctrl->set_downstream_port(block_port, terminator_port); - recv_terminator->set_upstream_port(terminator_port, block_port); - - // Check if the block connection is compatible (spp and item type) - check_stream_sig_compatible( - blk_ctrl->get_output_signature(block_port), args, "RX"); - - // Setup the DSP transport hints - device_addr_t rx_hints = get_rx_hints(mb_index); - - // Search the device and all nodes for lowest MTU - size_t mtu = std::min( - get_mtu(mb_index, uhd::direction_t::RX_DIRECTION), - blk_ctrl->get_mtu(block_port)); - UHD_RX_STREAMER_LOG() << "Maximum MTU supported by " - << blk_ctrl->unique_id() - << ": " << blk_ctrl->get_mtu(block_port); - std::vector<boost::shared_ptr<uhd::rfnoc::source_block_ctrl_base>> - upstream_source_nodes = - blk_ctrl->find_upstream_node<uhd::rfnoc::source_block_ctrl_base>(); - for (const boost::shared_ptr<uhd::rfnoc::source_block_ctrl_base>& node : - upstream_source_nodes) { - // Get MTU from Port 0 of the upstream nodes. This is okay for now as - // currently we use port 0 of a block in case of channel 1. - UHD_RX_STREAMER_LOG() << "Maximum MTU supported by " << node->unique_id() - << ": " << node->get_mtu(0); - mtu = std::min(mtu, node->get_mtu(0)); - } - rx_hints["mtu"] = std::to_string(mtu); - - // Make sure user supplied recv_frame_size is less than the MTU - if (rx_hints.cast<size_t>("recv_frame_size", mtu) > mtu) { - UHD_LOGGER_WARNING("STREAMER") - << "Requested recv_frame_size of " - << rx_hints["recv_frame_size"] - << " exceeds the maximum possible on this stream. Using " - << mtu; - rx_hints["recv_frame_size"] = std::to_string(mtu); - } - - // allocate sid and create transport - uhd::sid_t stream_address = blk_ctrl->get_address(block_port); - UHD_RX_STREAMER_LOG() << "creating rx stream " << rx_hints.to_string(); - both_xports_t xport = make_transport(stream_address, RX_DATA, rx_hints); - UHD_RX_STREAMER_LOG() << std::hex << "data_sid = " << xport.send_sid << std::dec - << " actual recv_buff_size = " << xport.recv_buff_size; - - // Configure the block - // Flow control setup - const size_t pkt_size = xport.recv->get_recv_frame_size(); - // Leave one pkt_size space for overrun packets - TODO make this obsolete - const size_t fc_window = - get_rx_flow_control_window(pkt_size, xport.recv_buff_size, rx_hints) - - pkt_size; - const size_t fc_handle_window = - std::max<size_t>(pkt_size, fc_window / stream_options.rx_fc_request_freq); - UHD_RX_STREAMER_LOG() << "Flow Control Window = " << (fc_window) - << ", Flow Control Handler Window = " << fc_handle_window; - blk_ctrl->configure_flow_control_out(true, - xport.lossless, - fc_window, - rx_hints.cast<size_t>("recv_pkt_limit", - 0), // On rfnoc-devel, update e300_impl::get_rx_hints() to set this to 32 - block_port); - - // Add flow control transport - boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t()); - fc_cache->sid = xport.send_sid; - fc_cache->xport = xport.send; - fc_cache->interval = fc_handle_window; - if (xport.endianness == ENDIANNESS_BIG) { - fc_cache->to_host = uhd::ntohx<uint32_t>; - fc_cache->from_host = uhd::htonx<uint32_t>; - fc_cache->pack = vrt::chdr::if_hdr_pack_be; - fc_cache->unpack = vrt::chdr::if_hdr_unpack_be; - } else { - fc_cache->to_host = uhd::wtohx<uint32_t>; - fc_cache->from_host = uhd::htowx<uint32_t>; - fc_cache->pack = vrt::chdr::if_hdr_pack_le; - fc_cache->unpack = vrt::chdr::if_hdr_unpack_le; - } - xport.recv = zero_copy_flow_ctrl::make( - xport.recv, 0, [fc_cache](managed_buffer::sptr buff) { - return rx_flow_ctrl(fc_cache, buff); - }); - - // Configure the block - // Note: We need to set_destination() after writing to SR_CLEAR_TX_FC. - // See noc_shell.v, in the section called Stream Source for details. - // Setting SR_CLEAR_TX_FC will actually also clear the destination and - // other settings. - blk_ctrl->sr_write(uhd::rfnoc::SR_CLEAR_TX_FC, 0x1, block_port); - blk_ctrl->sr_write(uhd::rfnoc::SR_CLEAR_TX_FC, 0x0, block_port); - // Configure routing for data - blk_ctrl->set_destination(xport.send_sid.get_src(), block_port); - - // Configure routing for responses - blk_ctrl->sr_write( - uhd::rfnoc::SR_RESP_OUT_DST_SID, xport.send_sid.get_src(), block_port); - UHD_RX_STREAMER_LOG() << "resp_out_dst_sid == " << xport.send_sid.get_src(); - - // Find all upstream radio nodes and set their response in SID to the host - std::vector<boost::shared_ptr<uhd::rfnoc::radio_ctrl>> upstream_radio_nodes = - blk_ctrl->find_upstream_node<uhd::rfnoc::radio_ctrl>(); - UHD_RX_STREAMER_LOG() << "Number of upstream radio nodes: " - << upstream_radio_nodes.size(); - for (const boost::shared_ptr<uhd::rfnoc::radio_ctrl>& node : - upstream_radio_nodes) { - node->sr_write( - uhd::rfnoc::SR_RESP_OUT_DST_SID, xport.send_sid.get_src(), block_port); - } - - // Second, configure the streamer - - // make the new streamer given the samples per packet - if (not my_streamer) { - // To calculate the max number of samples per packet, we assume the maximum - // header length to avoid fragmentation should the entire header be used. - const size_t bpp = - pkt_size - stream_options.rx_max_len_hdr; // bytes per packet - const size_t bpi = - convert::get_bytes_per_item(args.otw_format); // bytes per item - const size_t spp = std::min(args.args.cast<size_t>("spp", bpp / bpi), - bpp / bpi); // samples per packet - UHD_RX_STREAMER_LOG() << "bpp == " << bpp << ", bpi == " << bpi << ", spp == " << spp; - - my_streamer = boost::make_shared<device3_recv_packet_streamer>( - spp, recv_terminator, xport); - my_streamer->resize(chan_list.size()); - } - - // init some streamer stuff - std::string conv_endianness; - if (xport.endianness == ENDIANNESS_BIG) { - my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); - conv_endianness = "be"; - } else { - my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le); - conv_endianness = "le"; - } - - // set the converter - uhd::convert::id_type id; - id.input_format = args.otw_format + "_item32_" + conv_endianness; - id.num_inputs = 1; - id.output_format = args.cpu_format; - id.num_outputs = 1; - my_streamer->set_converter(id); - - // Give the streamer a functor to handle flow control ACK messages - my_streamer->set_xport_handle_flowctrl_ack( - stream_i, [fc_cache](const uint32_t* payload) { - handle_rx_flowctrl_ack(fc_cache, payload); - }); - - // Give the streamer a functor to get the recv_buffer - my_streamer->set_xport_chan_get_buff(stream_i, - [xport](double timeout) { return xport.recv->get_recv_buff(timeout); }, - true /*flush*/ - ); - - // Give the streamer a functor to handle overruns - // bind requires a weak_ptr to break the a streamer->streamer circular dependency - // Using "this" is OK because we know that this device3_impl will outlive the - // streamer - boost::weak_ptr<uhd::rx_streamer> weak_ptr(my_streamer); - my_streamer->set_overflow_handler( - stream_i, [recv_terminator, weak_ptr, stream_i]() { - recv_terminator->handle_overrun(weak_ptr, stream_i); - }); - - // Give the streamer a functor issue stream cmd - my_streamer->set_issue_stream_cmd( - stream_i, [blk_ctrl, block_port](const stream_cmd_t& stream_cmd) { - blk_ctrl->issue_stream_cmd(stream_cmd, block_port); - }); - } - - // Notify all blocks in this chain that they are connected to an active streamer - recv_terminator->set_rx_streamer(true, 0); - - // Store a weak pointer to prevent a streamer->device3_impl->streamer circular - // dependency. Note that we store the streamer only once, and use its terminator's ID - // to do so. - _rx_streamers[recv_terminator->unique_id()] = - boost::weak_ptr<uhd::rx_streamer>(my_streamer); - - // Sets tick rate, samp rate and scaling on this streamer. - // A registered terminator is required to do this. - update_rx_streamers(); - - post_streamer_hooks(RX_DIRECTION); - return my_streamer; -} - -/*********************************************************************** - * Transmit streamer - **********************************************************************/ -void device3_impl::update_tx_streamers() -{ - for (const std::string& block_id : _tx_streamers.keys()) { - UHD_TX_STREAMER_LOG() << "updating TX streamer: " << block_id; - boost::shared_ptr<device3_send_packet_streamer> my_streamer = - boost::dynamic_pointer_cast<device3_send_packet_streamer>( - _tx_streamers[block_id].lock()); - if (my_streamer) { - double tick_rate = my_streamer->get_terminator()->get_tick_rate(); - if (tick_rate == rfnoc::tick_node_ctrl::RATE_UNDEFINED) { - tick_rate = 1.0; - } - double samp_rate = my_streamer->get_terminator()->get_input_samp_rate(); - if (samp_rate == rfnoc::rate_node_ctrl::RATE_UNDEFINED) { - samp_rate = 1.0; - } - double scaling = my_streamer->get_terminator()->get_input_scale_factor(); - if (scaling == rfnoc::scalar_node_ctrl::SCALE_UNDEFINED) { - scaling = 32767.; - } - UHD_TX_STREAMER_LOG() - << "New tick_rate == " << tick_rate << " New samp_rate == " << samp_rate - << " New scaling == " << scaling; - my_streamer->set_tick_rate(tick_rate); - my_streamer->set_samp_rate(samp_rate); - my_streamer->set_scale_factor(scaling); - } - } -} - -tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t& args_) -{ - boost::mutex::scoped_lock lock(_transport_setup_mutex); - stream_args_t args = sanitize_stream_args(args_); - - // I. Generate the channel list - std::vector<uhd::rfnoc::block_id_t> chan_list; - std::vector<device_addr_t> chan_args; - generate_channel_list(args, chan_list, chan_args); - // Note: All 'args.args' are merged into chan_args now. - - // shared async queue for all channels in streamer - boost::shared_ptr<async_md_type> async_md(new async_md_type(1000 /*messages deep*/)); - - // II. Iterate over all channels - boost::shared_ptr<device3_send_packet_streamer> my_streamer; - // The terminator's lifetime is coupled to the streamer. - // There is only one terminator. If the streamer has multiple channels, - // it will be connected to each downstream block. - rfnoc::tx_stream_terminator::sptr send_terminator = - rfnoc::tx_stream_terminator::make(); - for (size_t stream_i = 0; stream_i < chan_list.size(); stream_i++) { - // First, configure the downstream blocks and create the transports - - // Get block ID and mb index - uhd::rfnoc::block_id_t block_id = chan_list[stream_i]; - // Update args so args.args is always valid for this particular channel: - args.args = chan_args[stream_i]; - size_t mb_index = block_id.get_device_no(); - size_t suggested_block_port = - args.args.cast<size_t>("block_port", rfnoc::ANY_PORT); - - // Access to this channel's block control - uhd::rfnoc::sink_block_ctrl_base::sptr blk_ctrl = - boost::dynamic_pointer_cast<uhd::rfnoc::sink_block_ctrl_base>( - get_block_ctrl(block_id)); - - // Connect the terminator with this channel's block. - // This will throw if the connection is not possible. - size_t block_port = - blk_ctrl->connect_upstream(send_terminator, suggested_block_port, args.args); - const size_t terminator_port = send_terminator->connect_downstream(blk_ctrl); - blk_ctrl->set_upstream_port(block_port, terminator_port); - send_terminator->set_downstream_port(terminator_port, block_port); - - // Check if the block connection is compatible (spp and item type) - check_stream_sig_compatible( - blk_ctrl->get_input_signature(block_port), args, "TX"); - - // Setup the dsp transport hints - device_addr_t tx_hints = get_tx_hints(mb_index); - - // Search the device and all nodes for lowest MTU - size_t mtu = std::min( - get_mtu(mb_index, uhd::direction_t::TX_DIRECTION), - blk_ctrl->get_mtu(block_port)); - UHD_TX_STREAMER_LOG() << "Maximum MTU supported by " - << blk_ctrl->unique_id() << ": " - << blk_ctrl->get_mtu(block_port); - std::vector<boost::shared_ptr<uhd::rfnoc::sink_block_ctrl_base>> - downstream_sink_nodes = - blk_ctrl->find_downstream_node<uhd::rfnoc::sink_block_ctrl_base>(); - for (const boost::shared_ptr<uhd::rfnoc::sink_block_ctrl_base>& node : - downstream_sink_nodes) { - // Get MTU from Port 0 of the downstream nodes. This is okay for now as - // currently we use port 0 of a block in case of channel 1. - UHD_TX_STREAMER_LOG() << "Maximum MTU supported by " - << node->unique_id() << ": " - << node->get_mtu(0); - mtu = std::min(mtu, node->get_mtu(0)); - } - tx_hints["mtu"] = std::to_string(mtu); - - // Make sure user supplied send_frame_size is less than the MTU - if (tx_hints.cast<size_t>("send_frame_size", mtu) > mtu) { - UHD_LOGGER_WARNING("STREAMER") - << "Requested send_frame_size of " - << tx_hints["send_frame_size"] - << " exceeds the maximum possible on this stream. Using " - << mtu; - tx_hints["send_frame_size"] = std::to_string(mtu); - } - - const size_t fifo_size = blk_ctrl->get_fifo_size(block_port); - // Allocate sid and create transport - uhd::sid_t stream_address = blk_ctrl->get_address(block_port); - UHD_TX_STREAMER_LOG() << "creating tx stream " << tx_hints.to_string(); - both_xports_t xport = make_transport(stream_address, TX_DATA, tx_hints); - both_xports_t async_xport = - make_transport(stream_address, ASYNC_MSG, device_addr_t("")); - UHD_TX_STREAMER_LOG() << std::hex << "data_sid = " << xport.send_sid << std::dec; - - // Configure flow control - // This disables the FC module's output, do this before configuring flow control - blk_ctrl->sr_write(uhd::rfnoc::SR_CLEAR_RX_FC, 0x1, block_port); - blk_ctrl->sr_write(uhd::rfnoc::SR_CLEAR_RX_FC, 0x0, block_port); - // Configure flow control on downstream block - const size_t pkt_size = xport.send->get_send_frame_size(); - const size_t fc_window = - std::min(tx_hints.cast<size_t>("send_buff_size", fifo_size), fifo_size); - const size_t fc_handle_window = - std::max<size_t>(pkt_size, fc_window / stream_options.tx_fc_response_freq); - UHD_TX_STREAMER_LOG() << "Flow Control Window = " << fc_window - << ", Flow Control Handler Window = " << fc_handle_window - << ", FIFO size = " << fifo_size; - blk_ctrl->configure_flow_control_in(fc_handle_window, /*bytes*/ - block_port); - // Add flow control transport - boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t(fc_window)); - if (xport.endianness == ENDIANNESS_BIG) { - fc_cache->to_host = uhd::ntohx<uint32_t>; - fc_cache->from_host = uhd::htonx<uint32_t>; - fc_cache->pack = vrt::chdr::if_hdr_pack_be; - fc_cache->unpack = vrt::chdr::if_hdr_unpack_be; - } else { - fc_cache->to_host = uhd::wtohx<uint32_t>; - fc_cache->from_host = uhd::htowx<uint32_t>; - fc_cache->pack = vrt::chdr::if_hdr_pack_le; - fc_cache->unpack = vrt::chdr::if_hdr_unpack_le; - } - xport.send = zero_copy_flow_ctrl::make(xport.send, - [fc_cache, xport](managed_buffer::sptr buff) { - return tx_flow_ctrl(fc_cache, xport.recv, buff); - }, - 0); - - // Configure return path for async messages - blk_ctrl->sr_write( - uhd::rfnoc::SR_RESP_IN_DST_SID, async_xport.recv_sid.get_dst(), block_port); - UHD_TX_STREAMER_LOG() << "resp_in_dst_sid == " - << boost::format("0x%04X") % xport.recv_sid.get_dst(); - - // FIXME: Once there is a better way to map the radio block and port - // to the channel or another way to receive asynchronous messages that - // is not in-band, this should be removed. - if (args.args.has_key("radio_id") and args.args.has_key("radio_port")) { - // Find downstream radio node and set the response SID to the host - uhd::rfnoc::block_id_t radio_id(args.args["radio_id"]); - size_t radio_port = args.args.cast<size_t>("radio_port", 0); - std::vector<boost::shared_ptr<uhd::rfnoc::radio_ctrl>> - downstream_radio_nodes = - blk_ctrl->find_downstream_node<uhd::rfnoc::radio_ctrl>(); - UHD_TX_STREAMER_LOG() - << "Number of downstream radio nodes: " << downstream_radio_nodes.size(); - for (const boost::shared_ptr<uhd::rfnoc::radio_ctrl>& node : - downstream_radio_nodes) { - if (node->get_block_id() == radio_id) { - node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, - async_xport.recv_sid.get_dst(), - radio_port); - } - } - } else { - // FIXME: This block is preserved for legacy behavior where the - // radio_id and radio_port are not provided. It fails if more - // than one radio is visible downstream or the port on the radio - // is not the same as the block_port. It should be removed as - // soon as possible. - // Find all downstream radio nodes and set their response SID to the host - std::vector<boost::shared_ptr<uhd::rfnoc::radio_ctrl>> - downstream_radio_nodes = - blk_ctrl->find_downstream_node<uhd::rfnoc::radio_ctrl>(); - UHD_TX_STREAMER_LOG() - << "Number of downstream radio nodes: " << downstream_radio_nodes.size(); - for (const boost::shared_ptr<uhd::rfnoc::radio_ctrl>& node : - downstream_radio_nodes) { - node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, - async_xport.recv_sid.get_dst(), - block_port); - } - } - - // Second, configure the streamer now that the blocks and transports are - // configured - - // make the new streamer given the samples per packet - if (not my_streamer) { - // To calculate the max number of samples per packet, we assume the maximum - // header length to avoid fragmentation should the entire header be used. - const size_t bpp = - tx_hints.cast<size_t>("bpp", pkt_size) - - stream_options.tx_max_len_hdr; - const size_t bpi = - convert::get_bytes_per_item(args.otw_format); // bytes per item - const size_t spp = std::min(args.args.cast<size_t>("spp", bpp / bpi), - bpp / bpi); // samples per packet - UHD_TX_STREAMER_LOG() - << "bpp == " << bpp - << ", bpi == " << bpi - << ", spp == " << spp; - - my_streamer = boost::make_shared<device3_send_packet_streamer>( - spp, send_terminator, xport, async_xport); - my_streamer->resize(chan_list.size()); - } - - // init some streamer stuff - std::string conv_endianness; - if (xport.endianness == ENDIANNESS_BIG) { - my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); - conv_endianness = "be"; - } else { - my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le); - conv_endianness = "le"; - } - - // set the converter - uhd::convert::id_type id; - id.input_format = args.cpu_format; - id.num_inputs = 1; - id.output_format = args.otw_format + "_item32_" + conv_endianness; - id.num_outputs = 1; - my_streamer->set_converter(id); - - boost::shared_ptr<async_tx_info_t> async_tx_info(new async_tx_info_t()); - async_tx_info->stream_channel = args.channels[stream_i]; - async_tx_info->device_channel = mb_index; - async_tx_info->async_queue = async_md; - async_tx_info->old_async_queue = _async_md; - - task::sptr async_task = - task::make([async_tx_info, async_xport, xport, send_terminator]() { - handle_tx_async_msgs(async_tx_info, - async_xport.recv, - xport.endianness == ENDIANNESS_BIG ? uhd::ntohx<uint32_t> - : uhd::wtohx<uint32_t>, - xport.endianness == ENDIANNESS_BIG ? vrt::chdr::if_hdr_unpack_be - : vrt::chdr::if_hdr_unpack_le, - [send_terminator]() { return send_terminator->get_tick_rate(); }); - }); - my_streamer->add_async_msg_task(async_task); - - // Give the streamer a functor to get the send buffer - my_streamer->set_xport_chan_get_buff(stream_i, - [xport](const double timeout) { return xport.send->get_send_buff(timeout); }); - // Give the streamer a functor handled received async messages - my_streamer->set_async_receiver( - [async_md](uhd::async_metadata_t& md, const double timeout) { - return async_md->pop_with_timed_wait(md, timeout); - }); - my_streamer->set_xport_chan_sid(stream_i, true, xport.send_sid); - // CHDR does not support trailers - my_streamer->set_enable_trailer(false); - - // Avoid sending FC ACKs if the transport is lossless or the user - // has explictly requested not to send them - if (not(xport.lossless or tx_hints.has_key("send_no_fc_acks"))) { - my_streamer->set_xport_chan_post_send_cb(stream_i, [fc_cache, xport]() { - tx_flow_ctrl_ack(fc_cache, xport.send, xport.send_sid); - }); - } - } - - // Notify all blocks in this chain that they are connected to an active streamer - send_terminator->set_tx_streamer(true, 0); - - // Store a weak pointer to prevent a streamer->device3_impl->streamer circular - // dependency. Note that we store the streamer only once, and use its terminator's ID - // to do so. - _tx_streamers[send_terminator->unique_id()] = - boost::weak_ptr<uhd::tx_streamer>(my_streamer); - - // Sets tick rate, samp rate and scaling on this streamer - // A registered terminator is required to do this. - update_tx_streamers(); - - post_streamer_hooks(TX_DIRECTION); - return my_streamer; -} diff --git a/host/lib/usrp/mpmd/mpmd_impl.cpp b/host/lib/usrp/mpmd/mpmd_impl.cpp index 30a3c5804..69f990807 100644 --- a/host/lib/usrp/mpmd/mpmd_impl.cpp +++ b/host/lib/usrp/mpmd/mpmd_impl.cpp @@ -7,15 +7,9 @@ #include "mpmd_impl.hpp" #include <uhd/exception.hpp> #include <uhd/types/component_file.hpp> -#include <uhd/types/eeprom.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/tasks.hpp> -#include <uhdlib/rfnoc/radio_ctrl_impl.hpp> -#include <uhdlib/rfnoc/rpc_block_ctrl.hpp> #include <boost/algorithm/string.hpp> -#include <boost/asio.hpp> #include <boost/make_shared.hpp> #include <boost/thread.hpp> #include <chrono> diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp index baf0dde3e..2a3a48b62 100644 --- a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp +++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp @@ -192,77 +192,79 @@ mpmd_link_if_ctrl_dpdk_udp::mpmd_link_if_ctrl_dpdk_udp( } } -uhd::both_xports_t -mpmd_link_if_ctrl_dpdk_udp::make_transport( - mpmd_link_if_mgr::xport_info_t &xport_info, - const usrp::device3_impl::xport_type_t xport_type, - const uhd::device_addr_t& xport_args -) { +// uhd::both_xports_t +// mpmd_link_if_ctrl_dpdk_udp::make_transport( +// mpmd_link_if_mgr::xport_info_t &xport_info, +// const uhd::transport::link_type_t xport_type, +// const uhd::device_addr_t& xport_args +//) { - // Constrain by this transport's MTU and the MTU in the xport_args - const size_t send_mtu = std::min(get_mtu(uhd::TX_DIRECTION), - xport_args.cast<size_t>("mtu", get_mtu(uhd::TX_DIRECTION))); - const size_t recv_mtu = std::min(get_mtu(uhd::RX_DIRECTION), - xport_args.cast<size_t>("mtu", get_mtu(uhd::RX_DIRECTION))); +//// Constrain by this transport's MTU and the MTU in the xport_args +// const size_t send_mtu = std::min(get_mtu(uhd::TX_DIRECTION), +// xport_args.cast<size_t>("mtu", get_mtu(uhd::TX_DIRECTION))); +// const size_t recv_mtu = std::min(get_mtu(uhd::RX_DIRECTION), +// xport_args.cast<size_t>("mtu", get_mtu(uhd::RX_DIRECTION))); - // Create actual UHD-DPDK UDP transport - transport::zero_copy_xport_params default_buff_args; - default_buff_args.num_recv_frames = MPMD_ETH_NUM_CTRL_FRAMES; - default_buff_args.num_send_frames = MPMD_ETH_NUM_CTRL_FRAMES; - default_buff_args.recv_frame_size = MPMD_10GE_MSG_FRAME_DEFAULT_SIZE; - default_buff_args.send_frame_size = MPMD_10GE_MSG_FRAME_DEFAULT_SIZE; +//// Create actual UHD-DPDK UDP transport +// transport::zero_copy_xport_params default_buff_args; +// default_buff_args.num_recv_frames = MPMD_ETH_NUM_CTRL_FRAMES; +// default_buff_args.num_send_frames = MPMD_ETH_NUM_CTRL_FRAMES; +// default_buff_args.recv_frame_size = MPMD_10GE_MSG_FRAME_DEFAULT_SIZE; +// default_buff_args.send_frame_size = MPMD_10GE_MSG_FRAME_DEFAULT_SIZE; - if (xport_type == usrp::device3_impl::RX_DATA) { - default_buff_args.num_recv_frames = - xport_args.cast<size_t>("num_recv_frames", MPMD_ETH_NUM_RECV_FRAMES); - default_buff_args.recv_frame_size = std::min( - xport_args.cast<size_t>("recv_frame_size", - MPMD_10GE_DATA_FRAME_DEFAULT_SIZE), - recv_mtu); - } else if (xport_type == usrp::device3_impl::TX_DATA) { - default_buff_args.num_send_frames = - xport_args.cast<size_t>("num_send_frames", MPMD_ETH_NUM_SEND_FRAMES); - default_buff_args.send_frame_size = std::min( - xport_args.cast<size_t>("send_frame_size", - MPMD_10GE_DATA_FRAME_DEFAULT_SIZE), - send_mtu); - } +// if (xport_type == uhd::transport::link_type_t::RX_DATA) { +// default_buff_args.num_recv_frames = +// xport_args.cast<size_t>("num_recv_frames", MPMD_ETH_NUM_RECV_FRAMES); +// default_buff_args.recv_frame_size = std::min( +// xport_args.cast<size_t>("recv_frame_size", +// MPMD_10GE_DATA_FRAME_DEFAULT_SIZE), +// recv_mtu); +//} else if (xport_type == uhd::transport::link_type_t::TX_DATA) { +// default_buff_args.num_send_frames = +// xport_args.cast<size_t>("num_send_frames", MPMD_ETH_NUM_SEND_FRAMES); +// default_buff_args.send_frame_size = std::min( +// xport_args.cast<size_t>("send_frame_size", +// MPMD_10GE_DATA_FRAME_DEFAULT_SIZE), +// send_mtu); +//} - UHD_LOG_TRACE("BUFF", "num_recv_frames=" << default_buff_args.num_recv_frames - << ", num_send_frames=" << default_buff_args.num_send_frames - << ", recv_frame_size=" << default_buff_args.recv_frame_size - << ", send_frame_size=" << default_buff_args.send_frame_size); +// UHD_LOG_TRACE("BUFF", "num_recv_frames=" << default_buff_args.num_recv_frames +//<< ", num_send_frames=" << default_buff_args.num_send_frames +//<< ", recv_frame_size=" << default_buff_args.recv_frame_size +//<< ", send_frame_size=" << default_buff_args.send_frame_size); - int dpdk_port_id = _ctx.get_route(xport_info["ipv4"]); - if (dpdk_port_id < 0) { - throw uhd::runtime_error("Could not find a DPDK port with route to " + - xport_info["ipv4"]); - } - auto recv = transport::dpdk_zero_copy::make( - _ctx, - (const unsigned int) dpdk_port_id, - xport_info["ipv4"], - xport_info["port"], - "0", - default_buff_args, - uhd::device_addr_t() - ); - const uint16_t port = recv->get_local_port(); - const std::string src_ip_addr = recv->get_local_addr(); - xport_info["src_port"] = std::to_string(port); - xport_info["src_ipv4"] = src_ip_addr; +// int dpdk_port_id = _ctx.get_route(xport_info["ipv4"]); +// if (dpdk_port_id < 0) { +// throw uhd::runtime_error("Could not find a DPDK port with route to " + +// xport_info["ipv4"]); +//} +// auto recv = transport::dpdk_zero_copy::make( +//_ctx, +//(const unsigned int) dpdk_port_id, +// xport_info["ipv4"], +// xport_info["port"], +//"0", +// default_buff_args, +// uhd::device_addr_t() +//); +// const uint16_t port = recv->get_local_port(); +// const std::string src_ip_addr = recv->get_local_addr(); +// xport_info["src_port"] = std::to_string(port); +// xport_info["src_ipv4"] = src_ip_addr; - // Create both_xports_t object and finish: - both_xports_t xports; - xports.endianness = uhd::ENDIANNESS_BIG; - xports.send_sid = sid_t(xport_info["send_sid"]); - xports.recv_sid = xports.send_sid.reversed(); - xports.recv_buff_size = (default_buff_args.recv_frame_size-MPMD_UDP_RESERVED_FRAME_SIZE)*default_buff_args.num_recv_frames; - xports.send_buff_size = (default_buff_args.send_frame_size-MPMD_UDP_RESERVED_FRAME_SIZE)*default_buff_args.num_send_frames; - xports.recv = recv; // Note: This is a type cast! - xports.send = recv; // This too - return xports; -} +//// Create both_xports_t object and finish: +// both_xports_t xports; +// xports.endianness = uhd::ENDIANNESS_BIG; +// xports.send_sid = sid_t(xport_info["send_sid"]); +// xports.recv_sid = xports.send_sid.reversed(); +// xports.recv_buff_size = +// (default_buff_args.recv_frame_size-MPMD_UDP_RESERVED_FRAME_SIZE)*default_buff_args.num_recv_frames; +// xports.send_buff_size = +// (default_buff_args.send_frame_size-MPMD_UDP_RESERVED_FRAME_SIZE)*default_buff_args.num_send_frames; +// xports.recv = recv; // Note: This is a type cast! +// xports.send = recv; // This too +// return xports; +//} bool mpmd_link_if_ctrl_dpdk_udp::is_valid( const mpmd_link_if_mgr::xport_info_t& xport_info diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp index 4423b4340..8f4f1c7d1 100644 --- a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp +++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp @@ -10,7 +10,6 @@ #include "mpmd_link_if_ctrl_base.hpp" #include <uhd/types/device_addr.hpp> #include <uhdlib/transport/dpdk_zero_copy.hpp> -#include "../device3/device3_impl.hpp" namespace uhd { namespace mpmd { namespace xport { @@ -25,12 +24,6 @@ public: const uhd::device_addr_t& mb_args ); - both_xports_t make_transport( - mpmd_link_if_mgr::xport_info_t& xport_info, - const usrp::device3_impl::xport_type_t xport_type, - const uhd::device_addr_t& xport_args - ); - bool is_valid( const mpmd_link_if_mgr::xport_info_t& xport_info ) const; diff --git a/host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp b/host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp index 4b0ba4212..a1d11bad7 100644 --- a/host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp +++ b/host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp @@ -130,10 +130,10 @@ public: * The latter needs to get sent back to MPM to complete the * transport handshake. */ - //virtual both_xports_t make_transport(const xport_info_list_t& xport_info_list, - //const usrp::device3_impl::xport_type_t xport_type, - //const uhd::device_addr_t& xport_args, - //xport_info_t& xport_info_out) = 0; + // virtual both_xports_t make_transport(const xport_info_list_t& xport_info_list, + // const uhd::transport::link_type_t::xport_type_t xport_type, + // const uhd::device_addr_t& xport_args, + // xport_info_t& xport_info_out) = 0; /*! Return the path MTU for whatever this manager lets us do */ diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 227ba4212..6f8b794ce 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -20,7 +20,6 @@ #include <uhd/convert.hpp> #include <uhd/utils/soft_register.hpp> #include <uhdlib/usrp/gpio_defs.hpp> -#include <uhdlib/rfnoc/legacy_compat.hpp> #include <uhdlib/rfnoc/rfnoc_device.hpp> #include <boost/assign/list_of.hpp> #include <boost/format.hpp> @@ -397,8 +396,7 @@ static double derive_freq_from_xx_subdev_and_dsp( **********************************************************************/ class multi_usrp_impl : public multi_usrp{ public: - multi_usrp_impl(device::sptr dev, const device_addr_t& addr) - : _dev(dev) + multi_usrp_impl(device::sptr dev) : _dev(dev) { _tree = _dev->get_tree(); } @@ -2259,7 +2257,6 @@ public: private: device::sptr _dev; property_tree::sptr _tree; - uhd::rfnoc::legacy_compat::sptr _legacy_compat; struct mboard_chan_pair{ size_t mboard, chan; @@ -2517,5 +2514,5 @@ multi_usrp::sptr multi_usrp::make(const device_addr_t& dev_addr) if (rfnoc_dev) { return rfnoc::detail::make_rfnoc_device(rfnoc_dev, dev_addr); } - return boost::make_shared<multi_usrp_impl>(dev, dev_addr); + return boost::make_shared<multi_usrp_impl>(dev); } diff --git a/host/lib/usrp/x300/x300_eth_mgr.cpp b/host/lib/usrp/x300/x300_eth_mgr.cpp index b1d9f40ee..8ff63b050 100644 --- a/host/lib/usrp/x300/x300_eth_mgr.cpp +++ b/host/lib/usrp/x300/x300_eth_mgr.cpp @@ -239,46 +239,46 @@ both_links_t eth_manager::get_links(link_type_t link_type, //#ifdef HAVE_DPDK // auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get(); - // default_buff_args.num_recv_frames = ETH_MSG_NUM_FRAMES; - // default_buff_args.num_send_frames = ETH_MSG_NUM_FRAMES; - // if (link_type == link_type_t::CTRL) { - //// Increasing number of recv frames here because ctrl_iface uses it - //// to determine how many control packets can be in flight before it - //// must wait for an ACK - // default_buff_args.num_recv_frames = - // uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE; - //} else if (xport_type == uhd::usrp::device3_impl::TX_DATA) { - // size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE - //? GE_DATA_FRAME_SEND_SIZE - //: XGE_DATA_FRAME_SEND_SIZE; - // default_buff_args.send_frame_size = args.cast<size_t>( - //"send_frame_size", std::min(default_frame_size, send_mtu)); - // default_buff_args.num_send_frames = - // args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames); - // default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0); - //} else if (xport_type == uhd::usrp::device3_impl::RX_DATA) { - // size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE - //? GE_DATA_FRAME_RECV_SIZE - //: XGE_DATA_FRAME_RECV_SIZE; - // default_buff_args.recv_frame_size = args.cast<size_t>( - //"recv_frame_size", std::min(default_frame_size, recv_mtu)); - // default_buff_args.num_recv_frames = - // args.cast<size_t>("num_recv_frames", default_buff_args.num_recv_frames); - // default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0); - //} - - // int dpdk_port_id = dpdk_ctx.get_route(conn.addr); - // if (dpdk_port_id < 0) { - // throw uhd::runtime_error( - //"Could not find a DPDK port with route to " + conn.addr); - //} - // auto recv = transport::dpdk_zero_copy::make(dpdk_ctx, - //(const unsigned int)dpdk_port_id, - // conn.addr, - // BOOST_STRINGIZE(X300_VITA_UDP_PORT), - //"0", - // default_buff_args, - // uhd::device_addr_t()); +// default_buff_args.num_recv_frames = ETH_MSG_NUM_FRAMES; +// default_buff_args.num_send_frames = ETH_MSG_NUM_FRAMES; +// if (link_type == link_type_t::CTRL) { +//// Increasing number of recv frames here because ctrl_iface uses it +//// to determine how many control packets can be in flight before it +//// must wait for an ACK +// default_buff_args.num_recv_frames = +// uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE; +//} else if (xport_type == uhd::transport::link_type_t::TX_DATA) { +// size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE +//? GE_DATA_FRAME_SEND_SIZE +//: XGE_DATA_FRAME_SEND_SIZE; +// default_buff_args.send_frame_size = args.cast<size_t>( +//"send_frame_size", std::min(default_frame_size, send_mtu)); +// default_buff_args.num_send_frames = +// args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames); +// default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0); +//} else if (xport_type == uhd::transport::link_type_t::RX_DATA) { +// size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE +//? GE_DATA_FRAME_RECV_SIZE +//: XGE_DATA_FRAME_RECV_SIZE; +// default_buff_args.recv_frame_size = args.cast<size_t>( +//"recv_frame_size", std::min(default_frame_size, recv_mtu)); +// default_buff_args.num_recv_frames = +// args.cast<size_t>("num_recv_frames", default_buff_args.num_recv_frames); +// default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0); +//} + +// int dpdk_port_id = dpdk_ctx.get_route(conn.addr); +// if (dpdk_port_id < 0) { +// throw uhd::runtime_error( +//"Could not find a DPDK port with route to " + conn.addr); +//} +// auto recv = transport::dpdk_zero_copy::make(dpdk_ctx, +//(const unsigned int)dpdk_port_id, +// conn.addr, +// BOOST_STRINGIZE(X300_VITA_UDP_PORT), +//"0", +// default_buff_args, +// uhd::device_addr_t()); //#else UHD_LOG_WARNING("X300", "Cannot create DPDK transport, falling back to UDP"); diff --git a/host/lib/usrp/x300/x300_pcie_mgr.cpp b/host/lib/usrp/x300/x300_pcie_mgr.cpp index 220a96530..6560f2770 100644 --- a/host/lib/usrp/x300/x300_pcie_mgr.cpp +++ b/host/lib/usrp/x300/x300_pcie_mgr.cpp @@ -275,13 +275,13 @@ uint32_t pcie_manager::allocate_pcie_dma_chan( const rfnoc::sep_id_t& /*remote_epid*/, const link_type_t /*link_type*/) { throw uhd::not_implemented_error("allocate_pcie_dma_chan()"); - //constexpr uint32_t CTRL_CHANNEL = 0; - //constexpr uint32_t ASYNC_MSG_CHANNEL = 1; - //constexpr uint32_t FIRST_DATA_CHANNEL = 2; - //if (link_type == uhd::usrp::device3_impl::CTRL) { - //return CTRL_CHANNEL; - //} else if (link_type == uhd::usrp::device3_impl::ASYNC_MSG) { - //return ASYNC_MSG_CHANNEL; + // constexpr uint32_t CTRL_CHANNEL = 0; + // constexpr uint32_t ASYNC_MSG_CHANNEL = 1; + // constexpr uint32_t FIRST_DATA_CHANNEL = 2; + // if (link_type == uhd::transport::link_type_t::CTRL) { + // return CTRL_CHANNEL; + //} else if (link_type == uhd::transport::link_type_t::ASYNC_MSG) { + // return ASYNC_MSG_CHANNEL; //} else { //// sid_t has no comparison defined, so we need to convert it uint32_t //uint32_t raw_sid = tx_sid.get(); @@ -330,50 +330,50 @@ both_links_t pcie_manager::get_links(link_type_t /*link_type*/, + std::to_string(local_device_id) + ", no such device associated with this motherboard!"); } - //zero_copy_xport_params default_buff_args; - //xports.endianness = ENDIANNESS_LITTLE; - //xports.lossless = true; - //const uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, xport_type); - //if (xport_type == uhd::usrp::device3_impl::CTRL) { - //// Transport for control stream - //if (not _ctrl_dma_xport) { - //// One underlying DMA channel will handle - //// all control traffic - //_ctrl_dma_xport = - //make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_CTRL_XPORTS); - //} - //// Create a virtual control transport - //xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst()); - //} else if (xport_type == uhd::usrp::device3_impl::ASYNC_MSG) { - //// Transport for async message stream - //if (not _async_msg_dma_xport) { - //// One underlying DMA channel will handle - //// all async message traffic - //_async_msg_dma_xport = - //make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_ASYNC_XPORTS); - //} - //// Create a virtual async message transport - //xports.recv = _async_msg_dma_xport->make_stream(xports.recv_sid.get_dst()); - //} else if (xport_type == uhd::usrp::device3_impl::TX_DATA) { - //default_buff_args.send_frame_size = args.cast<size_t>( - //"send_frame_size", std::min(send_mtu, PCIE_TX_DATA_FRAME_SIZE)); - //default_buff_args.num_send_frames = - //args.cast<size_t>("num_send_frames", PCIE_TX_DATA_NUM_FRAMES); - //default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0); - //default_buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE; - //default_buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES; - //xports.recv = nirio_zero_copy::make( - //_rio_fpga_interface, dma_channel_num, default_buff_args); - //} else if (xport_type == uhd::usrp::device3_impl::RX_DATA) { - //default_buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE; - //default_buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES; - //default_buff_args.recv_frame_size = args.cast<size_t>( - //"recv_frame_size", std::min(recv_mtu, PCIE_RX_DATA_FRAME_SIZE)); - //default_buff_args.num_recv_frames = - //args.cast<size_t>("num_recv_frames", PCIE_RX_DATA_NUM_FRAMES); - //default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0); - //xports.recv = nirio_zero_copy::make( - //_rio_fpga_interface, dma_channel_num, default_buff_args); + // zero_copy_xport_params default_buff_args; + // xports.endianness = ENDIANNESS_LITTLE; + // xports.lossless = true; + // const uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, + // xport_type); if (xport_type == uhd::transport::link_type_t::CTRL) { + //// Transport for control stream + // if (not _ctrl_dma_xport) { + //// One underlying DMA channel will handle + //// all control traffic + //_ctrl_dma_xport = + // make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_CTRL_XPORTS); + //} + //// Create a virtual control transport + // xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst()); + //} else if (xport_type == uhd::transport::link_type_t::ASYNC_MSG) { + //// Transport for async message stream + // if (not _async_msg_dma_xport) { + //// One underlying DMA channel will handle + //// all async message traffic + //_async_msg_dma_xport = + // make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_ASYNC_XPORTS); + //} + //// Create a virtual async message transport + // xports.recv = _async_msg_dma_xport->make_stream(xports.recv_sid.get_dst()); + //} else if (xport_type == uhd::transport::link_type_t::TX_DATA) { + // default_buff_args.send_frame_size = args.cast<size_t>( + //"send_frame_size", std::min(send_mtu, PCIE_TX_DATA_FRAME_SIZE)); + // default_buff_args.num_send_frames = + // args.cast<size_t>("num_send_frames", PCIE_TX_DATA_NUM_FRAMES); + // default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0); + // default_buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE; + // default_buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES; + // xports.recv = nirio_zero_copy::make( + //_rio_fpga_interface, dma_channel_num, default_buff_args); + //} else if (xport_type == uhd::transport::link_type_t::RX_DATA) { + // default_buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE; + // default_buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES; + // default_buff_args.recv_frame_size = args.cast<size_t>( + //"recv_frame_size", std::min(recv_mtu, PCIE_RX_DATA_FRAME_SIZE)); + // default_buff_args.num_recv_frames = + // args.cast<size_t>("num_recv_frames", PCIE_RX_DATA_NUM_FRAMES); + // default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0); + // xports.recv = nirio_zero_copy::make( + //_rio_fpga_interface, dma_channel_num, default_buff_args); //} //xports.send = xports.recv; diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp deleted file mode 100644 index c280f77c5..000000000 --- a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp +++ /dev/null @@ -1,1577 +0,0 @@ -// -// Copyright 2015-2017 Ettus Research, A National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "x300_radio_ctrl_impl.hpp" -#include "x300_dboard_iface.hpp" -#include <uhd/rfnoc/node_ctrl_base.hpp> -#include <uhd/transport/chdr.hpp> -#include <uhd/usrp/dboard_eeprom.hpp> -#include <uhd/usrp/dboard_iface.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/math.hpp> -#include <uhd/utils/safe_call.hpp> -#include <uhdlib/rfnoc/wb_iface_adapter.hpp> -#include <uhdlib/usrp/common/apply_corrections.hpp> -#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/date_time/posix_time/posix_time_io.hpp> -#include <boost/make_shared.hpp> -#include <chrono> -#include <thread> -#include <bitset> - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::rfnoc; -using namespace uhd::usrp::x300; - -static const size_t IO_MASTER_RADIO = 0; - -namespace { - -gain_fcns_t make_gain_fcns_from_subtree(property_tree::sptr subtree) -{ - gain_fcns_t gain_fcns; - gain_fcns.get_range = [subtree]() { - return subtree->access<meta_range_t>("range").get(); - }; - gain_fcns.get_value = [subtree]() { return subtree->access<double>("value").get(); }; - gain_fcns.set_value = [subtree](const double gain) { - subtree->access<double>("value").set(gain); - }; - return gain_fcns; -} - -} // namespace - -/**************************************************************************** - * Structors - ***************************************************************************/ -UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(x300_radio_ctrl) -, _ignore_cal_file(false) -{ - UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::ctor() "; - - //////////////////////////////////////////////////////////////////// - // Set up basic info - //////////////////////////////////////////////////////////////////// - _radio_type = (get_block_id().get_block_count() == 0) ? PRIMARY : SECONDARY; - _radio_slot = (get_block_id().get_block_count() == 0) ? "A" : "B"; - _radio_clk_rate = _tree->access<double>("master_clock_rate").get(); - - //////////////////////////////////////////////////////////////////// - // Set up peripherals - //////////////////////////////////////////////////////////////////// - wb_iface::sptr ctrl = _get_ctrl(IO_MASTER_RADIO); - _regs = boost::make_shared<radio_regmap_t>(_radio_type == PRIMARY ? 0 : 1); - _regs->initialize(*ctrl, true); - - // Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1 - if (_radio_type == PRIMARY) { - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); - _regs->misc_outs_reg.flush(); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); - _regs->misc_outs_reg.flush(); - } - _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1); - - //////////////////////////////////////////////////////////////// - // Setup peripherals - //////////////////////////////////////////////////////////////// - _spi = spi_core_3000::make(ctrl, - regs::sr_addr(radio_ctrl_impl::regs::SPI), - regs::rb_addr(radio_ctrl_impl::regs::RB_SPI)); - _adc = x300_adc_ctrl::make(_spi, DB_ADC_SEN); - _dac = x300_dac_ctrl::make(_spi, DB_DAC_SEN, _radio_clk_rate); - - if (_radio_type == PRIMARY) { - _fp_gpio = gpio_atr::gpio_atr_3000::make( - ctrl, regs::sr_addr(regs::FP_GPIO), regs::rb_addr(regs::RB_FP_GPIO)); - for (const gpio_atr::gpio_attr_map_t::value_type attr : gpio_atr::gpio_attr_map) { - switch (attr.first) { - case usrp::gpio_atr::GPIO_SRC: - _tree - ->create<std::vector<std::string>>( - fs_path("gpio") / "FP0" / attr.second) - .set(std::vector<std::string>( - 32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber([this](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") / "FP0" / attr.second) - .set(std::vector<std::string>( - 32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber( - [this, attr](const std::vector<std::string> str_val) { - uint32_t val = 0; - for (size_t i = 0; i < str_val.size(); i++) { - val += usrp::gpio_atr::gpio_attr_value_pair - .at(attr.second) - .at(str_val[i]) - << i; - } - _fp_gpio->set_gpio_attr(attr.first, val); - }); - break; - case usrp::gpio_atr::GPIO_READBACK: - _tree->create<uint32_t>(fs_path("gpio") / "FP0" / "READBACK") - .set_publisher([this]() { return _fp_gpio->read_gpio(); }); - break; - default: - _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second) - .set(0) - .add_coerced_subscriber([this, attr](const uint32_t val) { - _fp_gpio->set_gpio_attr(attr.first, val); - }); - } - } - } - - //////////////////////////////////////////////////////////////// - // create legacy codec control objects - //////////////////////////////////////////////////////////////// - _tree->create<int>( - "rx_codecs" / _radio_slot / "gains"); // phony property so this dir exists - _tree->create<int>( - "tx_codecs" / _radio_slot / "gains"); // phony property so this dir exists - _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("ads62p48"); - _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("ad9146"); - - _tree->create<meta_range_t>("rx_codecs" / _radio_slot / "gains" / "digital" / "range") - .set(meta_range_t(0, 6.0, 0.5)); - _tree->create<double>("rx_codecs" / _radio_slot / "gains" / "digital" / "value") - .add_coerced_subscriber(boost::bind(&x300_adc_ctrl::set_gain, _adc, _1)) - .set(0); - - //////////////////////////////////////////////////////////////// - // create front-end objects - //////////////////////////////////////////////////////////////// - for (size_t i = 0; i < _get_num_radios(); i++) { - _leds[i] = gpio_atr::gpio_atr_3000::make_write_only( - _get_ctrl(i), regs::sr_addr(regs::LEDS)); - _leds[i]->set_atr_mode( - usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); - - _rx_fe_map[i].core = rx_frontend_core_3000::make( - _get_ctrl(i), regs::sr_addr(x300_regs::RX_FE_BASE)); - _rx_fe_map[i].core->set_adc_rate(_radio_clk_rate); - _rx_fe_map[i].core->set_dc_offset(rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE); - _rx_fe_map[i].core->set_dc_offset_auto( - rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE); - _rx_fe_map[i].core->populate_subtree( - _tree->subtree(_root_path / "rx_fe_corrections" / i)); - - _tx_fe_map[i].core = tx_frontend_core_200::make( - _get_ctrl(i), regs::sr_addr(x300_regs::TX_FE_BASE)); - _tx_fe_map[i].core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); - _tx_fe_map[i].core->set_iq_balance( - tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); - _tx_fe_map[i].core->populate_subtree( - _tree->subtree(_root_path / "tx_fe_corrections" / i)); - - //////////////////////////////////////////////////////////////// - // Bind the daughterboard command time to the motherboard level property - //////////////////////////////////////////////////////////////// - - if (_tree->exists(fs_path("time") / "cmd")) { - _tree->access<time_spec_t>(fs_path("time") / "cmd") - .add_coerced_subscriber( - boost::bind(&x300_radio_ctrl_impl::set_fe_cmd_time, this, _1, i)); - } - } - - //////////////////////////////////////////////////////////////// - // Update default SPP (overwrites the default value from the XML file) - //////////////////////////////////////////////////////////////// - const size_t max_bytes_header = - uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t); - const size_t default_spp = - (_tree->access<size_t>("mtu/recv").get() - max_bytes_header) - / (2 * sizeof(int16_t)); - _tree->access<int>(get_arg_path("spp") / "value").set(default_spp); -} - -x300_radio_ctrl_impl::~x300_radio_ctrl_impl() -{ - UHD_SAFE_CALL( - // Tear down our part of the tree: - _tree->remove(fs_path("rx_codecs" / _radio_slot)); - _tree->remove(fs_path("tx_codecs" / _radio_slot)); - _tree->remove(_root_path / "rx_fe_corrections"); - _tree->remove(_root_path / "tx_fe_corrections"); - if (_radio_type == PRIMARY) { - for (const gpio_atr::gpio_attr_map_t::value_type attr : - gpio_atr::gpio_attr_map) { - _tree->remove(fs_path("gpio") / "FP0" / attr.second); - } - } - - // Reset peripherals - if (_radio_type == PRIMARY) { - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); - } _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); - _regs->misc_outs_reg.flush();) -} - -/**************************************************************************** - * API calls - ***************************************************************************/ -double x300_radio_ctrl_impl::set_rate(double rate) -{ - const double actual_rate = get_rate(); - if (not uhd::math::frequencies_are_equal(rate, actual_rate)) { - UHD_LOGGER_WARNING("X300 RADIO") - << "Requesting invalid sampling rate from device: " << rate / 1e6 - << " MHz. Actual rate is: " << actual_rate / 1e6 << " MHz."; - } - // On X3x0, tick rate can't actually be changed at runtime - return actual_rate; -} - -void x300_radio_ctrl_impl::set_fe_cmd_time(const time_spec_t& time, const size_t chan) -{ - if (_tree->exists(fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "time" / "cmd"))) { - _tree - ->access<time_spec_t>( - fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "time" / "cmd")) - .set(time); - } -} - -void x300_radio_ctrl_impl::set_tx_antenna(const std::string& ant, const size_t chan) -{ - _tree - ->access<std::string>( - fs_path("dboards" / _radio_slot / "tx_frontends" - / _tx_fe_map.at(chan).db_fe_name / "antenna" / "value")) - .set(ant); -} - -std::string x300_radio_ctrl_impl::get_tx_antenna(const size_t chan) -{ - return _tree - ->access<std::string>( - fs_path("dboards" / _radio_slot / "tx_frontends" - / _tx_fe_map.at(chan).db_fe_name / "antenna" / "value")) - .get(); -} - -void x300_radio_ctrl_impl::set_rx_antenna(const std::string& ant, const size_t chan) -{ - _tree - ->access<std::string>( - fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "antenna" / "value")) - .set(ant); -} - -std::string x300_radio_ctrl_impl::get_rx_antenna(const size_t chan) -{ - return _tree - ->access<std::string>( - fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "antenna" / "value")) - .get(); -} - -double x300_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan) -{ - return _tree - ->access<double>(fs_path("dboards" / _radio_slot / "tx_frontends" - / _tx_fe_map.at(chan).db_fe_name / "freq" / "value")) - .set(freq) - .get(); -} - -double x300_radio_ctrl_impl::get_tx_frequency(const size_t chan) -{ - return _tree - ->access<double>(fs_path("dboards" / _radio_slot / "tx_frontends" - / _tx_fe_map.at(chan).db_fe_name / "freq" / "value")) - .get(); -} - -double x300_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) -{ - return _tree - ->access<double>(fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "freq" / "value")) - .set(freq) - .get(); -} - -double x300_radio_ctrl_impl::get_rx_frequency(const size_t chan) -{ - return _tree - ->access<double>(fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "freq" / "value")) - .get(); -} - -double x300_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) -{ - return _tree - ->access<double>( - fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "bandwidth" / "value")) - .set(bandwidth) - .get(); -} - -double x300_radio_ctrl_impl::get_rx_bandwidth(const size_t chan) -{ - return _tree - ->access<double>( - fs_path("dboards" / _radio_slot / "rx_frontends" - / _rx_fe_map.at(chan).db_fe_name / "bandwidth" / "value")) - .get(); -} - -double x300_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) -{ - if (_tx_gain_groups.count(chan)) { - auto& gg = _tx_gain_groups.at(chan); - gg->set_value(gain); - return radio_ctrl_impl::set_tx_gain(gg->get_value(), chan); - } - return radio_ctrl_impl::set_tx_gain(0.0, chan); -} - -double x300_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) -{ - auto& gg = _rx_gain_groups.at(chan); - gg->set_value(gain); - return radio_ctrl_impl::set_rx_gain(gg->get_value(), chan); -} - -double x300_radio_ctrl_impl::get_rx_gain(const size_t chan) -{ - return _rx_gain_groups.at(chan)->get_value(); -} - -std::vector<std::string> x300_radio_ctrl_impl::get_rx_lo_names(const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - std::vector<std::string> lo_names; - if (_tree->exists(rx_fe_fe_root / "los")) { - for (const std::string& name : _tree->list(rx_fe_fe_root / "los")) { - lo_names.push_back(name); - } - } - return lo_names; -} - -std::vector<std::string> x300_radio_ctrl_impl::get_rx_lo_sources( - const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - if (_tree->exists(rx_fe_fe_root / "los" / ALL_LOS)) { - // Special value ALL_LOS support atomically sets the source for all LOs - return _tree - ->access<std::vector<std::string>>( - rx_fe_fe_root / "los" / ALL_LOS / "source" / "options") - .get(); - } else { - return std::vector<std::string>(); - } - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - return _tree - ->access<std::vector<std::string>>( - rx_fe_fe_root / "los" / name / "source" / "options") - .get(); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - // If the daughterboard doesn't expose it's LO(s) then it can only be internal - return std::vector<std::string>(1, "internal"); - } -} - -void x300_radio_ctrl_impl::set_rx_lo_source( - const std::string& src, const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - if (_tree->exists(rx_fe_fe_root / "los" / ALL_LOS)) { - // Special value ALL_LOS support atomically sets the source for all LOs - _tree - ->access<std::string>( - rx_fe_fe_root / "los" / ALL_LOS / "source" / "value") - .set(src); - } else { - for (const std::string& n : _tree->list(rx_fe_fe_root / "los")) { - this->set_rx_lo_source(src, n, chan); - } - } - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - _tree - ->access<std::string>( - rx_fe_fe_root / "los" / name / "source" / "value") - .set(src); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - throw uhd::runtime_error( - "This device does not support manual configuration of LOs"); - } -} - -const std::string x300_radio_ctrl_impl::get_rx_lo_source( - const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - // Special value ALL_LOS support atomically sets the source for all LOs - return _tree - ->access<std::string>( - rx_fe_fe_root / "los" / ALL_LOS / "source" / "value") - .get(); - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - return _tree - ->access<std::string>( - rx_fe_fe_root / "los" / name / "source" / "value") - .get(); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - // If the daughterboard doesn't expose it's LO(s) then it can only be internal - return "internal"; - } -} - -void x300_radio_ctrl_impl::set_rx_lo_export_enabled( - bool enabled, const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - if (_tree->exists(rx_fe_fe_root / "los" / ALL_LOS)) { - // Special value ALL_LOS support atomically sets the source for all LOs - _tree->access<bool>(rx_fe_fe_root / "los" / ALL_LOS / "export") - .set(enabled); - } else { - for (const std::string& n : _tree->list(rx_fe_fe_root / "los")) { - this->set_rx_lo_export_enabled(enabled, n, chan); - } - } - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - _tree->access<bool>(rx_fe_fe_root / "los" / name / "export").set(enabled); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - throw uhd::runtime_error( - "This device does not support manual configuration of LOs"); - } -} - -bool x300_radio_ctrl_impl::get_rx_lo_export_enabled( - const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - // Special value ALL_LOS support atomically sets the source for all LOs - return _tree->access<bool>(rx_fe_fe_root / "los" / ALL_LOS / "export").get(); - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - return _tree->access<bool>(rx_fe_fe_root / "los" / name / "export").get(); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - // If the daughterboard doesn't expose it's LO(s), assume it cannot export - return false; - } -} - -double x300_radio_ctrl_impl::set_rx_lo_freq( - double freq, const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - throw uhd::runtime_error( - "LO frequency must be set for each stage individually"); - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - _tree->access<double>(rx_fe_fe_root / "los" / name / "freq" / "value") - .set(freq); - return _tree - ->access<double>(rx_fe_fe_root / "los" / name / "freq" / "value") - .get(); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - throw uhd::runtime_error( - "This device does not support manual configuration of LOs"); - } -} - -double x300_radio_ctrl_impl::get_rx_lo_freq(const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - throw uhd::runtime_error( - "LO frequency must be retrieved for each stage individually"); - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - return _tree - ->access<double>(rx_fe_fe_root / "los" / name / "freq" / "value") - .get(); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - // Return actual RF frequency if the daughterboard doesn't expose it's LO(s) - return _tree->access<double>(rx_fe_fe_root / "freq" / " value").get(); - } -} - -freq_range_t x300_radio_ctrl_impl::get_rx_lo_freq_range( - const std::string& name, const size_t chan) -{ - fs_path rx_fe_fe_root = fs_path( - "dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name); - - if (_tree->exists(rx_fe_fe_root / "los")) { - if (name == ALL_LOS) { - throw uhd::runtime_error( - "LO frequency range must be retrieved for each stage individually"); - } else { - if (_tree->exists(rx_fe_fe_root / "los")) { - return _tree - ->access<freq_range_t>( - rx_fe_fe_root / "los" / name / "freq" / "range") - .get(); - } else { - throw uhd::runtime_error("Could not find LO stage " + name); - } - } - } else { - // Return the actual RF range if the daughterboard doesn't expose it's LO(s) - return _tree->access<meta_range_t>(rx_fe_fe_root / "freq" / "range").get(); - } -} - -template <typename map_type> -static size_t _get_chan_from_map(std::map<size_t, map_type> map, const std::string& fe) -{ - for (auto it = map.begin(); it != map.end(); ++it) { - if (it->second.db_fe_name == fe) { - return it->first; - } - } - throw uhd::runtime_error( - str(boost::format("Invalid daughterboard frontend name: %s") % fe)); -} - -size_t x300_radio_ctrl_impl::get_chan_from_dboard_fe( - const std::string& fe, const uhd::direction_t direction) -{ - switch (direction) { - case uhd::TX_DIRECTION: - return _get_chan_from_map(_tx_fe_map, fe); - case uhd::RX_DIRECTION: - return _get_chan_from_map(_rx_fe_map, fe); - default: - UHD_THROW_INVALID_CODE_PATH(); - } -} - -std::string x300_radio_ctrl_impl::get_dboard_fe_from_chan( - const size_t chan, const uhd::direction_t direction) -{ - switch (direction) { - case uhd::TX_DIRECTION: - return _tx_fe_map.at(chan).db_fe_name; - case uhd::RX_DIRECTION: - return _rx_fe_map.at(chan).db_fe_name; - default: - UHD_THROW_INVALID_CODE_PATH(); - } -} - -double x300_radio_ctrl_impl::get_output_samp_rate(size_t chan) -{ - // TODO: chan should never be ANY_PORT, but due to our current graph search - // method, this can actually happen: - if (chan == ANY_PORT) { - chan = 0; - for (size_t i = 0; i < _get_num_radios(); i++) { - if (_is_streamer_active(uhd::RX_DIRECTION, chan)) { - chan = i; - break; - } - } - } - return _rx_fe_map.at(chan).core->get_output_rate(); -} - -std::vector<std::string> x300_radio_ctrl_impl::get_gpio_banks() const -{ - std::vector<std::string> banks{"RX", "TX"}; - // These pairs are the same, but RXA/TXA are from pre-rfnoc era and are kept for - // backward compat: - banks.push_back("RX" + _radio_slot); - banks.push_back("TX" + _radio_slot); - if (_fp_gpio) { - banks.push_back("FP0"); - } - return banks; -} - -void x300_radio_ctrl_impl::set_gpio_attr(const std::string& bank, - const std::string& attr, - const uint32_t value, - const uint32_t mask) -{ - if (bank == "FP0" and _fp_gpio) { - std::vector<std::string> attr_value; - const auto attr_type = usrp::gpio_atr::gpio_attr_rev_map.at(attr); - switch (attr_type) { - case usrp::gpio_atr::GPIO_SRC: - case usrp::gpio_atr::GPIO_CTRL: - case usrp::gpio_atr::GPIO_DDR: { - attr_value = - _tree->access<std::vector<std::string>>(fs_path("gpio") / bank / attr) - .get(); - std::bitset<32> bit_mask = std::bitset<32>(mask); - std::bitset<32> new_value = std::bitset<32>(value); - for (size_t i = 0; i < bit_mask.size(); i++) { - if (bit_mask[i] == 1) { - attr_value[i] = - usrp::gpio_atr::attr_value_map.at(attr_type).at(new_value[i]); - } - } - _tree->access<std::vector<std::string>>(fs_path("gpio") / bank / attr) - .set(attr_value); - return; - } break; - default: { - const uint32_t curr_value = - _tree->access<uint32_t>(fs_path("gpio") / bank / attr).get(); - uint32_t new_value = (curr_value & ~mask) | (value & mask); - _tree->access<uint32_t>(fs_path("gpio") / bank / attr).set(new_value); - } break; - } - } - if (bank.size() > 2 and bank[1] == 'X') { - const std::string name = bank.substr(2); - const dboard_iface::unit_t unit = (bank[0] == 'R') ? dboard_iface::UNIT_RX - : dboard_iface::UNIT_TX; - dboard_iface::sptr iface = - _tree->access<dboard_iface::sptr>(fs_path("dboards") / name / "iface").get(); - if (attr == "CTRL") - iface->set_pin_ctrl(unit, uint16_t(value), uint16_t(mask)); - if (attr == "DDR") - iface->set_gpio_ddr(unit, uint16_t(value), uint16_t(mask)); - if (attr == "OUT") - iface->set_gpio_out(unit, uint16_t(value), uint16_t(mask)); - if (attr == "ATR_0X") - iface->set_atr_reg( - unit, gpio_atr::ATR_REG_IDLE, uint16_t(value), uint16_t(mask)); - if (attr == "ATR_RX") - iface->set_atr_reg( - unit, gpio_atr::ATR_REG_RX_ONLY, uint16_t(value), uint16_t(mask)); - if (attr == "ATR_TX") - iface->set_atr_reg( - unit, gpio_atr::ATR_REG_TX_ONLY, uint16_t(value), uint16_t(mask)); - if (attr == "ATR_XX") - iface->set_atr_reg( - unit, gpio_atr::ATR_REG_FULL_DUPLEX, uint16_t(value), uint16_t(mask)); - } -} - -uint32_t x300_radio_ctrl_impl::get_gpio_attr( - const std::string& bank, const std::string& attr) -{ - if (bank == "FP0" and _fp_gpio) { - const auto attr_type = usrp::gpio_atr::gpio_attr_rev_map.at(attr); - switch(attr_type) { - case usrp::gpio_atr::GPIO_SRC: - case usrp::gpio_atr::GPIO_CTRL: - case usrp::gpio_atr::GPIO_DDR: { - auto str_val = - _tree->access<std::vector<std::string>>( - fs_path("gpio") / bank / attr).get(); - uint32_t val = 0; - for (size_t i = 0; i < str_val.size(); i++) { - val += usrp::gpio_atr::gpio_attr_value_pair.at(attr).at(str_val[i]) << i; - } - return val; - } - break; - default: { - return _tree->access<uint32_t>(fs_path("gpio") / bank / attr).get(); - } - break; - } - } - if (bank.size() > 2 and bank[1] == 'X') { - const std::string name = bank.substr(2); - const dboard_iface::unit_t unit = (bank[0] == 'R') ? dboard_iface::UNIT_RX - : dboard_iface::UNIT_TX; - dboard_iface::sptr iface = - _tree->access<dboard_iface::sptr>(fs_path("dboards") / name / "iface").get(); - if (attr == "CTRL") - return iface->get_pin_ctrl(unit); - if (attr == "DDR") - return iface->get_gpio_ddr(unit); - if (attr == "OUT") - return iface->get_gpio_out(unit); - if (attr == "ATR_0X") - return iface->get_atr_reg(unit, gpio_atr::ATR_REG_IDLE); - if (attr == "ATR_RX") - return iface->get_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY); - if (attr == "ATR_TX") - return iface->get_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY); - if (attr == "ATR_XX") - return iface->get_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX); - if (attr == "READBACK") - return iface->read_gpio(unit); - } - return 0; -} - -/**************************************************************************** - * Radio control and setup - ***************************************************************************/ -void x300_radio_ctrl_impl::setup_radio(uhd::i2c_iface::sptr zpu_i2c, - x300_clock_ctrl::sptr clock, - bool ignore_cal_file, - bool verbose) -{ - _self_cal_adc_capture_delay(verbose); - _ignore_cal_file = ignore_cal_file; - - //////////////////////////////////////////////////////////////////// - // create RF frontend interfacing - //////////////////////////////////////////////////////////////////// - static const size_t BASE_ADDR = 0x50; - static const size_t RX_EEPROM_ADDR = 0x5; - static const size_t TX_EEPROM_ADDR = 0x4; - static const size_t GDB_EEPROM_ADDR = 0x1; - const static std::vector<size_t> EEPROM_ADDRS{ - RX_EEPROM_ADDR, TX_EEPROM_ADDR, GDB_EEPROM_ADDR}; - const static std::vector<std::string> EEPROM_PATHS{ - "rx_eeprom", "tx_eeprom", "gdb_eeprom"}; - - const size_t DB_OFFSET = (_radio_slot == "A") ? 0x0 : 0x2; - const fs_path db_path = ("dboards" / _radio_slot); - for (size_t i = 0; i < EEPROM_ADDRS.size(); i++) { - const size_t addr = EEPROM_ADDRS[i] + DB_OFFSET; - // Load EEPROM - _db_eeproms[addr].load(*zpu_i2c, BASE_ADDR | addr); - // Add to tree - _tree->create<dboard_eeprom_t>(db_path / EEPROM_PATHS[i]) - .set(_db_eeproms[addr]) - .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::_set_db_eeprom, - this, - zpu_i2c, - (BASE_ADDR | addr), - _1)); - } - - // create a new dboard interface - x300_dboard_iface_config_t db_config; - db_config.gpio = gpio_atr::db_gpio_atr_3000::make(_get_ctrl(IO_MASTER_RADIO), - radio_ctrl_impl::regs::sr_addr(radio_ctrl_impl::regs::GPIO), - radio_ctrl_impl::regs::rb_addr(radio_ctrl_impl::regs::RB_DB_GPIO)); - db_config.spi = _spi; - db_config.rx_spi_slaveno = DB_RX_SEN; - db_config.tx_spi_slaveno = DB_TX_SEN; - db_config.i2c = zpu_i2c; - db_config.clock = clock; - db_config.which_rx_clk = (_radio_slot == "A") ? X300_CLOCK_WHICH_DB0_RX - : X300_CLOCK_WHICH_DB1_RX; - db_config.which_tx_clk = (_radio_slot == "A") ? X300_CLOCK_WHICH_DB0_TX - : X300_CLOCK_WHICH_DB1_TX; - db_config.dboard_slot = (_radio_slot == "A") ? 0 : 1; - db_config.cmd_time_ctrl = _get_ctrl(IO_MASTER_RADIO); - - // create a new dboard manager - boost::shared_ptr<x300_dboard_iface> db_iface = - boost::make_shared<x300_dboard_iface>(db_config); - _db_manager = dboard_manager::make(_db_eeproms[RX_EEPROM_ADDR + DB_OFFSET], - _db_eeproms[TX_EEPROM_ADDR + DB_OFFSET], - _db_eeproms[GDB_EEPROM_ADDR + DB_OFFSET], - db_iface, - _tree->subtree(db_path), - true // defer daughterboard intitialization - ); - - size_t rx_chan = 0, tx_chan = 0; - for (const std::string& fe : _db_manager->get_rx_frontends()) { - if (rx_chan >= _get_num_radios()) { - break; - } - _rx_fe_map[rx_chan].db_fe_name = fe; - db_iface->add_rx_fe(fe, _rx_fe_map[rx_chan].core); - const fs_path fe_path(db_path / "rx_frontends" / fe); - const std::string conn = _tree->access<std::string>(fe_path / "connection").get(); - const double if_freq = - (_tree->exists(fe_path / "if_freq/value")) - ? _tree->access<double>(fe_path / "if_freq/value").get() - : 0.0; - _rx_fe_map[rx_chan].core->set_fe_connection(usrp::fe_connection_t(conn, if_freq)); - rx_chan++; - } - for (const std::string& fe : _db_manager->get_tx_frontends()) { - if (tx_chan >= _get_num_radios()) { - break; - } - _tx_fe_map[tx_chan].db_fe_name = fe; - const fs_path fe_path(db_path / "tx_frontends" / fe); - const std::string conn = _tree->access<std::string>(fe_path / "connection").get(); - _tx_fe_map[tx_chan].core->set_mux(conn); - tx_chan++; - } - UHD_ASSERT_THROW(rx_chan or tx_chan); - - // Initialize the daughterboards now that frontend cores and connections exist - _db_manager->initialize_dboards(); - - // now that dboard is created -- register into rx antenna event - if (not _rx_fe_map.empty()) { - for (size_t i = 0; i < _get_num_radios(); i++) { - if (_tree->exists(db_path / "rx_frontends" / _rx_fe_map[i].db_fe_name - / "antenna" / "value")) { - // We need a desired subscriber for antenna/value because the experts - // don't coerce that property. - _tree - ->access<std::string>(db_path / "rx_frontends" - / _rx_fe_map[i].db_fe_name / "antenna" - / "value") - .add_desired_subscriber(boost::bind( - &x300_radio_ctrl_impl::_update_atr_leds, this, _1, i)); - _update_atr_leds(_tree - ->access<std::string>(db_path / "rx_frontends" - / _rx_fe_map[i].db_fe_name - / "antenna" / "value") - .get(), - i); - } else { - _update_atr_leds("", i); // init anyway, even if never called - } - } - } - - // bind frontend corrections to the dboard freq props - const fs_path db_tx_fe_path = db_path / "tx_frontends"; - if (not _tx_fe_map.empty()) { - for (size_t i = 0; i < _get_num_radios(); i++) { - if (_tree->exists( - db_tx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value")) { - _tree - ->access<double>( - db_tx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value") - .add_coerced_subscriber( - boost::bind(&x300_radio_ctrl_impl::set_tx_fe_corrections, - this, - db_path, - _root_path / "tx_fe_corrections" / _tx_fe_map[i].db_fe_name, - _1)); - } - } - } - const fs_path db_rx_fe_path = db_path / "rx_frontends"; - if (not _rx_fe_map.empty()) { - for (size_t i = 0; i < _get_num_radios(); i++) { - if (_tree->exists( - db_rx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value")) { - _tree - ->access<double>( - db_rx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value") - .add_coerced_subscriber( - boost::bind(&x300_radio_ctrl_impl::set_rx_fe_corrections, - this, - db_path, - _root_path / "rx_fe_corrections" / _tx_fe_map[i].db_fe_name, - _1)); - } - } - } - - //////////////////////////////////////////////////////////////// - // Set tick rate - //////////////////////////////////////////////////////////////// - const double tick_rate = _tree->access<double>("tick_rate").get(); - radio_ctrl_impl::set_rate(tick_rate); - - //////////////////////////////////////////////////////////////// - // Set gain groups - // Note: The actual gain control comes from the daughterboard drivers, thus, - // we need to call into the prop tree at the appropriate location in order - // to modify the gains. - //////////////////////////////////////////////////////////////// - // TX - for (size_t chan = 0; chan < _num_tx_channels; chan++) { - fs_path rf_gains_path(db_tx_fe_path / _tx_fe_map.at(chan).db_fe_name / "gains"); - if (!_tree->exists(rf_gains_path)) { - continue; - } - - std::vector<std::string> gain_stages = _tree->list(rf_gains_path); - if (gain_stages.empty()) { - continue; - } - - // DAC does not have a gain path - auto gg = gain_group::make(); - for (const auto& name : gain_stages) { - gg->register_fcns(name, - make_gain_fcns_from_subtree(_tree->subtree(rf_gains_path / name)), - 1 /* high prio */); - } - _tx_gain_groups[chan] = gg; - } - // RX - for (size_t chan = 0; chan < _num_rx_channels; chan++) { - fs_path rf_gains_path(db_rx_fe_path / _rx_fe_map.at(chan).db_fe_name / "gains"); - fs_path adc_gains_path("rx_codecs" / _radio_slot / "gains"); - - auto gg = gain_group::make(); - // ADC also has a gain path - for (const auto& name : _tree->list(adc_gains_path)) { - gg->register_fcns("ADC-" + name, - make_gain_fcns_from_subtree(_tree->subtree(adc_gains_path / name)), - 0 /* low prio */); - } - if (_tree->exists(rf_gains_path)) { - for (const auto& name : _tree->list(rf_gains_path)) { - gg->register_fcns(name, - make_gain_fcns_from_subtree(_tree->subtree(rf_gains_path / name)), - 1 /* high prio */); - } - } - _rx_gain_groups[chan] = gg; - } -} - -void x300_radio_ctrl_impl::set_rx_fe_corrections( - const fs_path& db_path, const fs_path& rx_fe_corr_path, const double lo_freq) -{ - if (not _ignore_cal_file) { - apply_rx_fe_corrections(_tree, db_path, rx_fe_corr_path, lo_freq); - } -} - -void x300_radio_ctrl_impl::set_tx_fe_corrections( - const fs_path& db_path, const fs_path& tx_fe_corr_path, const double lo_freq) -{ - if (not _ignore_cal_file) { - apply_tx_fe_corrections(_tree, db_path, tx_fe_corr_path, lo_freq); - } -} - -void x300_radio_ctrl_impl::reset_codec() -{ - if (_radio_type == PRIMARY) { // ADC/DAC reset lines only exist in Radio0 - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); - _regs->misc_outs_reg.flush(); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); - _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); - _regs->misc_outs_reg.flush(); - } - UHD_ASSERT_THROW(bool(_adc)); - UHD_ASSERT_THROW(bool(_dac)); - _adc->reset(); - _dac->reset(); -} - -void x300_radio_ctrl_impl::self_test_adc(uint32_t ramp_time_ms) -{ - // Bypass all front-end corrections - for (size_t i = 0; i < _get_num_radios(); i++) { - _rx_fe_map[i].core->bypass_all(true); - } - - // Test basic patterns - _adc->set_test_word("ones", "ones"); - _check_adc(0xfffcfffc); - _adc->set_test_word("zeros", "zeros"); - _check_adc(0x00000000); - _adc->set_test_word("ones", "zeros"); - _check_adc(0xfffc0000); - _adc->set_test_word("zeros", "ones"); - _check_adc(0x0000fffc); - for (size_t k = 0; k < 14; k++) { - _adc->set_test_word("zeros", "custom", 1 << k); - _check_adc(1 << (k + 2)); - } - for (size_t k = 0; k < 14; k++) { - _adc->set_test_word("custom", "zeros", 1 << k); - _check_adc(1 << (k + 18)); - } - - // Turn on ramp pattern test - _adc->set_test_word("ramp", "ramp"); - _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - // Sleep added for SPI transactions to finish and ramp to start before checker is - // enabled. - std::this_thread::sleep_for(std::chrono::microseconds(1000)); - _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - - std::this_thread::sleep_for(std::chrono::milliseconds(ramp_time_ms)); - _regs->misc_ins_reg.refresh(); - - std::string i_status, q_status; - if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) - if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR)) - i_status = "Bit Errors!"; - else - i_status = "Good"; - else - i_status = "Not Locked!"; - - if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) - if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR)) - q_status = "Bit Errors!"; - else - q_status = "Good"; - else - q_status = "Not Locked!"; - - // Return to normal mode - _adc->set_test_word("normal", "normal"); - - if ((i_status != "Good") or (q_status != "Good")) { - throw uhd::runtime_error( - (boost::format( - "ADC self-test failed for %s. Ramp checker status: {ADC_A=%s, ADC_B=%s}") - % unique_id() % i_status % q_status) - .str()); - } - - // Restore front-end corrections - for (size_t i = 0; i < _get_num_radios(); i++) { - _rx_fe_map[i].core->bypass_all(false); - } -} - -void x300_radio_ctrl_impl::extended_adc_test( - const std::vector<x300_radio_ctrl_impl::sptr>& radios, double duration_s) -{ - static const size_t SECS_PER_ITER = 5; - UHD_LOGGER_INFO("X300 RADIO") - << boost::format( - "Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...") - % duration_s % SECS_PER_ITER; - - size_t num_iters = static_cast<size_t>(ceil(duration_s / SECS_PER_ITER)); - size_t num_failures = 0; - for (size_t iter = 0; iter < num_iters; iter++) { - // Run self-test - UHD_LOGGER_INFO("X300 RADIO") - << boost::format("Extended ADC Self-Test Iteration %06d... ") % (iter + 1); - try { - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->self_test_adc((SECS_PER_ITER * 1000) / radios.size()); - } - UHD_LOGGER_INFO("X300 RADIO") - << boost::format("Extended ADC Self-Test Iteration %06d passed ") - % (iter + 1); - } catch (std::exception& e) { - num_failures++; - UHD_LOGGER_ERROR("X300 RADIO") << e.what(); - } - } - if (num_failures == 0) { - UHD_LOGGER_INFO("X300 RADIO") << "Extended ADC Self-Test PASSED"; - } else { - throw uhd::runtime_error( - (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") - % num_failures % num_iters) - .str()); - } -} - -void x300_radio_ctrl_impl::synchronize_dacs( - const std::vector<x300_radio_ctrl_impl::sptr>& radios) -{ - if (radios.size() < 2) - return; // Nothing to synchronize - - //**PRECONDITION** - // This function assumes that all the VITA times in "radios" are synchronized - // to a common reference. Currently, this function is called in get_tx_stream - // which also has the same precondition. - - // Get a rough estimate of the cumulative command latency - boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->user_reg_read64( - regs::RB_TIME_NOW); // Discard value. We are just timing the call - } - boost::posix_time::time_duration t_elapsed = - boost::posix_time::microsec_clock::local_time() - t_start; - - // Add 100% of headroom + uncertainty to the command time - uint64_t t_sync_us = - (t_elapsed.total_microseconds() * 2) + 16000 /*Scheduler latency*/; - - std::string err_str; - // Try to sync 3 times before giving up - for (size_t attempt = 0; attempt < 3; attempt++) { - try { - // Reinitialize and resync all DACs - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->_dac->sync(); - } - - // Set tick rate and make sure FRAMEP/N is 0 - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->set_command_tick_rate( - radios[i]->_radio_clk_rate, IO_MASTER_RADIO); - radios[i]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); - } - - // Pick radios[0] as the time reference. - uhd::time_spec_t sync_time = radios[0]->_time64->get_time_now() - + uhd::time_spec_t(((double)t_sync_us) / 1e6); - - // Send the sync command - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->set_command_time(sync_time, IO_MASTER_RADIO); - // Arm FRAMEP/N sync pulse by asserting a rising edge - radios[i]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 1); - } - - // Reset FRAMEP/N to 0 after 2 clock cycles - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->set_command_time( - sync_time + (2.0 / radios[i]->_radio_clk_rate), IO_MASTER_RADIO); - radios[i]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); - radios[i]->set_command_time(uhd::time_spec_t(0.0), IO_MASTER_RADIO); - } - - // Wait and check status - std::this_thread::sleep_for(std::chrono::microseconds(t_sync_us)); - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->_dac->verify_sync(); - } - - return; - } catch (const uhd::runtime_error& e) { - err_str = e.what(); - UHD_LOGGER_TRACE("X300 RADIO") << "Retrying DAC synchronization: " << err_str; - } - } - throw uhd::runtime_error(err_str); -} - -double x300_radio_ctrl_impl::self_cal_adc_xfer_delay( - const std::vector<x300_radio_ctrl_impl::sptr>& radios, - x300_clock_ctrl::sptr clock, - boost::function<void(double)> wait_for_clk_locked, - bool apply_delay) -{ - UHD_LOGGER_INFO("X300 RADIO") << "Running ADC transfer delay self-cal: "; - - // Effective resolution of the self-cal. - static const size_t NUM_DELAY_STEPS = 100; - - double master_clk_period = (1.0e9 / clock->get_master_clock_rate()); // in ns - double delay_start = 0.0; - double delay_range = 2 * master_clk_period; - double delay_incr = delay_range / NUM_DELAY_STEPS; - - double cached_clk_delay = clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); - double fpga_clk_delay = clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); - - // Iterate through several values of delays and measure ADC data integrity - std::vector<std::pair<double, bool>> results; - for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { - // Delay the ADC clock (will set both Ch0 and Ch1 delays) - double delay = - clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr * i + delay_start); - wait_for_clk_locked(0.1); - - uint32_t err_code = 0; - for (size_t r = 0; r < radios.size(); r++) { - // Test each channel (I and Q) individually so as to not accidentally trigger - // on the data from the other channel if there is a swap - - // -- Test I Channel -- - // Put ADC in ramp test mode. Tie the other channel to all ones. - radios[r]->_adc->set_test_word("ramp", "ones"); - // Turn on the pattern checker in the FPGA. It will lock when it sees a zero - // and count deviations from the expected value - radios[r]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - radios[r]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - // 50ms @ 200MHz = 10 million samples - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - if (radios[r]->_regs->misc_ins_reg.read( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) { - err_code += radios[r]->_regs->misc_ins_reg.get( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR); - } else { - err_code += 100; // Increment error code by 100 to indicate no lock - } - - // -- Test Q Channel -- - // Put ADC in ramp test mode. Tie the other channel to all ones. - radios[r]->_adc->set_test_word("ones", "ramp"); - // Turn on the pattern checker in the FPGA. It will lock when it sees a zero - // and count deviations from the expected value - radios[r]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - radios[r]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - // 50ms @ 200MHz = 10 million samples - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - if (radios[r]->_regs->misc_ins_reg.read( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) { - err_code += radios[r]->_regs->misc_ins_reg.get( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR); - } else { - err_code += 100; // Increment error code by 100 to indicate no lock - } - } - // UHD_LOGGER_INFO("X300 RADIO") << (boost::format("XferDelay=%fns, Error=%d") % - // delay % err_code); - results.push_back(std::pair<double, bool>(delay, err_code == 0)); - } - - // Calculate the valid window - int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; - for (size_t i = 0; i < results.size(); i++) { - std::pair<double, bool>& item = results[i]; - if (item.second) { // If data is stable - if (cur_start_idx == -1) { // This is the first window - cur_start_idx = i; - cur_stop_idx = i; - } else { // We are extending the window - cur_stop_idx = i; - } - } else { - if (cur_start_idx == -1) { // We haven't yet seen valid data - // Do nothing - } else if (win_start_idx == -1) { // We passed the first valid window - win_start_idx = cur_start_idx; - win_stop_idx = cur_stop_idx; - } else { // Update cached window if current window is larger - double cur_win_len = - results[cur_stop_idx].first - results[cur_start_idx].first; - double cached_win_len = - results[win_stop_idx].first - results[win_start_idx].first; - if (cur_win_len > cached_win_len) { - win_start_idx = cur_start_idx; - win_stop_idx = cur_stop_idx; - } - } - // Reset current window - cur_start_idx = -1; - cur_stop_idx = -1; - } - } - if (win_start_idx == -1) { - throw uhd::runtime_error( - "self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); - } - - double win_center = - (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; - double win_length = results[win_stop_idx].first - results[win_start_idx].first; - if (win_length < master_clk_period / 4) { - throw uhd::runtime_error( - "self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); - } - - // Cycle slip the relative delay by a clock cycle to prevent sample misalignment - // fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need - bool cycle_slip = (win_center - fpga_clk_delay >= master_clk_period); - if (cycle_slip) { - win_center -= master_clk_period; - } - - if (apply_delay) { - // Apply delay - win_center = clock->set_clock_delay( - X300_CLOCK_WHICH_ADC0, win_center); // Sets ADC0 and ADC1 - wait_for_clk_locked(0.1); - // Validate - for (size_t r = 0; r < radios.size(); r++) { - radios[r]->self_test_adc(2000); - } - } else { - // Restore delay - clock->set_clock_delay( - X300_CLOCK_WHICH_ADC0, cached_clk_delay); // Sets ADC0 and ADC1 - } - - // Teardown - for (size_t r = 0; r < radios.size(); r++) { - radios[r]->_adc->set_test_word("normal", "normal"); - radios[r]->_regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - } - UHD_LOGGER_INFO("X300 RADIO") - << (boost::format( - "ADC transfer delay self-cal done (FPGA->ADC=%.3fns%s, Window=%.3fns)") - % (win_center - fpga_clk_delay) % (cycle_slip ? " +cyc" : "") - % win_length); - - return win_center; -} -/**************************************************************************** - * Helpers - ***************************************************************************/ -void x300_radio_ctrl_impl::_update_atr_leds(const std::string& rx_ant, const size_t chan) -{ - // The "RX1" port is used by TwinRX and the "TX/RX" port is used by all - // other full-duplex dboards. We need to handle both here. - const bool is_txrx = (rx_ant == "TX/RX" or rx_ant == "RX1"); - const int TXRX_RX = (1 << 0); - const int TXRX_TX = (1 << 1); - const int RX2_RX = (1 << 2); - _leds.at(chan)->set_atr_reg(gpio_atr::ATR_REG_IDLE, 0); - _leds.at(chan)->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, is_txrx ? TXRX_RX : RX2_RX); - _leds.at(chan)->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, TXRX_TX); - _leds.at(chan)->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, RX2_RX | TXRX_TX); -} - -void x300_radio_ctrl_impl::_self_cal_adc_capture_delay(bool print_status) -{ - if (print_status) - UHD_LOGGER_INFO("X300 RADIO") << "Running ADC capture delay self-cal..."; - - static const uint32_t NUM_DELAY_STEPS = 32; // The IDELAYE2 element has 32 steps - static const uint32_t NUM_RETRIES = - 2; // Retry self-cal if it fails in warmup situations - static const int32_t MIN_WINDOW_LEN = 4; - - int32_t win_start = -1, win_stop = -1; - uint32_t iter = 0; - while (iter++ < NUM_RETRIES) { - for (uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { - // Apply delay - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, dly_tap); - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); - - uint32_t err_code = 0; - - // -- Test I Channel -- - // Put ADC in ramp test mode. Tie the other channel to all ones. - _adc->set_test_word("ramp", "ones"); - // Turn on the pattern checker in the FPGA. It will lock when it sees a zero - // and count deviations from the expected value - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - // 5ms @ 200MHz = 1 million samples - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - if (_regs->misc_ins_reg.read( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) { - err_code += _regs->misc_ins_reg.get( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR); - } else { - err_code += 100; // Increment error code by 100 to indicate no lock - } - - // -- Test Q Channel -- - // Put ADC in ramp test mode. Tie the other channel to all ones. - _adc->set_test_word("ones", "ramp"); - // Turn on the pattern checker in the FPGA. It will lock when it sees a zero - // and count deviations from the expected value - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - // 5ms @ 200MHz = 1 million samples - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - if (_regs->misc_ins_reg.read( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) { - err_code += _regs->misc_ins_reg.get( - radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR); - } else { - err_code += 100; // Increment error code by 100 to indicate no lock - } - - if (err_code == 0) { - if (win_start == -1) { // This is the first window - win_start = dly_tap; - win_stop = dly_tap; - } else { // We are extending the window - win_stop = dly_tap; - } - } else { - if (win_start != -1) { // A valid window turned invalid - if (win_stop - win_start >= MIN_WINDOW_LEN) { - break; // Valid window found - } else { - win_start = -1; // Reset window - } - } - } - // UHD_LOGGER_INFO("X300 RADIO") << (boost::format("CapTap=%d, Error=%d") % - // dly_tap % err_code); - } - - // Retry the self-cal if it fails - if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) - && iter < NUM_RETRIES /*not last iteration*/) { - win_start = -1; - win_stop = -1; - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - } else { - break; - } - } - _adc->set_test_word("normal", "normal"); - _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); - - if (win_start == -1) { - throw uhd::runtime_error( - "self_cal_adc_capture_delay: Self calibration failed. Convergence error."); - } - - if (win_stop - win_start < MIN_WINDOW_LEN) { - throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. " - "Valid window too narrow."); - } - - uint32_t ideal_tap = (win_stop + win_start) / 2; - _regs->misc_outs_reg.write( - radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, ideal_tap); - _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); - _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); - - if (print_status) { - double tap_delay = (1.0e12 / _radio_clk_rate) / (2 * 32); // in ps - UHD_LOGGER_INFO("X300 RADIO") - << boost::format("ADC capture delay self-cal done (Tap=%d, Window=%d, " - "TapDelay=%.3fps, Iter=%d)") - % ideal_tap % (win_stop - win_start) % tap_delay % iter; - } -} - -void x300_radio_ctrl_impl::_check_adc(const uint32_t val) -{ - // Wait for previous control transaction to flush - user_reg_read64(regs::RB_TEST); - // Wait for ADC test pattern to propagate - std::this_thread::sleep_for(std::chrono::microseconds(5)); - // Read value of RX readback register and verify - uint32_t adc_rb = static_cast<uint32_t>(user_reg_read64(regs::RB_TEST) >> 32); - adc_rb ^= 0xfffc0000; // adapt for I inversion in FPGA - if (val != adc_rb) { - throw uhd::runtime_error( - (boost::format("ADC self-test failed for %s. (Exp=0x%x, Got=0x%x)") - % unique_id() % val % adc_rb) - .str()); - } -} - -void x300_radio_ctrl_impl::_set_db_eeprom( - i2c_iface::sptr i2c, const size_t addr, const uhd::usrp::dboard_eeprom_t& db_eeprom) -{ - db_eeprom.store(*i2c, addr); - _db_eeproms[addr] = db_eeprom; -} - -void x300_radio_ctrl_impl::_set_command_time(const time_spec_t& spec, const size_t port) -{ - set_fe_cmd_time(spec, port); -} -/**************************************************************************** - * Helpers - ***************************************************************************/ -bool x300_radio_ctrl_impl::check_radio_config() -{ - UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::check_radio_config() "; - const fs_path rx_fe_path = fs_path("dboards" / _radio_slot / "rx_frontends"); - for (size_t chan = 0; chan < _num_rx_channels; chan++) { - if (_tree->exists(rx_fe_path / _rx_fe_map.at(chan).db_fe_name / "enabled")) { - const bool chan_active = _is_streamer_active(uhd::RX_DIRECTION, chan); - if (chan_active) { - _tree - ->access<bool>( - rx_fe_path / _rx_fe_map.at(chan).db_fe_name / "enabled") - .set(chan_active); - } - } - } - - const fs_path tx_fe_path = fs_path("dboards" / _radio_slot / "tx_frontends"); - for (size_t chan = 0; chan < _num_tx_channels; chan++) { - if (_tree->exists(tx_fe_path / _tx_fe_map.at(chan).db_fe_name / "enabled")) { - const bool chan_active = _is_streamer_active(uhd::TX_DIRECTION, chan); - if (chan_active) { - _tree - ->access<bool>( - tx_fe_path / _tx_fe_map.at(chan).db_fe_name / "enabled") - .set(chan_active); - } - } - } - - return true; -} - -/**************************************************************************** - * Register block - ***************************************************************************/ -UHD_RFNOC_BLOCK_REGISTER(x300_radio_ctrl, "X300Radio"); diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp deleted file mode 100644 index 63aac876d..000000000 --- a/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp +++ /dev/null @@ -1,246 +0,0 @@ -// -// Copyright 2015-2016 Ettus Research -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#ifndef INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP -#define INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP - -#include "x300_adc_ctrl.hpp" -#include "x300_clock_ctrl.hpp" -#include "x300_dac_ctrl.hpp" -#include "x300_regs.hpp" -#include <uhd/usrp/dboard_eeprom.hpp> -#include <uhd/usrp/dboard_manager.hpp> -#include <uhd/usrp/gpio_defs.hpp> -#include <uhd/utils/gain_group.hpp> -#include <uhdlib/rfnoc/radio_ctrl_impl.hpp> -#include <uhdlib/usrp/cores/rx_frontend_core_3000.hpp> -#include <uhdlib/usrp/cores/spi_core_3000.hpp> -#include <uhdlib/usrp/cores/tx_frontend_core_200.hpp> -#include <unordered_map> - -namespace uhd { namespace rfnoc { - -/*! \brief Provide access to an X300 radio. - */ -class x300_radio_ctrl_impl : public radio_ctrl_impl -{ -public: - typedef boost::shared_ptr<x300_radio_ctrl_impl> sptr; - - /************************************************************************ - * Structors - ***********************************************************************/ - UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(x300_radio_ctrl) - virtual ~x300_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); - std::string get_tx_antenna(const size_t chan); - std::string get_rx_antenna(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_rx_bandwidth(const double bandwidth, const size_t chan); - double get_tx_frequency(const size_t chan); - double get_rx_frequency(const size_t chan); - double get_rx_bandwidth(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 get_rx_gain(const size_t chan); - - std::vector<std::string> get_rx_lo_names(const size_t chan); - std::vector<std::string> get_rx_lo_sources( - const std::string& name, const size_t chan); - freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan); - - void set_rx_lo_source( - const std::string& src, const std::string& name, const size_t chan); - const std::string get_rx_lo_source(const std::string& name, const size_t chan); - - void set_rx_lo_export_enabled( - bool enabled, const std::string& name, const size_t chan); - bool get_rx_lo_export_enabled(const std::string& name, const size_t chan); - - double set_rx_lo_freq(double freq, const std::string& name, const size_t chan); - double get_rx_lo_freq(const std::string& name, 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); - - 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); - - double get_output_samp_rate(size_t port); - - /************************************************************************ - * Hardware setup and control - ***********************************************************************/ - /*! Set up the radio. No API calls may be made before this one. - */ - void setup_radio(uhd::i2c_iface::sptr zpu_i2c, - x300_clock_ctrl::sptr clock, - bool ignore_cal_file, - bool verbose); - - void reset_codec(); - - void self_test_adc(uint32_t ramp_time_ms = 100); - - static void extended_adc_test( - const std::vector<x300_radio_ctrl_impl::sptr>&, double duration_s); - - static void synchronize_dacs(const std::vector<x300_radio_ctrl_impl::sptr>& radios); - - static double self_cal_adc_xfer_delay( - const std::vector<x300_radio_ctrl_impl::sptr>& radios, - x300_clock_ctrl::sptr clock, - boost::function<void(double)> wait_for_clk_locked, - bool apply_delay); - -protected: - virtual bool check_radio_config(); - -private: - class radio_regmap_t : public uhd::soft_regmap_t - { - public: - typedef boost::shared_ptr<radio_regmap_t> sptr; - class misc_outs_reg_t : public uhd::soft_reg32_wo_t - { - public: - UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED, /*width*/ 1, /*shift*/ 0); //[0] - UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N, /*width*/ 1, /*shift*/ 1); //[1] - UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET, /*width*/ 1, /*shift*/ 2); //[2] - UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB, /*width*/ 1, /*shift*/ 3); //[3] - UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL, /*width*/ 5, /*shift*/ 4); //[8:4] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER_ENABLED, /*width*/ 1, /*shift*/ 9); //[9] - UHD_DEFINE_SOFT_REG_FIELD(DAC_SYNC, /*width*/ 1, /*shift*/ 10); //[10] - - misc_outs_reg_t() : uhd::soft_reg32_wo_t(regs::sr_addr(regs::MISC_OUTS)) - { - // Initial values - set(DAC_ENABLED, 0); - set(DAC_RESET_N, 0); - set(ADC_RESET, 0); - set(ADC_DATA_DLY_STB, 0); - set(ADC_DATA_DLY_VAL, 16); - set(ADC_CHECKER_ENABLED, 0); - set(DAC_SYNC, 0); - } - } misc_outs_reg; - - class misc_ins_reg_t : public uhd::soft_reg64_ro_t - { - public: - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 32); //[0] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 33); //[1] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 34); //[2] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 35); //[3] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 36); //[4] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 37); //[5] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 38); //[6] - UHD_DEFINE_SOFT_REG_FIELD( - ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 39); //[7] - - misc_ins_reg_t() : uhd::soft_reg64_ro_t(regs::rb_addr(regs::RB_MISC_IO)) {} - } misc_ins_reg; - - radio_regmap_t(int radio_num) - : soft_regmap_t("radio" + std::to_string(radio_num) + "_regmap") - { - add_to_map(misc_outs_reg, "misc_outs_reg", PRIVATE); - add_to_map(misc_ins_reg, "misc_ins_reg", PRIVATE); - } - }; - - struct x300_regs - { - static const uint32_t TX_FE_BASE = 224; - static const uint32_t RX_FE_BASE = 232; - }; - - void _update_atr_leds(const std::string& rx_ant, const size_t chan); - - void _self_cal_adc_capture_delay(bool print_status); - - void _check_adc(const uint32_t val); - - void _set_db_eeprom( - uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t&); - - void set_rx_fe_corrections(const uhd::fs_path& db_path, - const uhd::fs_path& rx_fe_corr_path, - const double lo_freq); - void set_tx_fe_corrections(const uhd::fs_path& db_path, - const uhd::fs_path& tx_fe_corr_path, - const double lo_freq); - - void _set_command_time(const uhd::time_spec_t& spec, const size_t port); - void set_fe_cmd_time(const time_spec_t& time, const size_t chan); - -private: // members - enum radio_connection_t { PRIMARY, SECONDARY }; - - radio_connection_t _radio_type; - std::string _radio_slot; - //! Radio clock rate is the rate at which the ADC and DAC are running at. - // Not necessarily this block's sampling rate (tick rate). - double _radio_clk_rate; - - radio_regmap_t::sptr _regs; - std::map<size_t, usrp::gpio_atr::gpio_atr_3000::sptr> _leds; - spi_core_3000::sptr _spi; - x300_adc_ctrl::sptr _adc; - x300_dac_ctrl::sptr _dac; - usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio; - - std::map<size_t, usrp::dboard_eeprom_t> _db_eeproms; - usrp::dboard_manager::sptr _db_manager; - - struct rx_fe_perif - { - std::string name; - std::string db_fe_name; - rx_frontend_core_3000::sptr core; - }; - struct tx_fe_perif - { - std::string name; - std::string db_fe_name; - tx_frontend_core_200::sptr core; - }; - - std::map<size_t, rx_fe_perif> _rx_fe_map; - std::map<size_t, tx_fe_perif> _tx_fe_map; - - bool _ignore_cal_file; - - std::unordered_map<size_t, uhd::gain_group::sptr> _tx_gain_groups; - std::unordered_map<size_t, uhd::gain_group::sptr> _rx_gain_groups; -}; /* class radio_ctrl_impl */ - -}} /* namespace uhd::rfnoc */ - -#endif /* INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP */ |