aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp')
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/cores/CMakeLists.txt1
-rw-r--r--host/lib/usrp/cores/dma_fifo_core_3000.cpp4
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_3000.cpp186
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_3000.hpp69
-rw-r--r--host/lib/usrp/cores/rx_vita_core_3000.cpp4
-rw-r--r--host/lib/usrp/device3/CMakeLists.txt25
-rw-r--r--host/lib/usrp/device3/device3_impl.cpp188
-rw-r--r--host/lib/usrp/device3/device3_impl.hpp210
-rw-r--r--host/lib/usrp/device3/device3_io_impl.cpp851
-rw-r--r--host/lib/usrp/multi_usrp.cpp70
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt2
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.cpp2
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.hpp2
-rw-r--r--host/lib/usrp/x300/x300_dboard_iface.cpp93
-rw-r--r--host/lib/usrp/x300/x300_dboard_iface.hpp115
-rw-r--r--host/lib/usrp/x300/x300_fw_common.h2
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp719
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp261
-rw-r--r--host/lib/usrp/x300/x300_io_impl.cpp607
-rw-r--r--host/lib/usrp/x300/x300_radio_ctrl_impl.cpp888
-rw-r--r--host/lib/usrp/x300/x300_radio_ctrl_impl.hpp198
-rw-r--r--host/lib/usrp/x300/x300_regs.hpp79
23 files changed, 3109 insertions, 1468 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index d20cc966b..f769417d9 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -52,6 +52,7 @@ ENDIF(ENABLE_GPSD)
INCLUDE_SUBDIRECTORY(cores)
INCLUDE_SUBDIRECTORY(dboard)
INCLUDE_SUBDIRECTORY(common)
+INCLUDE_SUBDIRECTORY(device3)
INCLUDE_SUBDIRECTORY(usrp1)
INCLUDE_SUBDIRECTORY(usrp2)
INCLUDE_SUBDIRECTORY(b100)
diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt
index 404fc6137..1e16dd39e 100644
--- a/host/lib/usrp/cores/CMakeLists.txt
+++ b/host/lib/usrp/cores/CMakeLists.txt
@@ -30,6 +30,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_200.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_3000.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp
${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rx_vita_core_3000.cpp
diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.cpp b/host/lib/usrp/cores/dma_fifo_core_3000.cpp
index 1a9d5dd5c..5df28f7c2 100644
--- a/host/lib/usrp/cores/dma_fifo_core_3000.cpp
+++ b/host/lib/usrp/cores/dma_fifo_core_3000.cpp
@@ -240,6 +240,10 @@ public:
flush();
}
+ virtual ~dma_fifo_core_3000_impl()
+ {
+ }
+
virtual void flush() {
//Clear the FIFO and hold it in that state
_fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1);
diff --git a/host/lib/usrp/cores/rx_frontend_core_3000.cpp b/host/lib/usrp/cores/rx_frontend_core_3000.cpp
new file mode 100644
index 000000000..23197cf5a
--- /dev/null
+++ b/host/lib/usrp/cores/rx_frontend_core_3000.cpp
@@ -0,0 +1,186 @@
+//
+// Copyright 2011-2012,2014-2016 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "rx_frontend_core_3000.hpp"
+#include "dsp_core_utils.hpp"
+#include <boost/math/special_functions/round.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/bind.hpp>
+#include <uhd/types/dict.hpp>
+
+using namespace uhd;
+
+#define REG_RX_FE_MAG_CORRECTION (_base + 0 ) //18 bits
+#define REG_RX_FE_PHASE_CORRECTION (_base + 4 ) //18 bits
+#define REG_RX_FE_OFFSET_I (_base + 8 ) //18 bits
+#define REG_RX_FE_OFFSET_Q (_base + 12) //18 bits
+#define REG_RX_FE_MAPPING (_base + 16)
+#define REG_RX_FE_HET_CORDIC_PHASE (_base + 20)
+
+#define FLAG_DSP_RX_MAPPING_SWAP_IQ (1 << 0)
+#define FLAG_DSP_RX_MAPPING_REAL_MODE (1 << 1)
+#define FLAG_DSP_RX_MAPPING_INVERT_Q (1 << 2)
+#define FLAG_DSP_RX_MAPPING_INVERT_I (1 << 3)
+#define FLAG_DSP_RX_MAPPING_REAL_DECIM (1 << 4)
+//#define FLAG_DSP_RX_MAPPING_RESERVED (1 << 5)
+//#define FLAG_DSP_RX_MAPPING_RESERVED (1 << 6)
+#define FLAG_DSP_RX_MAPPING_BYPASS_ALL (1 << 7)
+
+#define OFFSET_FIXED (1ul << 31)
+#define OFFSET_SET (1ul << 30)
+#define FLAG_MASK (OFFSET_FIXED | OFFSET_SET)
+
+using namespace uhd::usrp;
+
+static boost::uint32_t fs_to_bits(const double num, const size_t bits){
+ return boost::int32_t(boost::math::round(num * (1 << (bits-1))));
+}
+
+rx_frontend_core_3000::~rx_frontend_core_3000(void){
+ /* NOP */
+}
+
+const std::complex<double> rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0);
+const bool rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE = true;
+const std::complex<double> rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0);
+
+class rx_frontend_core_3000_impl : public rx_frontend_core_3000{
+public:
+ rx_frontend_core_3000_impl(wb_iface::sptr iface, const size_t base):
+ _i_dc_off(0), _q_dc_off(0),
+ _adc_rate(0.0),
+ _fe_conn(fe_connection_t("IQ")),
+ _iface(iface), _base(base)
+ {
+ //NOP
+ }
+
+ void set_adc_rate(const double rate) {
+ _adc_rate = rate;
+ }
+
+ void bypass_all(bool bypass_en) {
+ if (bypass_en) {
+ _iface->poke32(REG_RX_FE_MAPPING, FLAG_DSP_RX_MAPPING_BYPASS_ALL);
+ } else {
+ set_fe_connection(_fe_conn);
+ }
+ }
+
+ void set_fe_connection(const fe_connection_t& fe_conn) {
+ boost::uint32_t mapping_reg_val = 0;
+ switch (fe_conn.get_sampling_mode()) {
+ case fe_connection_t::REAL:
+ case fe_connection_t::HETERODYNE:
+ mapping_reg_val = FLAG_DSP_RX_MAPPING_REAL_MODE|FLAG_DSP_RX_MAPPING_REAL_DECIM;
+ break;
+ default:
+ mapping_reg_val = 0;
+ break;
+ }
+
+ if (fe_conn.is_iq_swapped()) mapping_reg_val |= FLAG_DSP_RX_MAPPING_SWAP_IQ;
+ if (fe_conn.is_i_inverted()) mapping_reg_val |= FLAG_DSP_RX_MAPPING_INVERT_I;
+ if (fe_conn.is_q_inverted()) mapping_reg_val |= FLAG_DSP_RX_MAPPING_INVERT_Q;
+
+ _iface->poke32(REG_RX_FE_MAPPING, mapping_reg_val);
+
+ UHD_ASSERT_THROW(_adc_rate!=0.0)
+ double cordic_freq = 0.0, actual_cordic_freq = 0.0;
+ if (fe_conn.get_sampling_mode() == fe_connection_t::HETERODYNE) {
+ //1. Remember the sign of the IF frequency.
+ // It will be discarded in the next step
+ int if_freq_sign = boost::math::sign(fe_conn.get_if_freq());
+ //2. Map IF frequency to the range [0, _adc_rate)
+ double if_freq = std::abs(std::fmod(fe_conn.get_if_freq(), _adc_rate));
+ //3. Map IF frequency to the range [-_adc_rate/2, _adc_rate/2)
+ // This is the aliased frequency
+ if (if_freq > (_adc_rate / 2.0)) {
+ if_freq -= _adc_rate;
+ }
+ //4. Set DSP offset to spin the signal in the opposite
+ // direction as the aliased frequency
+ cordic_freq = if_freq * (-if_freq_sign);
+ }
+ int32_t freq_word;
+ get_freq_and_freq_word(cordic_freq, _adc_rate, actual_cordic_freq, freq_word);
+ _iface->poke32(REG_RX_FE_HET_CORDIC_PHASE, boost::uint32_t(freq_word));
+
+ _fe_conn = fe_conn;
+ }
+
+ void set_dc_offset_auto(const bool enb) {
+ _set_dc_offset(enb ? 0 : OFFSET_FIXED);
+ }
+
+ std::complex<double> set_dc_offset(const std::complex<double> &off) {
+ static const double scaler = double(1ul << 29);
+ _i_dc_off = boost::math::iround(off.real()*scaler);
+ _q_dc_off = boost::math::iround(off.imag()*scaler);
+
+ _set_dc_offset(OFFSET_SET | OFFSET_FIXED);
+
+ return std::complex<double>(_i_dc_off/scaler, _q_dc_off/scaler);
+ }
+
+ void _set_dc_offset(const boost::uint32_t flags) {
+ _iface->poke32(REG_RX_FE_OFFSET_I, flags | (_i_dc_off & ~FLAG_MASK));
+ _iface->poke32(REG_RX_FE_OFFSET_Q, flags | (_q_dc_off & ~FLAG_MASK));
+ }
+
+ void set_iq_balance(const std::complex<double> &cor) {
+ _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(cor.real(), 18));
+ _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18));
+ }
+
+ void populate_subtree(uhd::property_tree::sptr subtree) {
+ subtree->create<std::complex<double> >("dc_offset/value")
+ .set(DEFAULT_DC_OFFSET_VALUE)
+ .set_coercer(boost::bind(&rx_frontend_core_3000::set_dc_offset, this, _1))
+ ;
+ subtree->create<bool>("dc_offset/enable")
+ .set(DEFAULT_DC_OFFSET_ENABLE)
+ .add_coerced_subscriber(boost::bind(&rx_frontend_core_3000::set_dc_offset_auto, this, _1))
+ ;
+ subtree->create<std::complex<double> >("iq_balance/value")
+ .set(DEFAULT_IQ_BALANCE_VALUE)
+ .add_coerced_subscriber(boost::bind(&rx_frontend_core_3000::set_iq_balance, this, _1))
+ ;
+ }
+
+ double get_output_rate() {
+ switch (_fe_conn.get_sampling_mode()) {
+ case fe_connection_t::REAL:
+ case fe_connection_t::HETERODYNE:
+ return _adc_rate / 2;
+ default:
+ return _adc_rate;
+ }
+ return _adc_rate;
+ }
+
+private:
+ boost::int32_t _i_dc_off, _q_dc_off;
+ double _adc_rate;
+ fe_connection_t _fe_conn;
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+rx_frontend_core_3000::sptr rx_frontend_core_3000::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new rx_frontend_core_3000_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/rx_frontend_core_3000.hpp b/host/lib/usrp/cores/rx_frontend_core_3000.hpp
new file mode 100644
index 000000000..baa58331e
--- /dev/null
+++ b/host/lib/usrp/cores/rx_frontend_core_3000.hpp
@@ -0,0 +1,69 @@
+//
+// Copyright 2011,2014-2016 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/fe_connection.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <complex>
+#include <string>
+
+class rx_frontend_core_3000 : boost::noncopyable{
+public:
+ static const std::complex<double> DEFAULT_DC_OFFSET_VALUE;
+ static const bool DEFAULT_DC_OFFSET_ENABLE;
+ static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE;
+
+ typedef boost::shared_ptr<rx_frontend_core_3000> sptr;
+
+ virtual ~rx_frontend_core_3000(void) = 0;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ /*! Set the input sampling rate (i.e. ADC rate)
+ */
+ virtual void set_adc_rate(const double rate) = 0;
+
+ virtual void bypass_all(bool bypass_en) = 0;
+
+ virtual void set_fe_connection(const uhd::usrp::fe_connection_t& fe_conn) = 0;
+
+ virtual void set_dc_offset_auto(const bool enb) = 0;
+
+ virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0;
+
+ virtual void set_iq_balance(const std::complex<double> &cor) = 0;
+
+ virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;
+
+ /*! Return the sampling rate at the output
+ *
+ * In real mode, the frontend core will decimate the sampling rate by a
+ * factor of 2.
+ *
+ * \returns RX sampling rate
+ */
+ virtual double get_output_rate(void) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp
index 52121837e..54c57c2d5 100644
--- a/host/lib/usrp/cores/rx_vita_core_3000.cpp
+++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp
@@ -82,7 +82,9 @@ struct rx_vita_core_3000_impl : rx_vita_core_3000
void clear(void)
{
- this->configure_flow_control(0); //disable fc
+ // FC should never be disabled, this will actually become
+ // impossible in the future
+ //this->configure_flow_control(0); //disable fc
}
void set_nsamps_per_packet(const size_t nsamps)
diff --git a/host/lib/usrp/device3/CMakeLists.txt b/host/lib/usrp/device3/CMakeLists.txt
new file mode 100644
index 000000000..83f01a2e7
--- /dev/null
+++ b/host/lib/usrp/device3/CMakeLists.txt
@@ -0,0 +1,25 @@
+#
+# Copyright 2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# 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_impl.cpp b/host/lib/usrp/device3/device3_impl.cpp
new file mode 100644
index 000000000..7fcbc01b2
--- /dev/null
+++ b/host/lib/usrp/device3/device3_impl.cpp
@@ -0,0 +1,188 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "device3_impl.hpp"
+#include "graph_impl.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/rfnoc/block_ctrl_base.hpp>
+#include <boost/make_shared.hpp>
+#include <algorithm>
+
+#define UHD_DEVICE3_LOG() UHD_LOGV(never)
+
+using namespace uhd::usrp;
+
+device3_impl::device3_impl()
+ : _sid_framer(0)
+{
+ _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.
+ BOOST_FOREACH(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,
+ uhd::endianness_t endianness
+) {
+ // 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++) {
+ UHD_DEVICE3_LOG() << "[RFNOC] ------- Block Setup -----------" << std::endl;
+ // 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_DEVICE3_LOG() << 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(
+ endianness == ENDIANNESS_BIG,
+ xport.send,
+ xport.recv,
+ xport.send_sid,
+ str(boost::format("CE_%02d_Port_%02X") % i % ctrl_sid.get_dst_endpoint())
+ );
+ UHD_DEVICE3_LOG() << "OK" << std::endl;
+ uint64_t noc_id = ctrl->peek64(uhd::rfnoc::SR_READBACK_REG_ID);
+ UHD_DEVICE3_LOG() << str(boost::format("Port %d: Found NoC-Block with ID %016X.") % int(ctrl_sid.get_dst_endpoint()) % noc_id) << std::endl;
+ 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_DEVICE3_LOG() << "Using default block configuration." << std::endl;
+ block_def = uhd::rfnoc::blockdef::make_from_noc_id(uhd::rfnoc::DEFAULT_NOC_ID);
+ }
+ UHD_ASSERT_THROW(block_def);
+ make_args.ctrl_ifaces[0] = ctrl;
+ BOOST_FOREACH(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_DEVICE3_LOG() << 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(
+ endianness == ENDIANNESS_BIG,
+ xport1.send,
+ xport1.recv,
+ xport1.send_sid,
+ str(boost::format("CE_%02d_Port_%02d") % i % ctrl_sid.get_dst_endpoint())
+ );
+ UHD_DEVICE3_LOG() << "OK" << std::endl;
+ make_args.ctrl_ifaces[port_number] = ctrl1;
+ }
+
+ make_args.base_address = xport.send_sid.get_dst();
+ make_args.device_index = device_index;
+ make_args.tree = subtree;
+ make_args.is_big_endian = (endianness == ENDIANNESS_BIG);
+ _rfnoc_block_ctrl.push_back(uhd::rfnoc::block_ctrl_base::make(make_args, noc_id));
+ }
+}
+
+
+uhd::rfnoc::graph::sptr device3_impl::create_graph(const std::string &name)
+{
+ return boost::make_shared<uhd::rfnoc::graph_impl>(
+ name,
+ shared_from_this()
+ );
+}
+
diff --git a/host/lib/usrp/device3/device3_impl.hpp b/host/lib/usrp/device3/device3_impl.hpp
new file mode 100644
index 000000000..0d94ae21c
--- /dev/null
+++ b/host/lib/usrp/device3/device3_impl.hpp
@@ -0,0 +1,210 @@
+//
+// Copyright 2014-2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// 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 <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/transport/chdr.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/sid.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/types/endianness.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/device3.hpp>
+#include "xports.hpp"
+// Common FPGA cores:
+#include "ctrl_iface.hpp"
+#include "rx_dsp_core_3000.hpp"
+#include "tx_dsp_core_3000.hpp"
+#include "rx_vita_core_3000.hpp"
+#include "tx_vita_core_3000.hpp"
+#include "rx_frontend_core_200.hpp"
+#include "tx_frontend_core_200.hpp"
+#include "time_core_3000.hpp"
+#include "gpio_atr_3000.hpp"
+// RFNoC-specific includes:
+#include "radio_ctrl_impl.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_TX_FC_RESPONSE_CYCLES = 0; // Cycles: Off.
+
+static const size_t DEVICE3_TX_MAX_HDR_LEN = uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(boost::uint64_t); // Bytes
+static const size_t DEVICE3_RX_MAX_HDR_LEN = uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(boost::uint64_t); // Bytes
+
+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,
+ 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;
+ //! How often the downstream block should send ACKs in cycles
+ size_t tx_fc_response_cycles;
+ 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)
+ , tx_fc_response_cycles(DEVICE3_TX_FC_RESPONSE_CYCLES)
+ {};
+ };
+
+ /***********************************************************************
+ * 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
+ **********************************************************************/
+ // The 'rate' argument is so we can use these as subscribers to rate changes
+public: // TODO make these protected again
+ void update_rx_streamers(double rate=-1.0);
+ void update_tx_streamers(double rate=-1.0);
+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(); };
+ virtual uhd::endianness_t get_transport_endianness(size_t mb_index) = 0;
+
+ //! Is called after a streamer is generated
+ virtual void post_streamer_hooks(uhd::direction_t) {};
+
+ /***********************************************************************
+ * 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,
+ uhd::endianness_t endianness
+ );
+
+ /***********************************************************************
+ * Members
+ **********************************************************************/
+ //! A counter, designed to create unique SIDs
+ size_t _sid_framer;
+
+ // 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 */
+// vim: sw=4 expandtab:
diff --git a/host/lib/usrp/device3/device3_io_impl.cpp b/host/lib/usrp/device3/device3_io_impl.cpp
new file mode 100644
index 000000000..8c61f8f15
--- /dev/null
+++ b/host/lib/usrp/device3/device3_io_impl.cpp
@@ -0,0 +1,851 @@
+//
+// Copyright 2014-2016 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// Provides streaming-related functions which are used by device3 objects.
+
+#define DEVICE3_STREAMER // For the super_*_packet_handlers
+
+#include "device3_impl.hpp"
+#include <uhd/rfnoc/constants.hpp>
+#include <uhd/rfnoc/source_block_ctrl_base.hpp>
+#include <uhd/rfnoc/sink_block_ctrl_base.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include "../common/async_packet_handler.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "../../rfnoc/rx_stream_terminator.hpp"
+#include "../../rfnoc/tx_stream_terminator.hpp"
+#include <uhd/rfnoc/rate_node_ctrl.hpp>
+#include <uhd/rfnoc/radio_ctrl.hpp>
+
+#define UHD_STREAMER_LOG() UHD_LOGV(never)
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+//! CVITA uses 12-Bit sequence numbers
+static const boost::uint32_t HW_SEQ_NUM_MASK = 0xfff;
+
+
+/***********************************************************************
+ * 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_;
+ BOOST_FOREACH(const size_t chan_idx, args.channels) {
+ //// Find block ID for this channel:
+ if (args.args.has_key(str(boost::format("block_id%d") % chan_idx))) {
+ chan_list.push_back(
+ uhd::rfnoc::block_id_t(
+ args.args.pop(str(boost::format("block_id%d") % chan_idx))
+ )
+ );
+ chan_args.push_back(args.args);
+ } else if (args.args.has_key("block_id")) {
+ chan_list.push_back(args.args.get("block_id"));
+ chan_args.push_back(args.args);
+ chan_args.back().pop("block_id");
+ } else {
+ throw uhd::runtime_error(str(
+ boost::format("Cannot create streamers: No block_id specified for channel %d.")
+ % chan_idx
+ ));
+ }
+ //// Find block port for this channel
+ if (args.args.has_key(str(boost::format("block_port%d") % chan_idx))) {
+ chan_args.back()["block_port"] = args.args.pop(str(boost::format("block_port%d") % chan_idx));
+ } else if (args.args.has_key("block_port")) {
+ // We have to write it again, because the chan args from the
+ // property tree might have overwritten this
+ chan_args.back()["block_port"] = args.args.get("block_port");
+ }
+ }
+}
+
+
+/***********************************************************************
+ * RX Flow Control Functions
+ **********************************************************************/
+//! Stores the state of RX flow control
+struct rx_fc_cache_t
+{
+ rx_fc_cache_t():
+ last_seq_in(0){}
+ size_t last_seq_in;
+};
+
+/*! 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_pkts = (static_cast<size_t>(sw_buff_size * fullness_factor) / pkt_size);
+ if (rx_args.has_key("max_recv_window")) {
+ window_in_pkts = std::min(
+ window_in_pkts,
+ rx_args.cast<size_t>("max_recv_window", window_in_pkts)
+ );
+ }
+ if (window_in_pkts == 0) {
+ 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) >= pkt_size * window_in_pkts);
+ return window_in_pkts;
+}
+
+
+/*! Send out RX flow control packets.
+ *
+ * For an rx stream, this function takes care of sending back
+ * a flow control packet to the source telling it which
+ * packets have been consumed.
+ *
+ * This function should only be called by the function handling
+ * the rx stream, usually recv() in super_recv_packet_handler.
+ *
+ * \param sid The SID that goes into this packet. This is the reversed()
+ * version of the data stream's SID.
+ * \param xport A transport object over which to send the data
+ * \param big_endian Endianness of the transport
+ * \param seq32_state Pointer to a variable that saves the 32-Bit state
+ * of the sequence numbers, since we only have 12 Bit
+ * sequence numbers in CHDR.
+ * \param last_seq The value to send: The last consumed packet's sequence number.
+ */
+static void handle_rx_flowctrl(
+ const sid_t &sid,
+ zero_copy_if::sptr xport,
+ endianness_t endianness,
+ boost::shared_ptr<rx_fc_cache_t> fc_cache,
+ const size_t last_seq
+) {
+ static const size_t RXFC_PACKET_LEN_IN_WORDS = 2;
+ static const size_t RXFC_CMD_CODE_OFFSET = 0;
+ static const size_t RXFC_SEQ_NUM_OFFSET = 1;
+
+ managed_send_buffer::sptr buff = xport->get_send_buff(0.0);
+ if (not buff) {
+ throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer");
+ }
+ boost::uint32_t *pkt = buff->cast<boost::uint32_t *>();
+
+ // Recover sequence number. The sequence numbers handled by the streamers
+ // are 12 Bits, but we want to know the 32-Bit sequence number.
+ size_t &seq32 = fc_cache->last_seq_in;
+ const size_t seq12 = seq32 & HW_SEQ_NUM_MASK;
+ if (last_seq < seq12)
+ seq32 += (HW_SEQ_NUM_MASK + 1);
+ seq32 &= ~HW_SEQ_NUM_MASK;
+ seq32 |= last_seq;
+
+ // Super-verbose mode:
+ //static size_t fc_pkt_count = 0;
+ //UHD_MSG(status) << "sending flow ctrl packet " << fc_pkt_count++ << ", acking " << str(boost::format("%04d\tseq_sw==0x%08x") % last_seq % seq32) << std::endl;
+
+ //load packet info
+ vrt::if_packet_info_t packet_info;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_FC;
+ packet_info.num_payload_words32 = RXFC_PACKET_LEN_IN_WORDS;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = seq32;
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = 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;
+
+ if (endianness == ENDIANNESS_BIG) {
+ // Load Header:
+ vrt::chdr::if_hdr_pack_be(pkt, packet_info);
+ // Load Payload: (the sequence number)
+ pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htonx<boost::uint32_t>(0);
+ pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET] = uhd::htonx<boost::uint32_t>(seq32);
+ } else {
+ // Load Header:
+ vrt::chdr::if_hdr_pack_le(pkt, packet_info);
+ // Load Payload: (the sequence number)
+ pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htowx<boost::uint32_t>(0);
+ pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET] = uhd::htowx<boost::uint32_t>(seq32);
+ }
+
+ //std::cout << " SID=" << std::hex << sid << " hdr bits=" << packet_info.packet_type << " seq32=" << seq32 << std::endl;
+ //std::cout << "num_packet_words32: " << packet_info.num_packet_words32 << std::endl;
+ //for (size_t i = 0; i < packet_info.num_packet_words32; i++) {
+ //std::cout << str(boost::format("0x%08x") % pkt[i]) << " ";
+ //if (i % 2) {
+ //std::cout << std::endl;
+ //}
+ //}
+
+ //send the buffer over the interface
+ buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32));
+}
+
+/***********************************************************************
+ * TX Flow Control Functions
+ **********************************************************************/
+//! Stores the state of TX flow control
+struct tx_fc_cache_t
+{
+ tx_fc_cache_t(void):
+ stream_channel(0),
+ device_channel(0),
+ last_seq_out(0),
+ last_seq_ack(0),
+ seq_queue(1){}
+ size_t stream_channel;
+ size_t device_channel;
+ size_t last_seq_out;
+ size_t last_seq_ack;
+ uhd::transport::bounded_buffer<size_t> seq_queue;
+ boost::shared_ptr<device3_impl::async_md_type> async_queue;
+ boost::shared_ptr<device3_impl::async_md_type> old_async_queue;
+};
+
+/*! Return the size of the flow control window in packets.
+ *
+ * If the return value of this function is F, the last tx'd packet
+ * has index N and the last ack'd packet has index M, the amount of
+ * FC credit we have is C = F + M - N (i.e. we can send C more packets
+ * before getting another ack).
+ *
+ * Note: If `send_buff_size` is set in \p tx_hints, this will
+ * override hw_buff_size_.
+ */
+static size_t get_tx_flow_control_window(
+ size_t pkt_size,
+ const double hw_buff_size_,
+ const device_addr_t& tx_hints
+) {
+ double hw_buff_size = tx_hints.cast<double>("send_buff_size", hw_buff_size_);
+ size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / pkt_size);
+ if (window_in_pkts == 0) {
+ throw uhd::value_error("send_buff_size must be larger than the send_frame_size.");
+ }
+ return window_in_pkts;
+}
+
+static managed_send_buffer::sptr get_tx_buff_with_flowctrl(
+ task::sptr /*holds ref*/,
+ boost::shared_ptr<tx_fc_cache_t> fc_cache,
+ zero_copy_if::sptr xport,
+ size_t fc_window,
+ const double timeout
+){
+ while (true)
+ {
+ // delta is the amount of FC credit we've used up
+ const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK);
+ // If we want to send another packet, we must have FC credit left
+ if ((delta & HW_SEQ_NUM_MASK) < fc_window)
+ break;
+
+ // If credit is all used up, we check seq_queue for more.
+ const bool ok = fc_cache->seq_queue.pop_with_timed_wait(fc_cache->last_seq_ack, timeout);
+ if (not ok) {
+ return managed_send_buffer::sptr(); //timeout waiting for flow control
+ }
+ }
+
+ managed_send_buffer::sptr buff = xport->get_send_buff(timeout);
+ if (buff) {
+ fc_cache->last_seq_out++; //update seq, this will actually be a send
+ }
+ return buff;
+}
+
+#define DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL 0
+/*! Handle incoming messages. If they're flow control, update the TX FC cache.
+ * Otherwise, 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<tx_fc_cache_t> fc_cache,
+ zero_copy_if::sptr xport,
+ endianness_t endianness,
+ 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(boost::uint32_t);
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+
+ //unpacking can fail
+ boost::uint32_t (*endian_conv)(boost::uint32_t) = uhd::ntohx;
+ try
+ {
+ if (endianness == ENDIANNESS_BIG)
+ {
+ vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info);
+ endian_conv = uhd::ntohx;
+ }
+ else
+ {
+ vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info);
+ endian_conv = uhd::wtohx;
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl;
+ 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(
+ endian_conv,
+ metadata,
+ if_packet_info,
+ packet_buff,
+ tick_rate,
+ fc_cache->stream_channel
+ );
+
+ // TODO: Shouldn't we be polling if_packet_info.packet_type == PACKET_TYPE_FC?
+ // Thing is, on X300, packet_type == 0, so that wouldn't work. But it seems it should.
+ //The FC response and the burst ack are two indicators that the radio
+ //consumed packets. Use them to update the FC metadata
+ if (metadata.event_code == DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) {
+ const size_t seq = metadata.user_payload[0];
+ fc_cache->seq_queue.push_with_pop_on_full(seq);
+ }
+
+ //FC responses don't propagate up to the user so filter them here
+ if (metadata.event_code != DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) {
+ fc_cache->async_queue->push_with_pop_on_full(metadata);
+ metadata.channel = fc_cache->device_channel;
+ fc_cache->old_async_queue->push_with_pop_on_full(metadata);
+ standard_async_msg_prints(metadata);
+ }
+}
+
+
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+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(double /* rate */)
+{
+ BOOST_FOREACH(const std::string &block_id, _rx_streamers.keys()) {
+ UHD_STREAMER_LOG() << "[Device3] updating RX streamer to " << block_id << std::endl;
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::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;
+ }
+ my_streamer->set_tick_rate(tick_rate);
+ double samp_rate = my_streamer->get_terminator()->get_output_samp_rate();
+ if (samp_rate == rfnoc::rate_node_ctrl::RATE_UNDEFINED) {
+ samp_rate = 1.0;
+ }
+ // This formula is not derived by any scientific means -- we just need to
+ // increase the failure threshold as we increase rates. For 1 Msps, we use
+ // the default.
+ const size_t alignment_failure_factor = std::max(size_t(1), size_t(samp_rate * 1000 / tick_rate));
+ double scaling = my_streamer->get_terminator()->get_output_scale_factor();
+ if (scaling == rfnoc::scalar_node_ctrl::SCALE_UNDEFINED) {
+ scaling = 1/32767.;
+ }
+ UHD_STREAMER_LOG() << " New tick_rate == " << tick_rate << " New samp_rate == " << samp_rate << " New scaling == " << scaling << std::endl;
+
+ my_streamer->set_tick_rate(tick_rate);
+ my_streamer->set_samp_rate(samp_rate);
+ // 1000 packets is the default alignment failure threshold
+ my_streamer->set_alignment_failure_threshold(1000 * alignment_failure_factor);
+ 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<sph::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++) {
+ // Get block ID and mb index
+ uhd::rfnoc::block_id_t block_id = chan_list[stream_i];
+ UHD_STREAMER_LOG() << "[RX Streamer] chan " << stream_i << " connecting to " << block_id << std::endl;
+ // 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);
+
+ //allocate sid and create transport
+ uhd::sid_t stream_address = blk_ctrl->get_address(block_port);
+ UHD_STREAMER_LOG() << "[RX Streamer] creating rx stream " << rx_hints.to_string() << std::endl;
+ both_xports_t xport = make_transport(stream_address, RX_DATA, rx_hints);
+ UHD_STREAMER_LOG() << std::hex << "[RX Streamer] data_sid = " << xport.send_sid << std::dec << " actual recv_buff_size = " << xport.recv_buff_size << std::endl;
+
+ // Configure the block
+ blk_ctrl->set_destination(xport.send_sid.get_src(), block_port);
+
+ blk_ctrl->sr_write(uhd::rfnoc::SR_RESP_OUT_DST_SID, xport.send_sid.get_src(), block_port);
+ UHD_STREAMER_LOG() << "[RX Streamer] resp_out_dst_sid == " << xport.send_sid.get_src() << std::endl;
+
+ // 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_STREAMER_LOG() << "[RX Streamer] Number of upstream radio nodes: " << upstream_radio_nodes.size() << std::endl;
+ BOOST_FOREACH(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);
+ }
+
+ // 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 = xport.recv->get_recv_frame_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_STREAMER_LOG() << "[RX Streamer] spp == " << spp << std::endl;
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer)
+ my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+ my_streamer->resize(chan_list.size());
+
+ //init some streamer stuff
+ std::string conv_endianness;
+ if (get_transport_endianness(mb_index) == 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);
+
+ //flow control setup
+ const size_t pkt_size = spp * bpi + stream_options.rx_max_len_hdr;
+ const size_t fc_window = get_rx_flow_control_window(pkt_size, xport.recv_buff_size, rx_hints);
+ const size_t fc_handle_window = std::max<size_t>(1, fc_window / stream_options.rx_fc_request_freq);
+ UHD_STREAMER_LOG()<< "[RX Streamer] Flow Control Window (minus one) = " << fc_window-1 << ", Flow Control Handler Window = " << fc_handle_window << std::endl;
+ blk_ctrl->configure_flow_control_out(
+ fc_window-1, // Leave one space for overrun packets TODO make this obsolete
+ block_port
+ );
+
+ //Give the streamer a functor to get the recv_buffer
+ //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency
+ my_streamer->set_xport_chan_get_buff(
+ stream_i,
+ boost::bind(&zero_copy_if::get_recv_buff, xport.recv, _1),
+ 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
+ my_streamer->set_overflow_handler(
+ stream_i,
+ boost::bind(
+ &uhd::rfnoc::rx_stream_terminator::handle_overrun, recv_terminator,
+ boost::weak_ptr<uhd::rx_streamer>(my_streamer), stream_i
+ )
+ );
+
+ //Give the streamer a functor to send flow control messages
+ //handle_rx_flowctrl is static and has no lifetime issues
+ boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t());
+ my_streamer->set_xport_handle_flowctrl(
+ stream_i, boost::bind(
+ &handle_rx_flowctrl,
+ xport.send_sid,
+ xport.send,
+ get_transport_endianness(mb_index),
+ fc_cache,
+ _1
+ ),
+ fc_handle_window,
+ true/*init*/
+ );
+
+ //Give the streamer a functor issue stream cmd
+ //bind requires a shared pointer to add a streamer->framer lifetime dependency
+ my_streamer->set_issue_stream_cmd(
+ stream_i,
+ boost::bind(&uhd::rfnoc::source_block_ctrl_base::issue_stream_cmd, blk_ctrl, _1, block_port)
+ );
+
+ // Tell the streamer which SID is valid for this channel
+ my_streamer->set_xport_chan_sid(stream_i, true, xport.send_sid);
+ }
+
+ // Connect the terminator to the streamer
+ my_streamer->set_terminator(recv_terminator);
+
+ // 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<sph::recv_packet_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(double /* rate */)
+{
+ BOOST_FOREACH(const std::string &block_id, _tx_streamers.keys()) {
+ UHD_STREAMER_LOG() << "[Device3] updating TX streamer: " << block_id << std::endl;
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::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_STREAMER_LOG() << " New tick_rate == " << tick_rate << " New samp_rate == " << samp_rate << " New scaling == " << scaling << std::endl;
+ 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<sph::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++) {
+ // 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);
+
+ //allocate sid and create transport
+ uhd::sid_t stream_address = blk_ctrl->get_address(block_port);
+ UHD_STREAMER_LOG() << "[TX Streamer] creating tx stream " << tx_hints.to_string() << std::endl;
+ both_xports_t xport = make_transport(stream_address, TX_DATA, tx_hints);
+ UHD_STREAMER_LOG() << std::hex << "[TX Streamer] data_sid = " << xport.send_sid << std::dec << std::endl;
+
+ // 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", xport.send->get_send_frame_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_STREAMER_LOG() << "[TX Streamer] spp == " << spp << std::endl;
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer)
+ my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+ my_streamer->resize(chan_list.size());
+
+ //init some streamer stuff
+ std::string conv_endianness;
+ if (get_transport_endianness(mb_index) == 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);
+
+ //flow control setup
+ const size_t pkt_size = spp * bpi + stream_options.tx_max_len_hdr;
+ // For flow control, this value is used to determine the window size in *packets*
+ size_t fc_window = get_tx_flow_control_window(
+ pkt_size, // This is the maximum packet size
+ blk_ctrl->get_fifo_size(block_port),
+ tx_hints // This can override the value reported by the block!
+ );
+ const size_t fc_handle_window = std::max<size_t>(1, fc_window / stream_options.tx_fc_response_freq);
+ UHD_STREAMER_LOG() << "[TX Streamer] Flow Control Window = " << fc_window << ", Flow Control Handler Window = " << fc_handle_window << std::endl;
+ blk_ctrl->configure_flow_control_in(
+ stream_options.tx_fc_response_cycles,
+ fc_handle_window, /*pkts*/
+ block_port
+ );
+
+ boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t());
+ fc_cache->stream_channel = stream_i;
+ fc_cache->device_channel = mb_index;
+ fc_cache->async_queue = async_md;
+ fc_cache->old_async_queue = _async_md;
+
+ boost::function<double(void)> tick_rate_retriever = boost::bind(
+ &rfnoc::tick_node_ctrl::get_tick_rate,
+ send_terminator,
+ std::set< rfnoc::node_ctrl_base::sptr >() // Need to specify default args with bind
+ );
+ task::sptr task = task::make(
+ boost::bind(
+ &handle_tx_async_msgs,
+ fc_cache,
+ xport.recv,
+ get_transport_endianness(mb_index),
+ tick_rate_retriever
+ )
+ );
+
+ blk_ctrl->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.recv_sid.get_dst(), block_port);
+ UHD_STREAMER_LOG() << "[TX Streamer] resp_in_dst_sid == " << boost::format("0x%04X") % xport.recv_sid.get_dst() << std::endl;
+ // Find all downstream radio nodes and set their response in 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_STREAMER_LOG() << "[TX Streamer] Number of downstream radio nodes: " << downstream_radio_nodes.size() << std::endl;
+ BOOST_FOREACH(const boost::shared_ptr<uhd::rfnoc::radio_ctrl> &node, downstream_radio_nodes) {
+ node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.send_sid.get_src(), block_port);
+ }
+
+ //Give the streamer a functor to get the send buffer
+ //get_tx_buff_with_flowctrl is static so bind has no lifetime issues
+ //xport.send (sptr) is required to add streamer->data-transport lifetime dependency
+ //task (sptr) is required to add a streamer->async-handler lifetime dependency
+ my_streamer->set_xport_chan_get_buff(
+ stream_i,
+ boost::bind(&get_tx_buff_with_flowctrl, task, fc_cache, xport.send, fc_window, _1)
+ );
+ //Give the streamer a functor handled received async messages
+ my_streamer->set_async_receiver(
+ boost::bind(&async_md_type::pop_with_timed_wait, async_md, _1, _2)
+ );
+ my_streamer->set_xport_chan_sid(stream_i, true, xport.send_sid);
+ // CHDR does not support trailers
+ my_streamer->set_enable_trailer(false);
+ }
+
+ // Connect the terminator to the streamer
+ my_streamer->set_terminator(send_terminator);
+
+ // 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<sph::send_packet_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/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp
index 0c3c78fc6..cb1ba8784 100644
--- a/host/lib/usrp/multi_usrp.cpp
+++ b/host/lib/usrp/multi_usrp.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2013 Ettus Research LLC
+// Copyright 2010-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/convert.hpp>
#include <uhd/utils/soft_register.hpp>
+#include "legacy_compat.hpp"
#include <boost/assign/list_of.hpp>
#include <boost/thread.hpp>
#include <boost/foreach.hpp>
@@ -390,12 +391,28 @@ public:
multi_usrp_impl(const device_addr_t &addr){
_dev = device::make(addr, device::USRP);
_tree = _dev->get_tree();
+ _is_device3 = bool(boost::dynamic_pointer_cast<uhd::device3>(_dev));
+
+ if (is_device3()) {
+ _legacy_compat = rfnoc::legacy_compat::make(get_device3(), addr);
+ }
}
device::sptr get_device(void){
return _dev;
}
+ bool is_device3(void) {
+ return _is_device3;
+ }
+
+ device3::sptr get_device3(void) {
+ if (not is_device3()) {
+ throw uhd::type_error("Cannot call get_device3() on a non-generation 3 device.");
+ }
+ return boost::dynamic_pointer_cast<uhd::device3>(_dev);
+ }
+
dict<std::string, std::string> get_usrp_rx_info(size_t chan){
mboard_chan_pair mcp = rx_chan_to_mcp(chan);
dict<std::string, std::string> usrp_info;
@@ -608,7 +625,12 @@ public:
void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){
if (chan != ALL_CHANS){
- _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd);
+ if (is_device3()) {
+ mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ _legacy_compat->issue_stream_cmd(stream_cmd, mcp.mboard, mcp.chan);
+ } else {
+ _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd);
+ }
return;
}
for (size_t c = 0; c < get_rx_num_channels(); c++){
@@ -743,6 +765,9 @@ public:
******************************************************************/
rx_streamer::sptr get_rx_stream(const stream_args_t &args) {
_check_link_rate(args, false);
+ if (is_device3()) {
+ return _legacy_compat->get_rx_stream(args);
+ }
return this->get_device()->get_rx_stream(args);
}
@@ -809,28 +834,10 @@ public:
}
tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){
- tune_request_t local_request = tune_request;
-
- // If any mixer is driven by an external LO the daughterboard assumes that no CORDIC correction is
- // necessary. Since the LO might be sourced from another daughterboard which would normally apply a
- // cordic correction we must disable all DSP tuning to ensure identical configurations across daughterboards.
- if (tune_request.dsp_freq_policy == tune_request.POLICY_AUTO and
- tune_request.rf_freq_policy == tune_request.POLICY_AUTO)
- {
- for (size_t c = 0; c < get_rx_num_channels(); c++) {
- UHD_VAR(get_rx_lo_source(ALL_LOS, c));
- if (get_rx_lo_source(ALL_LOS, c) == "external") {
- local_request.dsp_freq_policy = tune_request.POLICY_MANUAL;
- local_request.dsp_freq = 0;
- break;
- }
- }
- }
-
tune_result_t result = tune_xx_subdev_and_dsp(RX_SIGN,
_tree->subtree(rx_dsp_root(chan)),
_tree->subtree(rx_rf_fe_root(chan)),
- local_request);
+ tune_request);
//do_tune_freq_results_message(tune_request, result, get_rx_freq(chan), "RX");
return result;
}
@@ -920,7 +927,7 @@ public:
}
} else {
// If the daughterboard doesn't expose it's LO(s) then it can only be internal
- return std::vector<std::string> {1, "internal"};
+ return std::vector<std::string>(1, "internal");
}
}
@@ -1286,6 +1293,9 @@ public:
******************************************************************/
tx_streamer::sptr get_tx_stream(const stream_args_t &args) {
_check_link_rate(args, true);
+ if (is_device3()) {
+ return _legacy_compat->get_tx_stream(args);
+ }
return this->get_device()->get_tx_stream(args);
}
@@ -1680,6 +1690,8 @@ public:
private:
device::sptr _dev;
property_tree::sptr _tree;
+ bool _is_device3;
+ uhd::rfnoc::legacy_compat::sptr _legacy_compat;
struct mboard_chan_pair{
size_t mboard, chan;
@@ -1732,6 +1744,10 @@ private:
fs_path rx_dsp_root(const size_t chan)
{
mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ if (is_device3()) {
+ return _legacy_compat->rx_dsp_root(mcp.mboard, mcp.chan);
+ }
+
if (_tree->exists(mb_root(mcp.mboard) / "rx_chan_dsp_mapping")) {
std::vector<size_t> map = _tree->access<std::vector<size_t> >(mb_root(mcp.mboard) / "rx_chan_dsp_mapping").get();
UHD_ASSERT_THROW(map.size() > mcp.chan);
@@ -1752,6 +1768,10 @@ private:
fs_path tx_dsp_root(const size_t chan)
{
mboard_chan_pair mcp = tx_chan_to_mcp(chan);
+ if (is_device3()) {
+ return _legacy_compat->tx_dsp_root(mcp.mboard, mcp.chan);
+ }
+
if (_tree->exists(mb_root(mcp.mboard) / "tx_chan_dsp_mapping")) {
std::vector<size_t> map = _tree->access<std::vector<size_t> >(mb_root(mcp.mboard) / "tx_chan_dsp_mapping").get();
UHD_ASSERT_THROW(map.size() > mcp.chan);
@@ -1771,6 +1791,9 @@ private:
fs_path rx_fe_root(const size_t chan)
{
mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ if (is_device3()) {
+ return _legacy_compat->rx_fe_root(mcp.mboard, mcp.chan);
+ }
try
{
const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
@@ -1785,6 +1808,9 @@ private:
fs_path tx_fe_root(const size_t chan)
{
mboard_chan_pair mcp = tx_chan_to_mcp(chan);
+ if (is_device3()) {
+ return _legacy_compat->tx_fe_root(mcp.mboard, mcp.chan);
+ }
try
{
const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt
index f8b129f89..ea237b008 100644
--- a/host/lib/usrp/x300/CMakeLists.txt
+++ b/host/lib/usrp/x300/CMakeLists.txt
@@ -24,6 +24,7 @@
########################################################################
IF(ENABLE_X300)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_radio_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_uart.cpp
@@ -33,7 +34,6 @@ IF(ENABLE_X300)
${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c
)
ENDIF(ENABLE_X300)
diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp
index d49fba383..6ffd1ede4 100644
--- a/host/lib/usrp/x300/x300_dac_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2014 Ettus Research LLC
+// Copyright 2010-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
diff --git a/host/lib/usrp/x300/x300_dac_ctrl.hpp b/host/lib/usrp/x300/x300_dac_ctrl.hpp
index f2a407971..ca47a90e7 100644
--- a/host/lib/usrp/x300/x300_dac_ctrl.hpp
+++ b/host/lib/usrp/x300/x300_dac_ctrl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2014 Ettus Research LLC
+// Copyright 2010-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
diff --git a/host/lib/usrp/x300/x300_dboard_iface.cpp b/host/lib/usrp/x300/x300_dboard_iface.cpp
index 87f537874..b28768f90 100644
--- a/host/lib/usrp/x300/x300_dboard_iface.cpp
+++ b/host/lib/usrp/x300/x300_dboard_iface.cpp
@@ -15,92 +15,16 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
-#include "x300_impl.hpp"
+#include "x300_dboard_iface.hpp"
#include "x300_regs.hpp"
-#include <uhd/usrp/dboard_iface.hpp>
#include <uhd/utils/safe_call.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
-#include "ad7922_regs.hpp" //aux adc
-#include "ad5623_regs.hpp" //aux dac
using namespace uhd;
using namespace uhd::usrp;
using namespace boost::assign;
-class x300_dboard_iface : public dboard_iface
-{
-public:
- x300_dboard_iface(const x300_dboard_iface_config_t &config);
- ~x300_dboard_iface(void);
-
- special_props_t get_special_props(void)
- {
- special_props_t props;
- props.soft_clock_divider = false;
- props.mangle_i2c_addrs = (_config.dboard_slot == 1);
- return props;
- }
-
- void write_aux_dac(unit_t, aux_dac_t, double);
- double read_aux_adc(unit_t, aux_adc_t);
-
- void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
- boost::uint32_t get_pin_ctrl(unit_t unit);
- void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
- boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg);
- void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
- boost::uint32_t get_gpio_ddr(unit_t unit);
- void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
- boost::uint32_t get_gpio_out(unit_t unit);
- boost::uint32_t read_gpio(unit_t unit);
-
- void set_command_time(const uhd::time_spec_t& t);
- uhd::time_spec_t get_command_time(void);
-
- void write_i2c(boost::uint16_t, const byte_vector_t &);
- byte_vector_t read_i2c(boost::uint16_t, size_t);
-
- void set_clock_rate(unit_t, double);
- double get_clock_rate(unit_t);
- std::vector<double> get_clock_rates(unit_t);
- void set_clock_enabled(unit_t, bool);
- double get_codec_rate(unit_t);
-
- void write_spi(
- unit_t unit,
- const spi_config_t &config,
- boost::uint32_t data,
- size_t num_bits
- );
-
- boost::uint32_t read_write_spi(
- unit_t unit,
- const spi_config_t &config,
- boost::uint32_t data,
- size_t num_bits
- );
- void set_fe_connection(
- unit_t unit,
- const std::string& fe_name,
- const fe_connection_t& fe_conn
- );
-
- const x300_dboard_iface_config_t _config;
- uhd::dict<unit_t, ad5623_regs_t> _dac_regs;
- uhd::dict<unit_t, double> _clock_rates;
- void _write_aux_dac(unit_t);
-
-};
-
-/***********************************************************************
- * Make Function
- **********************************************************************/
-dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &config)
-{
- return dboard_iface::sptr(new x300_dboard_iface(config));
-}
-
/***********************************************************************
* Structors
**********************************************************************/
@@ -375,12 +299,23 @@ void x300_dboard_iface::set_command_time(const uhd::time_spec_t& t)
_config.cmd_time_ctrl->set_time(t);
}
+void x300_dboard_iface::add_rx_fe(
+ const std::string& fe_name,
+ rx_frontend_core_3000::sptr fe_core)
+{
+ _rx_fes[fe_name] = fe_core;
+}
+
void x300_dboard_iface::set_fe_connection(
- unit_t unit, const std::string& /*fe_name*/,
+ unit_t unit, const std::string& fe_name,
const fe_connection_t& fe_conn)
{
if (unit == UNIT_RX) {
- _config.rx_dsp->set_mux(fe_conn);
+ if (_rx_fes.has_key(fe_name)) {
+ _rx_fes[fe_name]->set_fe_connection(fe_conn);
+ } else {
+ throw uhd::assertion_error("front-end name was not registered: " + fe_name);
+ }
} else {
throw uhd::not_implemented_error("frontend connection not configurable for TX");
}
diff --git a/host/lib/usrp/x300/x300_dboard_iface.hpp b/host/lib/usrp/x300/x300_dboard_iface.hpp
new file mode 100644
index 000000000..124d768e8
--- /dev/null
+++ b/host/lib/usrp/x300/x300_dboard_iface.hpp
@@ -0,0 +1,115 @@
+//
+// Copyright 2010-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_X300_DBOARD_IFACE_HPP
+#define INCLUDED_X300_DBOARD_IFACE_HPP
+
+#include "x300_clock_ctrl.hpp"
+#include "spi_core_3000.hpp"
+#include "i2c_core_100_wb32.hpp"
+#include "gpio_atr_3000.hpp"
+#include "rx_frontend_core_3000.hpp"
+#include <uhd/usrp/dboard_iface.hpp>
+#include "ad7922_regs.hpp" //aux adc
+#include "ad5623_regs.hpp" //aux dac
+#include <uhd/types/dict.hpp>
+
+struct x300_dboard_iface_config_t
+{
+ uhd::usrp::gpio_atr::db_gpio_atr_3000::sptr gpio;
+ spi_core_3000::sptr spi;
+ size_t rx_spi_slaveno;
+ size_t tx_spi_slaveno;
+ uhd::i2c_iface::sptr i2c;
+ x300_clock_ctrl::sptr clock;
+ x300_clock_which_t which_rx_clk;
+ x300_clock_which_t which_tx_clk;
+ boost::uint8_t dboard_slot;
+ uhd::timed_wb_iface::sptr cmd_time_ctrl;
+};
+
+class x300_dboard_iface : public uhd::usrp::dboard_iface
+{
+public:
+ x300_dboard_iface(const x300_dboard_iface_config_t &config);
+ ~x300_dboard_iface(void);
+
+ inline special_props_t get_special_props(void)
+ {
+ special_props_t props;
+ props.soft_clock_divider = false;
+ props.mangle_i2c_addrs = (_config.dboard_slot == 1);
+ return props;
+ }
+
+ void write_aux_dac(unit_t, aux_dac_t, double);
+ double read_aux_adc(unit_t, aux_adc_t);
+
+ void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
+ boost::uint32_t get_pin_ctrl(unit_t unit);
+ void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
+ boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg);
+ void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
+ boost::uint32_t get_gpio_ddr(unit_t unit);
+ void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff);
+ boost::uint32_t get_gpio_out(unit_t unit);
+ boost::uint32_t read_gpio(unit_t unit);
+
+ void set_command_time(const uhd::time_spec_t& t);
+ uhd::time_spec_t get_command_time(void);
+
+ void write_i2c(boost::uint16_t, const uhd::byte_vector_t &);
+ uhd::byte_vector_t read_i2c(boost::uint16_t, size_t);
+
+ void set_clock_rate(unit_t, double);
+ double get_clock_rate(unit_t);
+ std::vector<double> get_clock_rates(unit_t);
+ void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
+
+ void write_spi(
+ unit_t unit,
+ const uhd::spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+ boost::uint32_t read_write_spi(
+ unit_t unit,
+ const uhd::spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+ void set_fe_connection(
+ unit_t unit, const std::string& name,
+ const uhd::usrp::fe_connection_t& fe_conn);
+
+ void add_rx_fe(
+ const std::string& fe_name,
+ rx_frontend_core_3000::sptr fe_core);
+
+private:
+ const x300_dboard_iface_config_t _config;
+ uhd::dict<unit_t, ad5623_regs_t> _dac_regs;
+ uhd::dict<unit_t, double> _clock_rates;
+ uhd::dict<std::string, rx_frontend_core_3000::sptr> _rx_fes;
+ void _write_aux_dac(unit_t);
+};
+
+
+
+#endif /* INCLUDED_X300_DBOARD_IFACE_HPP */
diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h
index 6039ee376..e05cd9ec7 100644
--- a/host/lib/usrp/x300/x300_fw_common.h
+++ b/host/lib/usrp/x300/x300_fw_common.h
@@ -33,7 +33,7 @@ extern "C" {
#define X300_REVISION_MIN 2
#define X300_FW_COMPAT_MAJOR 4
#define X300_FW_COMPAT_MINOR 0
-#define X300_FPGA_COMPAT_MAJOR 20
+#define X300_FPGA_COMPAT_MAJOR 0x20
//shared memory sections - in between the stack and the program space
#define X300_FW_SHMEM_BASE 0x6000
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index b59b920ab..ce257947e 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2015 Ettus Research LLC
+// Copyright 2013-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -47,24 +47,16 @@
using namespace uhd;
using namespace uhd::usrp;
+using namespace uhd::rfnoc;
using namespace uhd::transport;
using namespace uhd::niusrprio;
using namespace uhd::usrp::gpio_atr;
using namespace uhd::usrp::x300;
namespace asio = boost::asio;
-static bool has_dram_buff(wb_iface::sptr zpu_ctrl) {
- bool dramR0 = dma_fifo_core_3000::check(
- zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_DRAM_FIFO0), SR_ADDR(SET0_BASE, ZPU_RB_DRAM_FIFO0));
- bool dramR1 = dma_fifo_core_3000::check(
- zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_DRAM_FIFO1), SR_ADDR(SET0_BASE, ZPU_RB_DRAM_FIFO1));
- return (dramR0 and dramR1);
-}
-
static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) {
//Possible options:
//1G = {0:1G, 1:1G} w/ DRAM, HG = {0:1G, 1:10G} w/ DRAM, XG = {0:10G, 1:10G} w/ DRAM
- //1GS = {0:1G, 1:1G} w/ SRAM, HGS = {0:1G, 1:10G} w/ SRAM, XGS = {0:10G, 1:10G} w/ SRAM
//HA = {0:1G, 1:Aurora} w/ DRAM, XA = {0:10G, 1:Aurora} w/ DRAM
std::string option;
@@ -84,9 +76,6 @@ static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) {
} else {
option = "HG"; //Default
}
- if (not has_dram_buff(zpu_ctrl)) {
- option += "S";
- }
return option;
}
@@ -250,7 +239,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu
//set these values as empty string so the device may still be found
//and the filter's below can still operate on the discovered device
if (not hint.has_key("fpga")) {
- new_addr["fpga"] = "HGS";
+ new_addr["fpga"] = "HG";
}
new_addr["name"] = "";
new_addr["serial"] = "";
@@ -388,15 +377,13 @@ static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string &file_nam
UHD_MSG(status) << " done!" << std::endl;
}
-x300_impl::x300_impl(const uhd::device_addr_t &dev_addr)
+x300_impl::x300_impl(const uhd::device_addr_t &dev_addr)
+ : device3_impl()
+ , _sid_framer(0)
{
UHD_MSG(status) << "X300 initialization sequence..." << std::endl;
- _type = device::USRP;
_ignore_cal_file = dev_addr.has_key("ignore-cal-file");
- _async_md.reset(new async_md_type(1000/*messages deep*/));
- _tree = uhd::property_tree::make();
_tree->create<std::string>("/name").set("X-Series Device");
- _sid_framer = 0;
const device_addrs_t device_args = separate_device_addr(dev_addr);
_mb.resize(device_args.size());
@@ -519,6 +506,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
std::string eth0_addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"];
eth_addrs.push_back(eth0_addr);
+ mb.next_src_addr = 0; //Host source address for blocks
if (dev_addr.has_key("second_addr")) {
std::string eth1_addr = dev_addr["second_addr"];
@@ -570,6 +558,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
const boost::uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3};
mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos, 2);
+ _tree->create<size_t>(mb_path / "mtu/recv").set(X300_PCIE_RX_DATA_FRAME_SIZE);
+ _tree->create<size_t>(mb_path / "mtu/send").set(X300_PCIE_TX_DATA_FRAME_SIZE);
_tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE);
}
@@ -657,6 +647,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
<< std::endl;
}
+ _tree->create<size_t>(mb_path / "mtu/recv").set(_max_frame_sizes.recv_frame_size);
+ _tree->create<size_t>(mb_path / "mtu/send").set(_max_frame_sizes.send_frame_size);
_tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE);
}
@@ -777,15 +769,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
}
////////////////////////////////////////////////////////////////////
- // read dboard eeproms
- ////////////////////////////////////////////////////////////////////
- for (size_t i = 0; i < 8; i++)
- {
- if (i == 0 or i == 2) continue; //not used
- mb.db_eeproms[i].load(*mb.zpu_i2c, 0x50 | i);
- }
-
- ////////////////////////////////////////////////////////////////////
// read hardware revision and compatibility number
////////////////////////////////////////////////////////////////////
mb.hw_rev = 0;
@@ -847,15 +830,14 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
//Initialize clock source to use internal reference and generate
//a valid radio clock. This may change after configuration is done.
//This will configure the LMK and wait for lock
- update_clock_source(mb, "internal");
+ update_clock_source(mb, X300_DEFAULT_CLOCK_SOURCE);
////////////////////////////////////////////////////////////////////
// create clock properties
////////////////////////////////////////////////////////////////////
- _tree->create<double>(mb_path / "tick_rate")
- .set_publisher(boost::bind(&x300_clock_ctrl::get_master_clock_rate, mb.clock));
-
- _tree->create<time_spec_t>(mb_path / "time" / "cmd");
+ _tree->create<double>(mb_path / "master_clock_rate")
+ .set_publisher(boost::bind(&x300_clock_ctrl::get_master_clock_rate, mb.clock))
+ ;
UHD_MSG(status) << "Radio 1x clock:" << (mb.clock->get_master_clock_rate()/1e6)
<< std::endl;
@@ -898,78 +880,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, i), 0);
}
- ////////////////////////////////////////////////////////////////////
- // DRAM FIFO initialization
- ////////////////////////////////////////////////////////////////////
- mb.has_dram_buff = has_dram_buff(mb.zpu_ctrl);
- if (mb.has_dram_buff) {
- for (size_t i = 0; i < mboard_members_t::NUM_RADIOS; i++) {
- static const size_t NUM_REGS = 8;
- mb.dram_buff_ctrl[i] = dma_fifo_core_3000::make(
- mb.zpu_ctrl,
- SR_ADDR(SET0_BASE, ZPU_SR_DRAM_FIFO0+(i*NUM_REGS)),
- SR_ADDR(SET0_BASE, ZPU_RB_DRAM_FIFO0+i));
- mb.dram_buff_ctrl[i]->resize(X300_DRAM_FIFO_SIZE * i, X300_DRAM_FIFO_SIZE);
-
- if (mb.dram_buff_ctrl[i]->ext_bist_supported()) {
- UHD_MSG(status) << boost::format("Running BIST for DRAM FIFO %d... ") % i;
- boost::uint32_t bisterr = mb.dram_buff_ctrl[i]->run_bist();
- if (bisterr != 0) {
- throw uhd::runtime_error(str(boost::format("DRAM FIFO BIST failed! (code: %d)\n") % bisterr));
- } else {
- double throughput = mb.dram_buff_ctrl[i]->get_bist_throughput(X300_BUS_CLOCK_RATE);
- UHD_MSG(status) << (boost::format("pass (Throughput: %.1fMB/s)") % (throughput/1e6)) << std::endl;
- }
- } else {
- if (mb.dram_buff_ctrl[i]->run_bist() != 0) {
- throw uhd::runtime_error(str(boost::format("DRAM FIFO %d BIST failed!\n") % i));
- }
- }
- }
- }
-
- ////////////////////////////////////////////////////////////////////
- // setup radios
- ////////////////////////////////////////////////////////////////////
- this->setup_radio(mb_i, "A", dev_addr);
- this->setup_radio(mb_i, "B", dev_addr);
-
- ////////////////////////////////////////////////////////////////////
- // ADC test and cal
- ////////////////////////////////////////////////////////////////////
- if (dev_addr.has_key("self_cal_adc_delay")) {
- self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */);
- }
- if (dev_addr.has_key("ext_adc_self_test")) {
- extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30));
- } else if ( ! dev_addr.has_key("disable_adc_self_test") ) {
- self_test_adcs(mb);
- }
-
- ////////////////////////////////////////////////////////////////////
- // front panel gpio
- ////////////////////////////////////////////////////////////////////
- mb.fp_gpio = gpio_atr_3000::make(mb.radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO);
- BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)
- {
- _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)
- .set(0)
- .add_coerced_subscriber(boost::bind(&gpio_atr_3000::set_gpio_attr, mb.fp_gpio, attr.first, _1));
- }
- _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK")
- .set_publisher(boost::bind(&gpio_atr_3000::read_gpio, mb.fp_gpio));
-
- ////////////////////////////////////////////////////////////////////
- // register the time keepers - only one can be the highlander
- ////////////////////////////////////////////////////////////////////
- _tree->create<time_spec_t>(mb_path / "time" / "now")
- .set_publisher(boost::bind(&time_core_3000::get_time_now, mb.radio_perifs[0].time64))
- .add_coerced_subscriber(boost::bind(&x300_impl::sync_times, this, mb, _1))
- .set(0.0);
- _tree->create<time_spec_t>(mb_path / "time" / "pps")
- .set_publisher(boost::bind(&time_core_3000::get_time_last_pps, mb.radio_perifs[0].time64))
- .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[0].time64, _1))
- .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[1].time64, _1));
////////////////////////////////////////////////////////////////////
// setup time sources and properties
@@ -989,7 +899,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
// setup clock sources and properties
////////////////////////////////////////////////////////////////////
_tree->create<std::string>(mb_path / "clock_source" / "value")
- .set("internal")
+ .set(X300_DEFAULT_CLOCK_SOURCE)
.add_coerced_subscriber(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1));
static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo");
@@ -1009,21 +919,11 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
.add_coerced_subscriber(boost::bind(&x300_clock_ctrl::set_ref_out, mb.clock, _1));
//initialize tick rate (must be done before setting time)
- _tree->access<double>(mb_path / "tick_rate")
- .add_coerced_subscriber(boost::bind(&x300_impl::set_tick_rate, this, boost::ref(mb), _1))
- .add_coerced_subscriber(boost::bind(&x300_impl::update_tick_rate, this, boost::ref(mb), _1))
- .set(mb.clock->get_master_clock_rate());
-
- ////////////////////////////////////////////////////////////////////
- // create frontend mapping
- ////////////////////////////////////////////////////////////////////
- std::vector<size_t> default_map(2, 0); default_map[1] = 1;
- _tree->create<std::vector<size_t> >(mb_path / "rx_chan_dsp_mapping").set(default_map);
- _tree->create<std::vector<size_t> >(mb_path / "tx_chan_dsp_mapping").set(default_map);
- _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
- .add_coerced_subscriber(boost::bind(&x300_impl::update_subdev_spec, this, "rx", mb_i, _1));
- _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
- .add_coerced_subscriber(boost::bind(&x300_impl::update_subdev_spec, this, "tx", mb_i, _1));
+ _tree->create<double>(mb_path / "tick_rate")
+ .add_coerced_subscriber(boost::bind(&device3_impl::update_tx_streamers, this, _1))
+ .add_coerced_subscriber(boost::bind(&device3_impl::update_rx_streamers, this, _1))
+ .set(mb.clock->get_master_clock_rate())
+ ;
////////////////////////////////////////////////////////////////////
// and do the misc mboard sensors
@@ -1031,29 +931,55 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
_tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked")
.set_publisher(boost::bind(&x300_impl::get_ref_locked, this, mb));
- ////////////////////////////////////////////////////////////////////
- // do some post-init tasks
- ////////////////////////////////////////////////////////////////////
- subdev_spec_t rx_fe_spec, tx_fe_spec;
- rx_fe_spec.push_back(subdev_spec_pair_t("A",
- _tree->list(mb_path / "dboards" / "A" / "rx_frontends").at(0)));
- rx_fe_spec.push_back(subdev_spec_pair_t("B",
- _tree->list(mb_path / "dboards" / "B" / "rx_frontends").at(0)));
- tx_fe_spec.push_back(subdev_spec_pair_t("A",
- _tree->list(mb_path / "dboards" / "A" / "tx_frontends").at(0)));
- tx_fe_spec.push_back(subdev_spec_pair_t("B",
- _tree->list(mb_path / "dboards" / "B" / "tx_frontends").at(0)));
-
- _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec);
- _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec);
-
- mb.regmap_db = boost::make_shared<uhd::soft_regmap_db_t>();
- mb.regmap_db->add(*mb.fw_regmap);
- mb.regmap_db->add(*mb.radio_perifs[0].regmap);
- mb.regmap_db->add(*mb.radio_perifs[1].regmap);
-
- _tree->create<uhd::soft_regmap_accessor_t::sptr>(mb_path / "registers")
- .set(mb.regmap_db);
+ //////////////// RFNOC /////////////////
+ const size_t n_rfnoc_blocks = mb.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_NUM_CE));
+ enumerate_rfnoc_blocks(
+ mb_i,
+ n_rfnoc_blocks,
+ X300_XB_DST_PCI + 1, /* base port */
+ uhd::sid_t(X300_SRC_ADDR0, 0, X300_DST_ADDR + mb_i, 0),
+ dev_addr,
+ mb.if_pkt_is_big_endian ? ENDIANNESS_BIG : ENDIANNESS_LITTLE
+ );
+ //////////////// RFNOC /////////////////
+
+ // If we have a radio, we must configure its codec control:
+ const std::string radio_blockid_hint = str(boost::format("%d/Radio") % mb_i);
+ std::vector<rfnoc::block_id_t> radio_ids =
+ find_blocks<rfnoc::x300_radio_ctrl_impl>(radio_blockid_hint);
+ if (not radio_ids.empty()) {
+ if (radio_ids.size() > 2) {
+ UHD_MSG(warning) << "Too many Radio Blocks found. Using only the first two." << std::endl;
+ radio_ids.resize(2);
+ }
+
+ BOOST_FOREACH(const rfnoc::block_id_t &id, radio_ids) {
+ rfnoc::x300_radio_ctrl_impl::sptr radio(get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id));
+ mb.radios.push_back(radio);
+ radio->setup_radio(mb.zpu_i2c, mb.clock, dev_addr.has_key("self_cal_adc_delay"));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // ADC test and cal
+ ////////////////////////////////////////////////////////////////////
+ if (dev_addr.has_key("self_cal_adc_delay")) {
+ rfnoc::x300_radio_ctrl_impl::self_cal_adc_xfer_delay(
+ mb.radios, mb.clock,
+ boost::bind(&x300_impl::wait_for_clk_locked, this, mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, _1),
+ true /* Apply ADC delay */);
+ }
+ if (dev_addr.has_key("ext_adc_self_test")) {
+ rfnoc::x300_radio_ctrl_impl::extended_adc_test(
+ mb.radios,
+ dev_addr.cast<double>("ext_adc_self_test", 30));
+ } else if (not dev_addr.has_key("recover_mb_eeprom")){
+ for (size_t i = 0; i < mb.radios.size(); i++) {
+ mb.radios.at(i)->self_test_adc();
+ }
+ }
+ } else {
+ UHD_MSG(status) << "No Radio Block found. Assuming radio-less operation." << std::endl;
+ }
mb.initialization_done = true;
}
@@ -1064,14 +990,6 @@ x300_impl::~x300_impl(void)
{
BOOST_FOREACH(mboard_members_t &mb, _mb)
{
- //Disable/reset ADC/DAC
- mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1);
- mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0);
- mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0);
- mb.radio_perifs[0].regmap->misc_outs_reg.flush();
- mb.radio_perifs[1].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0);
- mb.radio_perifs[1].regmap->misc_outs_reg.flush();
-
//kill the claimer task and unclaim the device
mb.claimer_task.reset();
{ //Critical section
@@ -1090,268 +1008,117 @@ x300_impl::~x300_impl(void)
}
}
-void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr)
+uint32_t x300_impl::allocate_pcie_dma_chan(const uhd::sid_t &tx_sid, const xport_type_t xport_type)
{
- const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i);
- UHD_ASSERT_THROW(mb_i < _mb.size());
- mboard_members_t &mb = _mb[mb_i];
- const size_t radio_index = mb.get_radio_index(slot_name);
- radio_perifs_t &perif = mb.radio_perifs[radio_index];
-
- UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl;
-
- ////////////////////////////////////////////////////////////////////
- // radio control
- ////////////////////////////////////////////////////////////////////
- boost::uint8_t dest = (radio_index == 0)? X300_XB_DST_R0 : X300_XB_DST_R1;
- boost::uint32_t ctrl_sid;
- both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid);
- perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name);
-
- perif.regmap = boost::make_shared<radio_regmap_t>(radio_index);
- perif.regmap->initialize(*perif.ctrl, true);
-
- //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1
- if (radio_index == 0) {
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1);
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0);
- perif.regmap->misc_outs_reg.flush();
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0);
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1);
- perif.regmap->misc_outs_reg.flush();
- }
- perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1);
-
- this->register_loopback_self_test(perif.ctrl);
-
- ////////////////////////////////////////////////////////////////
- // Setup peripherals
- ////////////////////////////////////////////////////////////////
- perif.spi = spi_core_3000::make(perif.ctrl, radio::sr_addr(radio::SPI), radio::RB32_SPI);
- perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN);
- perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate());
- perif.leds = gpio_atr_3000::make_write_only(perif.ctrl, radio::sr_addr(radio::LEDS));
- perif.leds->set_atr_mode(MODE_ATR, 0xFFFFFFFF);
- perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT));
- perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE);
- perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::TX_FRONT));
- perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL));
- perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP));
- perif.ddc->set_link_rate(10e9/8); //whatever
- perif.ddc->set_tick_rate(mb.clock->get_master_clock_rate());
- //The DRAM FIFO is treated as in internal radio FIFO for flow control purposes
- tx_vita_core_3000::fc_monitor_loc fc_loc =
- mb.has_dram_buff ? tx_vita_core_3000::FC_PRE_FIFO : tx_vita_core_3000::FC_PRE_RADIO;
- perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL), fc_loc);
- perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP));
- perif.duc->set_link_rate(10e9/8); //whatever
- perif.duc->set_tick_rate(mb.clock->get_master_clock_rate());
-
- ////////////////////////////////////////////////////////////////////
- // create time control objects
- ////////////////////////////////////////////////////////////////////
- time_core_3000::readback_bases_type time64_rb_bases;
- time64_rb_bases.rb_now = radio::RB64_TIME_NOW;
- time64_rb_bases.rb_pps = radio::RB64_TIME_PPS;
- perif.time64 = time_core_3000::make(perif.ctrl, radio::sr_addr(radio::TIME), time64_rb_bases);
-
- //Capture delays are calibrated every time. The status is only printed is the user
- //asks to run the xfer self cal using "self_cal_adc_delay"
- self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay"));
-
- _tree->access<time_spec_t>(mb_path / "time" / "cmd")
- .add_coerced_subscriber(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1));
-
- ////////////////////////////////////////////////////////////////
- // create codec control objects
- ////////////////////////////////////////////////////////////////
- _tree->create<int>(mb_path / "rx_codecs" / slot_name / "gains"); //phony property so this dir exists
- _tree->create<int>(mb_path / "tx_codecs" / slot_name / "gains"); //phony property so this dir exists
- _tree->create<std::string>(mb_path / "rx_codecs" / slot_name / "name").set("ads62p48");
- _tree->create<std::string>(mb_path / "tx_codecs" / slot_name / "name").set("ad9146");
-
- _tree->create<meta_range_t>(mb_path / "rx_codecs" / slot_name / "gains" / "digital" / "range").set(meta_range_t(0, 6.0, 0.5));
- _tree->create<double>(mb_path / "rx_codecs" / slot_name / "gains" / "digital" / "value")
- .add_coerced_subscriber(boost::bind(&x300_adc_ctrl::set_gain, perif.adc, _1)).set(0);
-
- ////////////////////////////////////////////////////////////////////
- // front end corrections
- ////////////////////////////////////////////////////////////////////
- perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name));
- perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name));
-
- ////////////////////////////////////////////////////////////////////
- // connect rx dsp control objects
- ////////////////////////////////////////////////////////////////////
- const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % radio_index);
- perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path));
- _tree->access<double>(rx_dsp_path / "rate" / "value")
- .add_coerced_subscriber(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), radio_index, _1))
- ;
- _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
- .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
-
- ////////////////////////////////////////////////////////////////////
- // connect tx dsp control objects
- ////////////////////////////////////////////////////////////////////
- const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % radio_index);
- perif.duc->populate_subtree(_tree->subtree(tx_dsp_path));
- _tree->access<double>(tx_dsp_path / "rate" / "value")
- .add_coerced_subscriber(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), radio_index, _1))
- ;
-
- ////////////////////////////////////////////////////////////////////
- // create RF frontend interfacing
- ////////////////////////////////////////////////////////////////////
- const fs_path db_path = (mb_path / "dboards" / slot_name);
- const size_t j = (slot_name == "B")? 0x2 : 0x0;
- _tree->create<dboard_eeprom_t>(db_path / "rx_eeprom")
- .set(mb.db_eeproms[X300_DB0_RX_EEPROM | j])
- .add_coerced_subscriber(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_RX_EEPROM | j), _1));
- _tree->create<dboard_eeprom_t>(db_path / "tx_eeprom")
- .set(mb.db_eeproms[X300_DB0_TX_EEPROM | j])
- .add_coerced_subscriber(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_TX_EEPROM | j), _1));
- _tree->create<dboard_eeprom_t>(db_path / "gdb_eeprom")
- .set(mb.db_eeproms[X300_DB0_GDB_EEPROM | j])
- .add_coerced_subscriber(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_GDB_EEPROM | j), _1));
-
- //create a new dboard interface
- x300_dboard_iface_config_t db_config;
- db_config.gpio = db_gpio_atr_3000::make(perif.ctrl, radio::sr_addr(radio::GPIO), radio::RB32_GPIO);
- db_config.spi = perif.spi;
- db_config.rx_spi_slaveno = DB_RX_SEN;
- db_config.tx_spi_slaveno = DB_TX_SEN;
- db_config.i2c = mb.zpu_i2c;
- db_config.clock = mb.clock;
- db_config.rx_dsp = mb.radio_perifs[radio_index].ddc;
- db_config.which_rx_clk = (slot_name == "A")? X300_CLOCK_WHICH_DB0_RX : X300_CLOCK_WHICH_DB1_RX;
- db_config.which_tx_clk = (slot_name == "A")? X300_CLOCK_WHICH_DB0_TX : X300_CLOCK_WHICH_DB1_TX;
- db_config.dboard_slot = (slot_name == "A")? 0 : 1;
- db_config.cmd_time_ctrl = perif.ctrl;
-
- //create a new dboard manager
- _dboard_managers[db_path] = dboard_manager::make(
- mb.db_eeproms[X300_DB0_RX_EEPROM | j].id,
- mb.db_eeproms[X300_DB0_TX_EEPROM | j].id,
- mb.db_eeproms[X300_DB0_GDB_EEPROM | j].id,
- x300_make_dboard_iface(db_config),
- _tree->subtree(db_path)
- );
-
- //now that dboard is created -- register into rx antenna event
- const std::string fe_name = _tree->list(db_path / "rx_frontends").front();
- _tree->access<std::string>(db_path / "rx_frontends" / fe_name / "antenna" / "value")
- .add_coerced_subscriber(boost::bind(&x300_impl::update_atr_leds, this, mb.radio_perifs[radio_index].leds, _1));
- this->update_atr_leds(mb.radio_perifs[radio_index].leds, ""); //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";
- BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)) {
- _tree->access<double>(db_tx_fe_path / name / "freq" / "value")
- .add_coerced_subscriber(boost::bind(&x300_impl::set_tx_fe_corrections, this, mb_path, slot_name, _1));
- }
- const fs_path db_rx_fe_path = db_path / "rx_frontends";
- BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) {
- _tree->access<double>(db_rx_fe_path / name / "freq" / "value")
- .add_coerced_subscriber(boost::bind(&x300_impl::set_rx_fe_corrections, this, mb_path, slot_name, _1));
- }
-}
+ static const uint32_t CTRL_CHANNEL = 0;
+ static const uint32_t FIRST_DATA_CHANNEL = 1;
+ if (xport_type == CTRL) {
+ return CTRL_CHANNEL;
+ } else {
+ // sid_t has no comparison defined
+ uint32_t raw_sid = tx_sid.get();
-void x300_impl::set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq)
-{
- if(not _ignore_cal_file){
- apply_rx_fe_corrections(this->get_tree()->subtree(mb_path), fe_name, lo_freq);
- }
-}
+ if (_dma_chan_pool.count(raw_sid) == 0) {
+ _dma_chan_pool[raw_sid] = _dma_chan_pool.size() + FIRST_DATA_CHANNEL;
+ UHD_MSG(status) << "[X300] Assigning PCIe DMA channel " << _dma_chan_pool[raw_sid]
+ << " to SID " << tx_sid.to_pp_string_hex() << std::endl;
+ }
-void x300_impl::set_tx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq)
-{
- if(not _ignore_cal_file){
- apply_tx_fe_corrections(this->get_tree()->subtree(mb_path), fe_name, lo_freq);
+ if (_dma_chan_pool.size() + FIRST_DATA_CHANNEL > X300_PCIE_MAX_CHANNELS) {
+ throw uhd::runtime_error("Trying to allocate more DMA channels than are available");
+ }
+ return _dma_chan_pool[raw_sid];
}
}
-boost::uint32_t get_pcie_dma_channel(boost::uint8_t destination, boost::uint8_t prefix)
-{
- static const boost::uint32_t RADIO_GRP_SIZE = 3;
- static const boost::uint32_t RADIO0_GRP = 0;
- static const boost::uint32_t RADIO1_GRP = 1;
-
- boost::uint32_t radio_grp = (destination == X300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP;
- return ((radio_grp * RADIO_GRP_SIZE) + prefix);
+static boost::uint32_t extract_sid_from_pkt(void* pkt, size_t) {
+ return uhd::sid_t(uhd::wtohx(static_cast<const boost::uint32_t*>(pkt)[1])).get_dst();
}
-x300_impl::both_xports_t x300_impl::make_transport(
- const size_t mb_index,
- const boost::uint8_t& destination,
- const boost::uint8_t& prefix,
- const uhd::device_addr_t& args,
- boost::uint32_t& sid)
-{
+uhd::both_xports_t x300_impl::make_transport(
+ const uhd::sid_t &address,
+ const xport_type_t xport_type,
+ const uhd::device_addr_t& args
+) {
+ const size_t mb_index = address.get_dst_addr() - X300_DST_ADDR;
mboard_members_t &mb = _mb[mb_index];
- both_xports_t xports;
-
- sid_config_t config;
- config.router_addr_there = X300_DEVICE_THERE;
- config.dst_prefix = prefix;
- config.router_dst_there = destination;
-
- // Choose the endpoint based on the destination
- size_t endpoint = 0;
- if (destination == X300_XB_DST_R1) {
- if (mb.eth_conns.size() > 1)
- endpoint = 1;
- }
-
- // Decide on the IP/Interface pair based on the endpoint index
- std::string interface_addr = mb.eth_conns[endpoint].addr;
- config.iface_index = mb.eth_conns[endpoint].type;
-
- sid = this->allocate_sid(mb, config);
- static const uhd::device_addr_t DEFAULT_XPORT_ARGS;
-
- const uhd::device_addr_t& xport_args =
- (prefix != X300_RADIO_DEST_PREFIX_CTRL) ? args : DEFAULT_XPORT_ARGS;
-
+ const uhd::device_addr_t& xport_args = (xport_type == CTRL) ? uhd::device_addr_t() : args;
zero_copy_xport_params default_buff_args;
+ both_xports_t xports;
if (mb.xport_path == "nirio") {
- default_buff_args.send_frame_size =
- (prefix == X300_RADIO_DEST_PREFIX_TX)
- ? X300_PCIE_TX_DATA_FRAME_SIZE
- : X300_PCIE_MSG_FRAME_SIZE;
-
- default_buff_args.recv_frame_size =
- (prefix == X300_RADIO_DEST_PREFIX_RX)
- ? X300_PCIE_RX_DATA_FRAME_SIZE
- : X300_PCIE_MSG_FRAME_SIZE;
-
- default_buff_args.num_send_frames =
- (prefix == X300_RADIO_DEST_PREFIX_TX)
- ? X300_PCIE_DATA_NUM_FRAMES
- : X300_PCIE_MSG_NUM_FRAMES;
-
- default_buff_args.num_recv_frames =
- (prefix == X300_RADIO_DEST_PREFIX_RX)
- ? X300_PCIE_DATA_NUM_FRAMES
- : X300_PCIE_MSG_NUM_FRAMES;
-
- xports.recv = nirio_zero_copy::make(
- mb.rio_fpga_interface,
- get_pcie_dma_channel(destination, prefix),
- default_buff_args,
- xport_args);
+ xports.send_sid = this->allocate_sid(mb, address, X300_SRC_ADDR0, X300_XB_DST_PCI);
+ xports.recv_sid = xports.send_sid.reversed();
+
+ uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, xport_type);
+ if (xport_type == CTRL) {
+ //Transport for control stream
+ if (_ctrl_dma_xport.get() == NULL) {
+ //One underlying DMA channel will handle
+ //all control traffic
+ zero_copy_xport_params ctrl_buff_args;
+ ctrl_buff_args.send_frame_size = X300_PCIE_MSG_FRAME_SIZE;
+ ctrl_buff_args.recv_frame_size = X300_PCIE_MSG_FRAME_SIZE;
+ ctrl_buff_args.num_send_frames = X300_PCIE_MSG_NUM_FRAMES * X300_PCIE_MAX_MUXED_XPORTS;
+ ctrl_buff_args.num_recv_frames = X300_PCIE_MSG_NUM_FRAMES * X300_PCIE_MAX_MUXED_XPORTS;
+
+ zero_copy_if::sptr base_xport = nirio_zero_copy::make(
+ mb.rio_fpga_interface, dma_channel_num,
+ ctrl_buff_args, uhd::device_addr_t());
+ _ctrl_dma_xport = muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, X300_PCIE_MAX_MUXED_XPORTS);
+ }
+ //Create a virtual control transport
+ xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst());
+ } else {
+ //Transport for data stream
+ default_buff_args.send_frame_size =
+ (xport_type == TX_DATA)
+ ? X300_PCIE_TX_DATA_FRAME_SIZE
+ : X300_PCIE_MSG_FRAME_SIZE;
+
+ default_buff_args.recv_frame_size =
+ (xport_type == RX_DATA)
+ ? X300_PCIE_RX_DATA_FRAME_SIZE
+ : X300_PCIE_MSG_FRAME_SIZE;
+
+ default_buff_args.num_send_frames =
+ (xport_type == TX_DATA)
+ ? X300_PCIE_DATA_NUM_FRAMES
+ : X300_PCIE_MSG_NUM_FRAMES;
+
+ default_buff_args.num_recv_frames =
+ (xport_type == RX_DATA)
+ ? X300_PCIE_DATA_NUM_FRAMES
+ : X300_PCIE_MSG_NUM_FRAMES;
+
+ xports.recv = nirio_zero_copy::make(
+ mb.rio_fpga_interface, dma_channel_num,
+ default_buff_args, xport_args);
+ }
xports.send = xports.recv;
+ // Router config word is:
+ // - Upper 16 bits: Destination address (e.g. 0.0)
+ // - Lower 16 bits: DMA channel
+ uint32_t router_config_word = (xports.recv_sid.get_dst() << 16) | dma_channel_num;
+ mb.rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word);
+
//For the nirio transport, buffer size is depends on the frame size and num frames
xports.recv_buff_size = xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size();
xports.send_buff_size = xports.send->get_num_send_frames() * xports.send->get_send_frame_size();
} else if (mb.xport_path == "eth") {
+ // Decide on the IP/Interface pair based on the endpoint index
+ std::string interface_addr = mb.eth_conns[mb.next_src_addr].addr;
+ const uint32_t xbar_src_addr =
+ mb.next_src_addr==0 ? X300_SRC_ADDR0 : X300_SRC_ADDR1;
+ const uint32_t xbar_src_dst =
+ mb.eth_conns[mb.next_src_addr].type==X300_IFACE_ETH0 ? X300_XB_DST_E0 : X300_XB_DST_E1;
+ mb.next_src_addr = (mb.next_src_addr + 1) % mb.eth_conns.size();
+
+ xports.send_sid = this->allocate_sid(mb, address, xbar_src_addr, xbar_src_dst);
+ xports.recv_sid = xports.send_sid.reversed();
/* Determine what the recommended frame size is for this
* connection type.*/
@@ -1359,24 +1126,22 @@ x300_impl::both_xports_t x300_impl::make_transport(
fs_path mboard_path = fs_path("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate");
- UHD_ASSERT_THROW(mb.loaded_fpga_image.size() >= 2);
-
- if (mb.loaded_fpga_image.substr(0,2) == "HG") {
+ if (mb.loaded_fpga_image == "HG") {
size_t max_link_rate = 0;
- if (config.iface_index == X300_IFACE_ETH0) {
+ if (xbar_src_dst == X300_XB_DST_E0) {
eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE;
max_link_rate += X300_MAX_RATE_1GIGE;
- } else if (config.iface_index == X300_IFACE_ETH1) {
+ } else if (xbar_src_dst == X300_XB_DST_E1) {
eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE;
max_link_rate += X300_MAX_RATE_10GIGE;
}
_tree->access<double>(mboard_path).set(max_link_rate);
- } else if (mb.loaded_fpga_image.substr(0,2) == "XG" or mb.loaded_fpga_image.substr(0,2) == "XA") {
+ } else if (mb.loaded_fpga_image == "XG" or mb.loaded_fpga_image == "XA") {
eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE;
size_t max_link_rate = X300_MAX_RATE_10GIGE;
max_link_rate *= mb.eth_conns.size();
_tree->access<double>(mboard_path).set(max_link_rate);
- } else if (mb.loaded_fpga_image.substr(0,2) == "HA") {
+ } else if (mb.loaded_fpga_image == "HA") {
eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE;
size_t max_link_rate = X300_MAX_RATE_1GIGE;
max_link_rate *= mb.eth_conns.size();
@@ -1414,22 +1179,22 @@ x300_impl::both_xports_t x300_impl::make_transport(
// Make sure frame sizes do not exceed the max available value supported by UHD
default_buff_args.send_frame_size =
- (prefix == X300_RADIO_DEST_PREFIX_TX)
+ (xport_type == TX_DATA)
? std::min(system_max_send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE)
: std::min(system_max_send_frame_size, X300_ETH_MSG_FRAME_SIZE);
default_buff_args.recv_frame_size =
- (prefix == X300_RADIO_DEST_PREFIX_RX)
+ (xport_type == RX_DATA)
? std::min(system_max_recv_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE)
: std::min(system_max_recv_frame_size, X300_ETH_MSG_FRAME_SIZE);
default_buff_args.num_send_frames =
- (prefix == X300_RADIO_DEST_PREFIX_TX)
+ (xport_type == TX_DATA)
? X300_ETH_DATA_NUM_FRAMES
: X300_ETH_MSG_NUM_FRAMES;
default_buff_args.num_recv_frames =
- (prefix == X300_RADIO_DEST_PREFIX_RX)
+ (xport_type == RX_DATA)
? X300_ETH_DATA_NUM_FRAMES
: X300_ETH_MSG_NUM_FRAMES;
@@ -1445,7 +1210,7 @@ x300_impl::both_xports_t x300_impl::make_transport(
// Create a threaded transport for the receive chain only
// Note that this shouldn't affect PCIe
- if (prefix == X300_RADIO_DEST_PREFIX_RX) {
+ if (xport_type == RX_DATA) {
xports.recv = zero_copy_recv_offload::make(
xports.recv,
X300_THREAD_BUFFER_TIMEOUT
@@ -1465,12 +1230,12 @@ x300_impl::both_xports_t x300_impl::make_transport(
//send a mini packet with SID into the ZPU
//ZPU will reprogram the ethernet framer
UHD_LOG << "programming packet for new xport on "
- << mb.get_pri_eth().addr << std::hex << "sid 0x" << sid << std::dec << std::endl;
+ << interface_addr << " sid " << xports.send_sid << std::endl;
//YES, get a __send__ buffer from the __recv__ socket
//-- this is the only way to program the framer for recv:
managed_send_buffer::sptr buff = xports.recv->get_send_buff();
buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0
- buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid);
+ buff->cast<boost::uint32_t *>()[1] = uhd::htonx(xports.send_sid.get());
buff->commit(8);
buff.reset();
@@ -1487,60 +1252,27 @@ x300_impl::both_xports_t x300_impl::make_transport(
}
-boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t &config)
-{
- const std::string &xport_path = mb.xport_path;
- const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff;
- boost::uint8_t sid_ret_addr = X300_SRC_ADDR_ETH0;
- boost::uint32_t xb_port = X300_XB_DST_E0;
-
- // Use the interface index to decide on the ethernet port
- if (config.iface_index == X300_IFACE_ETH1) {
- sid_ret_addr = X300_SRC_ADDR_ETH1;
- xb_port = X300_XB_DST_E1;
- }
-
- // Ensure we choose the right port in the case of NI-RIO
- if (xport_path == "nirio") {
- xb_port = X300_XB_DST_PCI;
- }
-
- int int_sid_ret_addr = int(sid_ret_addr);
-
- const boost::uint32_t sid = 0
- | (sid_ret_addr << 24)
- | (_sid_framer << 16)
- | (config.router_addr_there << 8)
- | (stream << 0)
- ;
-
- UHD_LOG << std::hex
- << " sid 0x" << sid
- << " return address 0x" << int_sid_ret_addr
- << " framer 0x" << _sid_framer
- << " stream 0x" << stream
- << " router_dst_there 0x" << int(config.router_dst_there)
- << " router_addr_there 0x" << int(config.router_addr_there)
- << std::dec << std::endl;
+uhd::sid_t x300_impl::allocate_sid(
+ mboard_members_t &mb,
+ const uhd::sid_t &address,
+ const uint32_t src_addr,
+ const uint32_t src_dst
+) {
+ uhd::sid_t sid = address;
+ sid.set_src_addr(src_addr);
+ sid.set_src_endpoint(_sid_framer);
+ // TODO Move all of this setup_mb()
// Program the X300 to recognise it's own local address.
- mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), config.router_addr_there);
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), address.get_dst_addr());
// Program CAM entry for outgoing packets matching a X300 resource (for example a Radio)
- // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM
- mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + (stream)), config.router_dst_there);
+ // This type of packet matches the XB_LOCAL address and is looked up in the upper half of the CAM
+ mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + address.get_dst_endpoint()), address.get_dst_xbarport());
// Program CAM entry for returning packets to us (for example GR host via Eth0)
// This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM
- mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + (sid_ret_addr)), xb_port);
-
- if (xport_path == "nirio") {
- boost::uint32_t router_config_word = ((_sid_framer & 0xff) << 16) | //Return SID
- get_pcie_dma_channel(config.router_dst_there, config.dst_prefix); //Dest
- mb.rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word);
- }
+ mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + src_addr), src_dst);
- UHD_LOG << std::hex
- << "done router config for sid 0x" << sid
- << std::dec << std::endl;
+ UHD_LOG << "done router config for sid " << sid << std::endl;
//increment for next setup
_sid_framer++;
@@ -1548,56 +1280,9 @@ boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t
return sid;
}
-void x300_impl::update_atr_leds(gpio_atr_3000::sptr leds, const std::string &rx_ant)
-{
- const bool is_txrx = (rx_ant == "TX/RX");
- const int rx_led = (1 << 2);
- const int tx_led = (1 << 1);
- const int txrx_led = (1 << 0);
- leds->set_atr_reg(ATR_REG_IDLE, 0);
- leds->set_atr_reg(ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led);
- leds->set_atr_reg(ATR_REG_TX_ONLY, tx_led);
- leds->set_atr_reg(ATR_REG_FULL_DUPLEX, rx_led | tx_led);
-}
-
-void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate)
-{
- BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) {
- perif.ctrl->set_tick_rate(rate);
- perif.time64->set_tick_rate(rate);
- perif.framer->set_tick_rate(rate);
- perif.ddc->set_tick_rate(rate);
- perif.duc->set_tick_rate(rate);
- }
-}
-
-void x300_impl::register_loopback_self_test(wb_iface::sptr iface)
-{
- bool test_fail = false;
- UHD_MSG(status) << "Performing register loopback test... " << std::flush;
- size_t hash = size_t(time(NULL));
- for (size_t i = 0; i < 100; i++)
- {
- boost::hash_combine(hash, i);
- iface->poke32(radio::sr_addr(radio::TEST), boost::uint32_t(hash));
- test_fail = iface->peek32(radio::RB32_TEST) != boost::uint32_t(hash);
- if (test_fail) break; //exit loop on any failure
- }
- UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
-}
-
-void x300_impl::radio_loopback(wb_iface::sptr iface, const bool on)
-{
- iface->poke32(radio::sr_addr(radio::LOOPBACK), (on ? 0x1 : 0x0));
- UHD_MSG(status) << ((on)? "Radio Loopback On" : "Radio Loopback Off") << std::endl;
-}
-
-
-
/***********************************************************************
* clock and time control logic
**********************************************************************/
-
void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb)
{
mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb?1:0);
@@ -1670,18 +1355,8 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou
}
// Reset ADCs and DACs
- for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
- radio_perifs_t &perif = mb.radio_perifs[r];
- if (perif.regmap && r==0) { //ADC/DAC reset lines only exist in Radio0
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1);
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0);
- perif.regmap->misc_outs_reg.flush();
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0);
- perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1);
- perif.regmap->misc_outs_reg.flush();
- }
- if (perif.adc) perif.adc->reset();
- if (perif.dac) perif.dac->reset();
+ BOOST_FOREACH(rfnoc::x300_radio_ctrl_impl::sptr r, mb.radios) {
+ r->reset_codec();
}
}
@@ -1711,8 +1386,12 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour
void x300_impl::sync_times(mboard_members_t &mb, const uhd::time_spec_t& t)
{
- BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs)
- perif.time64->set_time_sync(t);
+ std::vector<rfnoc::block_id_t> radio_ids = find_blocks<rfnoc::x300_radio_ctrl_impl>("Radio");
+ BOOST_FOREACH(const rfnoc::block_id_t &id, radio_ids) {
+ get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id)->set_time_sync(t);
+ }
+
+ mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0);
mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 1);
mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0);
}
@@ -1911,7 +1590,10 @@ void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t
: "resource")
% members.get_pri_eth().addr);
- throw uhd::runtime_error(str(boost::format(
+ std::cout << "=========================================================" << std::endl;
+ std::cout << "Warning:" << std::endl;
+ //throw uhd::runtime_error(str(boost::format(
+ std::cout << (str(boost::format(
"Expected FPGA compatibility number %d, but got %d:\n"
"The FPGA image on your device is not compatible with this host code build.\n"
"Download the appropriate FPGA images for this version of UHD.\n"
@@ -1923,6 +1605,7 @@ void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t
) % int(X300_FPGA_COMPAT_MAJOR) % compat_major
% print_utility_error("uhd_images_downloader.py")
% image_loader_cmd));
+ std::cout << "=========================================================" << std::endl;
}
_tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")
% compat_major % compat_minor));
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index b5bbbe136..d491e2bc0 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -19,55 +19,36 @@
#define INCLUDED_X300_IMPL_HPP
#include <uhd/property_tree.hpp>
-#include <uhd/device.hpp>
+#include "../device3/device3_impl.hpp"
#include <uhd/usrp/mboard_eeprom.hpp>
-#include <uhd/usrp/dboard_manager.hpp>
-#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/types/sensors.hpp>
+#include "x300_radio_ctrl_impl.hpp"
#include "x300_clock_ctrl.hpp"
#include "x300_fw_common.h"
#include <uhd/transport/udp_simple.hpp> //mtu
-#include <uhd/utils/tasks.hpp>
-#include "spi_core_3000.hpp"
-#include "x300_adc_ctrl.hpp"
-#include "x300_dac_ctrl.hpp"
-#include "rx_vita_core_3000.hpp"
-#include "tx_vita_core_3000.hpp"
-#include "time_core_3000.hpp"
-#include "rx_dsp_core_3000.hpp"
-#include "tx_dsp_core_3000.hpp"
#include "i2c_core_100_wb32.hpp"
-#include "radio_ctrl_core_3000.hpp"
-#include "rx_frontend_core_200.hpp"
-#include "tx_frontend_core_200.hpp"
-#include "gpio_atr_3000.hpp"
-#include "dma_fifo_core_3000.hpp"
#include <boost/weak_ptr.hpp>
#include <uhd/usrp/gps_ctrl.hpp>
-#include <uhd/usrp/mboard_eeprom.hpp>
-#include <uhd/transport/bounded_buffer.hpp>
#include <uhd/transport/nirio/niusrprio_session.h>
#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/transport/muxed_zero_copy_if.hpp>
#include "recv_packet_demuxer_3000.hpp"
#include "x300_regs.hpp"
+///////////// RFNOC /////////////////////
+#include <uhd/rfnoc/block_ctrl.hpp>
+///////////// RFNOC /////////////////////
+#include <boost/dynamic_bitset.hpp>
static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin";
+static const std::string X300_DEFAULT_CLOCK_SOURCE = "internal";
static const double X300_DEFAULT_TICK_RATE = 200e6; //Hz
static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6; //Hz
static const double X300_BUS_CLOCK_RATE = 166.666667e6; //Hz
-static const size_t X300_TX_HW_BUFF_SIZE_SRAM = 520*1024; //512K SRAM buffer + 8K 2Clk FIFO
-static const size_t X300_TX_FC_RESPONSE_FREQ_SRAM = 8; //per flow-control window
-static const size_t X300_TX_HW_BUFF_SIZE_DRAM = 128*1024;
-static const size_t X300_TX_FC_RESPONSE_FREQ_DRAM = 32;
-static const boost::uint32_t X300_DRAM_FIFO_SIZE = 32*1024*1024;
-
static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000;//32MiB For an ~8k frame size any size >32MiB is just wasted buffer space
static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib
-static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full.
-static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window
//The FIFO closest to the DMA controller is 1023 elements deep for RX and 1029 elements deep for TX
//where an element is 8 bytes. For best throughput ensure that the data frame fits in these buffers.
@@ -77,6 +58,8 @@ static const size_t X300_PCIE_TX_DATA_FRAME_SIZE = 8192; //bytes
static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048;
static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes
static const size_t X300_PCIE_MSG_NUM_FRAMES = 64;
+static const size_t X300_PCIE_MAX_CHANNELS = 6;
+static const size_t X300_PCIE_MAX_MUXED_XPORTS = 32;
static const size_t X300_10GE_DATA_FRAME_MAX_SIZE = 8000; //bytes
static const size_t X300_1GE_DATA_FRAME_MAX_SIZE = 1472; //bytes
@@ -88,46 +71,22 @@ static const size_t X300_ETH_MSG_NUM_FRAMES = 64;
static const size_t X300_ETH_DATA_NUM_FRAMES = 32;
static const double X300_DEFAULT_SYSREF_RATE = 10e6;
-static const size_t X300_TX_MAX_HDR_LEN = // bytes
- sizeof(boost::uint32_t) // Header
- + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID
- + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp
-static const size_t X300_RX_MAX_HDR_LEN = // bytes
- sizeof(boost::uint32_t) // Header
- + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID
- + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp
-
static const size_t X300_MAX_RATE_PCIE = 800000000; // bytes/s
static const size_t X300_MAX_RATE_10GIGE = 800000000; // bytes/s
static const size_t X300_MAX_RATE_1GIGE = 100000000; // bytes/s
#define X300_RADIO_DEST_PREFIX_TX 0
-#define X300_RADIO_DEST_PREFIX_CTRL 1
-#define X300_RADIO_DEST_PREFIX_RX 2
-
-#define X300_XB_DST_E0 0
-#define X300_XB_DST_E1 1
-#define X300_XB_DST_R0 2 // Radio 0 -> Slot A
-#define X300_XB_DST_R1 3 // Radio 1 -> Slot B
-#define X300_XB_DST_CE0 4
-#define X300_XB_DST_CE1 5
-#define X300_XB_DST_CE2 5
-#define X300_XB_DST_PCI 7
-
-#define X300_DEVICE_THERE 2
-#define X300_SRC_ADDR_ETH0 0
-#define X300_SRC_ADDR_ETH1 1
-
-//eeprom addrs for various boards
-enum
-{
- X300_DB0_RX_EEPROM = 0x5,
- X300_DB0_TX_EEPROM = 0x4,
- X300_DB0_GDB_EEPROM = 0x1,
- X300_DB1_RX_EEPROM = 0x7,
- X300_DB1_TX_EEPROM = 0x6,
- X300_DB1_GDB_EEPROM = 0x3,
-};
+
+#define X300_XB_DST_E0 0
+#define X300_XB_DST_E1 1
+#define X300_XB_DST_PCI 2
+#define X300_XB_DST_R0 3 // Radio 0 -> Slot A
+#define X300_XB_DST_R1 4 // Radio 1 -> Slot B
+#define X300_XB_DST_CE0 5
+
+#define X300_SRC_ADDR0 0
+#define X300_SRC_ADDR1 1
+#define X300_DST_ADDR 2
// Ethernet ports
enum x300_eth_iface_t
@@ -143,22 +102,7 @@ struct x300_eth_conn_t
x300_eth_iface_t type;
};
-struct x300_dboard_iface_config_t
-{
- uhd::usrp::gpio_atr::db_gpio_atr_3000::sptr gpio;
- spi_core_3000::sptr spi;
- size_t rx_spi_slaveno;
- size_t tx_spi_slaveno;
- i2c_core_100_wb32::sptr i2c;
- x300_clock_ctrl::sptr clock;
- rx_dsp_core_3000::sptr rx_dsp;
- x300_clock_which_t which_rx_clk;
- x300_clock_which_t which_tx_clk;
- boost::uint8_t dboard_slot;
- uhd::timed_wb_iface::sptr cmd_time_ctrl;
-};
-uhd::usrp::dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &);
uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true);
@@ -166,22 +110,14 @@ uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr
uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_);
-class x300_impl : public uhd::device
+class x300_impl : public uhd::usrp::device3_impl
{
public:
- typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
x300_impl(const uhd::device_addr_t &);
void setup_mb(const size_t which, const uhd::device_addr_t &);
~x300_impl(void);
- //the io interface
- uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &);
- uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &);
-
- //support old async call
- bool recv_async_msg(uhd::async_metadata_t &, double);
-
// used by x300_find_with_addr to find X300 devices.
static boost::mutex claimer_mutex; //All claims and checks in this process are serialized
static bool is_claimed(uhd::wb_iface::sptr);
@@ -192,43 +128,26 @@ public:
static x300_mboard_t get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port);
static x300_mboard_t get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom);
-private:
- boost::shared_ptr<async_md_type> _async_md;
+protected:
+ void subdev_to_blockid(
+ const uhd::usrp::subdev_spec_pair_t &spec, const size_t mb_i,
+ uhd::rfnoc::block_id_t &block_id, uhd::device_addr_t &block_args
+ );
+ uhd::usrp::subdev_spec_pair_t blockid_to_subdev(
+ const uhd::rfnoc::block_id_t &blockid, const uhd::device_addr_t &block_args
+ );
- //perifs in the radio core
- struct radio_perifs_t
- {
- //Interfaces
- radio_ctrl_core_3000::sptr ctrl;
- spi_core_3000::sptr spi;
- x300_adc_ctrl::sptr adc;
- x300_dac_ctrl::sptr dac;
- time_core_3000::sptr time64;
- rx_vita_core_3000::sptr framer;
- rx_dsp_core_3000::sptr ddc;
- tx_vita_core_3000::sptr deframer;
- tx_dsp_core_3000::sptr duc;
- uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds;
- rx_frontend_core_200::sptr rx_fe;
- tx_frontend_core_200::sptr tx_fe;
- //Registers
- uhd::usrp::x300::radio_regmap_t::sptr regmap;
- };
-
- //overflow recovery impl
- void handle_overflow(radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer);
+private:
//vector of member objects per motherboard
struct mboard_members_t
{
- uhd::dict<size_t, boost::weak_ptr<uhd::rx_streamer> > rx_streamers;
- uhd::dict<size_t, boost::weak_ptr<uhd::tx_streamer> > tx_streamers;
-
bool initialization_done;
uhd::task::sptr claimer_task;
std::string xport_path;
std::vector<x300_eth_conn_t> eth_conns;
+ size_t next_src_addr;
// Discover the ethernet connections per motherboard
void discover_eth(const uhd::usrp::mboard_eeprom_t mb_eeprom,
@@ -250,24 +169,9 @@ private:
spi_core_3000::sptr zpu_spi;
i2c_core_100_wb32::sptr zpu_i2c;
- //perifs in each radio
- static const size_t NUM_RADIOS = 2;
- radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B
- uhd::usrp::dboard_eeprom_t db_eeproms[8];
- //! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs
- size_t get_radio_index(const std::string &slot_name) {
- UHD_ASSERT_THROW(slot_name == "A" or slot_name == "B");
- return slot_name == "A" ? 0 : 1;
- }
-
- bool has_dram_buff;
- dma_fifo_core_3000::sptr dram_buff_ctrl[NUM_RADIOS];
-
-
//other perifs on mboard
x300_clock_ctrl::sptr clock;
uhd::gps_ctrl::sptr gps;
- uhd::usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio;
uhd::usrp::x300::fw_regmap_t::sptr fw_regmap;
@@ -277,57 +181,25 @@ private:
size_t hw_rev;
std::string current_refclk_src;
- uhd::soft_regmap_db_t::sptr regmap_db;
+ std::vector<uhd::rfnoc::x300_radio_ctrl_impl::sptr> radios;
};
std::vector<mboard_members_t> _mb;
//task for periodically reclaiming the device from others
void claimer_loop(uhd::wb_iface::sptr);
- boost::mutex _transport_setup_mutex;
-
- void register_loopback_self_test(uhd::wb_iface::sptr iface);
-
- void radio_loopback(uhd::wb_iface::sptr iface, const bool on);
-
- /*! \brief Initialize the radio component on a given slot.
- *
- * Call this function once per slot (A and B) and motherboard to initialize all the radio components.
- * This will:
- * - Reset and init DACs and ADCs
- * - Setup controls for DAC, ADC, SPI and LEDs
- * - Self test ADC
- * - Sync DACs (for MIMO)
- * - Initialize the property tree for control objects etc. (gain, rate...)
- *
- * \param mb_i Motherboard index
- * \param slot_name Slot name (A or B).
- */
- void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr);
-
size_t _sid_framer;
- struct sid_config_t
- {
- boost::uint8_t router_addr_there;
- boost::uint8_t dst_prefix; //2bits
- boost::uint8_t router_dst_there;
- x300_eth_iface_t iface_index;
- };
- boost::uint32_t allocate_sid(mboard_members_t &mb, const sid_config_t &config);
- struct both_xports_t
- {
- uhd::transport::zero_copy_if::sptr recv;
- uhd::transport::zero_copy_if::sptr send;
- size_t recv_buff_size;
- size_t send_buff_size;
- };
- both_xports_t make_transport(
- const size_t mb_index,
- const boost::uint8_t& destination,
- const boost::uint8_t& prefix,
- const uhd::device_addr_t& args,
- boost::uint32_t& sid);
+ uhd::sid_t allocate_sid(
+ mboard_members_t &mb,
+ const uhd::sid_t &address,
+ const uint32_t src_addr,
+ const uint32_t src_dst);
+ uhd::both_xports_t make_transport(
+ const uhd::sid_t &address,
+ const xport_type_t xport_type,
+ const uhd::device_addr_t& args
+ );
struct frame_size_t
{
@@ -343,6 +215,15 @@ private:
*/
frame_size_t determine_max_frame_size(const std::string &addr, const frame_size_t &user_mtu);
+ std::map<uint32_t, uint32_t> _dma_chan_pool;
+ uhd::transport::muxed_zero_copy_if::sptr _ctrl_dma_xport;
+
+ /*! Allocate or return a previously allocated PCIe channel pair
+ *
+ * Note the SID is always the transmit SID (i.e. from host to device).
+ */
+ uint32_t allocate_pcie_dma_chan(const uhd::sid_t &tx_sid, const xport_type_t xport_type);
+
////////////////////////////////////////////////////////////////////
//
//Caching for transport interface re-use -- like sharing a DMA.
@@ -366,26 +247,8 @@ private:
uhd::dict<std::string, uhd::usrp::dboard_manager::sptr> _dboard_managers;
- void set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq);
- void set_tx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq);
bool _ignore_cal_file;
-
- /*! Update the IQ MUX settings for the radio peripheral according to given subdev spec.
- *
- * Also checks if the given subdev is valid for this device and updates the channel to DSP mapping.
- *
- * \param tx_rx "tx" or "rx", depending where you're setting the subdev spec
- * \param mb_i Mainboard index number.
- * \param spec Subdev spec
- */
- void update_subdev_spec(const std::string &tx_rx, const size_t mb_i, const uhd::usrp::subdev_spec_t &spec);
-
- void set_tick_rate(mboard_members_t &, const double);
- void update_tick_rate(mboard_members_t &, const double);
- void update_rx_samp_rate(mboard_members_t&, const size_t, const double);
- void update_tx_samp_rate(mboard_members_t&, const size_t, const double);
-
void update_clock_control(mboard_members_t&);
void initialize_clock_control(mboard_members_t &mb);
void set_time_source_out(mboard_members_t&, const bool);
@@ -403,19 +266,15 @@ private:
void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);
void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members);
- void update_atr_leds(uhd::usrp::gpio_atr::gpio_atr_3000::sptr, const std::string &ant);
-
- void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false);
- double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false);
- void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100);
-
- void extended_adc_test(mboard_members_t& mb, double duration_s);
+ /// More IO stuff
+ uhd::device_addr_t get_tx_hints(size_t mb_index);
+ uhd::device_addr_t get_rx_hints(size_t mb_index);
+ uhd::endianness_t get_transport_endianness(size_t mb_index) {
+ return _mb[mb_index].if_pkt_is_big_endian ? uhd::ENDIANNESS_BIG : uhd::ENDIANNESS_LITTLE;
+ };
- //**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.
- static void synchronize_dacs(const std::vector<radio_perifs_t*>& mboards);
+ void post_streamer_hooks(uhd::direction_t dir);
};
#endif /* INCLUDED_X300_IMPL_HPP */
+// vim: sw=4 expandtab:
diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp
index 329405261..5314d1b9a 100644
--- a/host/lib/usrp/x300/x300_io_impl.cpp
+++ b/host/lib/usrp/x300/x300_io_impl.cpp
@@ -15,18 +15,19 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
+#define DEVICE3_STREAMER
+
#include "x300_regs.hpp"
#include "x300_impl.hpp"
-#include "validate_subdev_spec.hpp"
#include "../../transport/super_recv_packet_handler.hpp"
#include "../../transport/super_send_packet_handler.hpp"
#include <uhd/transport/nirio_zero_copy.hpp>
#include "async_packet_handler.hpp"
#include <uhd/transport/bounded_buffer.hpp>
-#include <uhd/transport/chdr.hpp>
#include <boost/bind.hpp>
#include <uhd/utils/tasks.hpp>
#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
@@ -35,589 +36,65 @@ using namespace uhd::usrp;
using namespace uhd::transport;
/***********************************************************************
- * update streamer rates
+ * Hooks for get_tx_stream() and get_rx_stream()
**********************************************************************/
-void x300_impl::update_tick_rate(mboard_members_t &mb, const double rate)
+device_addr_t x300_impl::get_rx_hints(size_t mb_index)
{
- BOOST_FOREACH(const size_t &dspno, mb.rx_streamers.keys())
- {
- boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock());
- if (my_streamer) my_streamer->set_tick_rate(rate);
- }
- BOOST_FOREACH(const size_t &dspno, mb.tx_streamers.keys())
+ device_addr_t rx_hints = _mb[mb_index].recv_args;
+ // (default to a large recv buff)
+ if (not rx_hints.has_key("recv_buff_size"))
{
- boost::shared_ptr<sph::send_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock());
- if (my_streamer) my_streamer->set_tick_rate(rate);
- }
-}
-
-void x300_impl::update_rx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate)
-{
- if (not mb.rx_streamers.has_key(dspno)) return;
- boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock());
- if (not my_streamer) return;
- my_streamer->set_samp_rate(rate);
- const double adj = mb.radio_perifs[dspno].ddc->get_scaling_adjustment();
- my_streamer->set_scale_factor(adj);
-}
-
-void x300_impl::update_tx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate)
-{
- if (not mb.tx_streamers.has_key(dspno)) return;
- boost::shared_ptr<sph::send_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock());
- if (not my_streamer) return;
- my_streamer->set_samp_rate(rate);
- const double adj = mb.radio_perifs[dspno].duc->get_scaling_adjustment();
- my_streamer->set_scale_factor(adj);
-}
-
-/***********************************************************************
- * Setup dboard muxing for IQ
- **********************************************************************/
-void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i, const subdev_spec_t &spec)
-{
- UHD_ASSERT_THROW(tx_rx == "tx" or tx_rx == "rx");
- UHD_ASSERT_THROW(mb_i < _mb.size());
- const std::string mb_name = boost::lexical_cast<std::string>(mb_i);
- fs_path mb_root = "/mboards/" + mb_name;
-
- //sanity checking
- validate_subdev_spec(_tree, spec, tx_rx, mb_name);
- UHD_ASSERT_THROW(spec.size() <= 2);
- if (spec.size() == 1) {
- UHD_ASSERT_THROW(spec[0].db_name == "A" || spec[0].db_name == "B");
- }
- else if (spec.size() == 2) {
- UHD_ASSERT_THROW(
- (spec[0].db_name == "A" && spec[1].db_name == "B") ||
- (spec[0].db_name == "B" && spec[1].db_name == "A")
- );
- }
-
- std::vector<size_t> chan_to_dsp_map(spec.size(), 0);
- // setup mux for this spec
- for (size_t i = 0; i < spec.size(); i++)
- {
- const int radio_idx = _mb[mb_i].get_radio_index(spec[i].db_name);
- chan_to_dsp_map[i] = radio_idx;
-
- //extract connection
- const fs_path fe_path(mb_root / "dboards" / spec[i].db_name / (tx_rx + "_frontends") / spec[i].sd_name);
- const std::string conn = _tree->access<std::string>(fe_path / "connection").get();
- if (tx_rx == "tx") {
- //swap condition
- _mb[mb_i].radio_perifs[radio_idx].tx_fe->set_mux(conn);
- } else {
- double if_freq = (_tree->exists(fe_path / "if_freq/value")) ?
- _tree->access<double>(fe_path / "if_freq/value").get() : 0.0;
- _mb[mb_i].radio_perifs[radio_idx].ddc->set_mux(usrp::fe_connection_t(conn, if_freq));
- _mb[mb_i].radio_perifs[radio_idx].rx_fe->set_mux(false);
+ if (_mb[mb_index].xport_path != "nirio") {
+ //For the ethernet transport, the buffer has to be set before creating
+ //the transport because it is independent of the frame size and # frames
+ //For nirio, the buffer size is not configurable by the user
+ #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD)
+ //limit buffer resize on macos or it will error
+ rx_hints["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH_MACOS);
+ #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)
+ //set to half-a-second of buffering at max rate
+ rx_hints["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH);
+ #endif
}
}
-
- _tree->access<std::vector<size_t> >(mb_root / (tx_rx + "_chan_dsp_mapping")).set(chan_to_dsp_map);
+ return rx_hints;
}
-/***********************************************************************
- * RX flow control handler
- **********************************************************************/
-static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args)
+device_addr_t x300_impl::get_tx_hints(size_t mb_index)
{
- double fullness_factor = rx_args.cast<double>("recv_buff_fullness", X300_RX_SW_BUFF_FULL_FACTOR);
-
- if (fullness_factor < 0.01 || fullness_factor > 1) {
- throw uhd::value_error("recv_buff_fullness must be between 0.01 and 1 inclusive (1% to 100%)");
+ device_addr_t tx_hints = _mb[mb_index].send_args;
+ if (_mb[mb_index].xport_path != "nirio") {
+ tx_hints["bpp"] = boost::lexical_cast<std::string>(X300_1GE_DATA_FRAME_MAX_SIZE);
}
-
- size_t window_in_pkts = (static_cast<size_t>(sw_buff_size * fullness_factor) / frame_size);
- if (window_in_pkts == 0) {
- throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size.");
- }
- return window_in_pkts;
+ return tx_hints;
}
-static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xport, bool big_endian, boost::shared_ptr<boost::uint32_t> seq32_state, const size_t last_seq)
+void x300_impl::post_streamer_hooks(direction_t dir)
{
- managed_send_buffer::sptr buff = xport->get_send_buff(0.0);
- if (not buff)
- {
- throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer");
- }
- boost::uint32_t *pkt = buff->cast<boost::uint32_t *>();
-
- //recover seq32
- boost::uint32_t &seq32 = *seq32_state;
- const size_t seq12 = seq32 & 0xfff;
- if (last_seq < seq12) seq32 += (1 << 12);
- seq32 &= ~0xfff;
- seq32 |= last_seq;
-
- //load packet info
- vrt::if_packet_info_t packet_info;
- packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
- packet_info.num_payload_words32 = 2;
- packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
- packet_info.packet_count = seq32;
- packet_info.sob = false;
- packet_info.eob = false;
- packet_info.sid = sid;
- 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
- if (big_endian)
- vrt::chdr::if_hdr_pack_be(pkt, packet_info);
- else
- vrt::chdr::if_hdr_pack_le(pkt, packet_info);
-
- //load payload
- pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0);
- pkt[packet_info.num_header_words32+1] = uhd::htonx<boost::uint32_t>(seq32);
-
- //send the buffer over the interface
- buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32));
-}
-
-
-/***********************************************************************
- * TX flow control handler
- **********************************************************************/
-struct x300_tx_fc_guts_t
-{
- x300_tx_fc_guts_t(void):
- stream_channel(0),
- device_channel(0),
- last_seq_out(0),
- last_seq_ack(0),
- seq_queue(1){}
- size_t stream_channel;
- size_t device_channel;
- size_t last_seq_out;
- size_t last_seq_ack;
- bounded_buffer<size_t> seq_queue;
- boost::shared_ptr<x300_impl::async_md_type> async_queue;
- boost::shared_ptr<x300_impl::async_md_type> old_async_queue;
-};
-
-#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0
-
-/*!
- * If the return value of this function is F, the last tx'd packet
- * has index N and the last ack'd packet has index M, the amount of
- * FC credit we have is C = F + M - N (i.e. we can send C more packets
- * before getting another ack).
- */
-static size_t get_tx_flow_control_window(size_t frame_size, const bool dram_buff, const device_addr_t& tx_args)
-{
- double default_buff_size = dram_buff ? X300_TX_HW_BUFF_SIZE_DRAM : X300_TX_HW_BUFF_SIZE_SRAM;
- double hw_buff_size = tx_args.cast<double>("send_buff_size", default_buff_size);
- size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / frame_size);
- if (window_in_pkts == 0) {
- throw uhd::value_error("send_buff_size must be larger than the send_frame_size.");
- }
- return window_in_pkts;
-}
-
-static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero_copy_if::sptr xport, bool big_endian, x300_clock_ctrl::sptr clock)
-{
- 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(boost::uint32_t);
- const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
-
- //unpacking can fail
- boost::uint32_t (*endian_conv)(boost::uint32_t) = uhd::ntohx;
- try
- {
- if (big_endian)
- {
- vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info);
- endian_conv = uhd::ntohx;
- }
- else
- {
- vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info);
- endian_conv = uhd::wtohx;
- }
- }
- catch(const std::exception &ex)
- {
- UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl;
+ if (dir != TX_DIRECTION) {
return;
}
- //fill in the async metadata
- async_metadata_t metadata;
- load_metadata_from_buff(
- endian_conv, metadata, if_packet_info, packet_buff,
- clock->get_master_clock_rate(), guts->stream_channel);
-
- //The FC response and the burst ack are two indicators that the radio
- //consumed packets. Use them to update the FC metadata
- if (metadata.event_code == X300_ASYNC_EVENT_CODE_FLOW_CTRL or
- metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK
- ) {
- const size_t seq = metadata.user_payload[0];
- guts->seq_queue.push_with_pop_on_full(seq);
- }
-
- //FC responses don't propagate up to the user so filter them here
- if (metadata.event_code != X300_ASYNC_EVENT_CODE_FLOW_CTRL) {
- guts->async_queue->push_with_pop_on_full(metadata);
- metadata.channel = guts->device_channel;
- guts->old_async_queue->push_with_pop_on_full(metadata);
- standard_async_msg_prints(metadata);
- }
-}
-
-static managed_send_buffer::sptr get_tx_buff_with_flowctrl(
- task::sptr /*holds ref*/,
- boost::shared_ptr<x300_tx_fc_guts_t> guts,
- zero_copy_if::sptr xport,
- size_t fc_pkt_window,
- const double timeout
-){
- while (true)
- {
- // delta is the amount of FC credit we've used up
- const size_t delta = (guts->last_seq_out & 0xfff) - (guts->last_seq_ack & 0xfff);
- // If we want to send another packet, we must have FC credit left
- if ((delta & 0xfff) < fc_pkt_window) break;
-
- // If credit is all used up, we check seq_queue for more.
- const bool ok = guts->seq_queue.pop_with_timed_wait(guts->last_seq_ack, timeout);
- if (not ok) return managed_send_buffer::sptr(); //timeout waiting for flow control
- }
-
- managed_send_buffer::sptr buff = xport->get_send_buff(timeout);
- if (buff) {
- guts->last_seq_out++; //update seq, this will actually be a send
- }
- return buff;
-}
-
-/***********************************************************************
- * Async Data
- **********************************************************************/
-bool x300_impl::recv_async_msg(
- async_metadata_t &async_metadata, double timeout
-){
- return _async_md->pop_with_timed_wait(async_metadata, timeout);
-}
-
-/***********************************************************************
- * Receive streamer
- **********************************************************************/
-rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_)
-{
- boost::mutex::scoped_lock lock(_transport_setup_mutex);
- stream_args_t args = args_;
-
- //setup defaults for unspecified values
- if (not args.otw_format.empty() and args.otw_format != "sc16")
- {
- throw uhd::value_error("x300_impl::get_rx_stream only supports otw_format sc16");
- }
- args.otw_format = "sc16";
- args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
-
- boost::shared_ptr<sph::recv_packet_streamer> my_streamer;
- for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
- {
- // Find the mainboard and subdev that corresponds to channel args.channels[stream_i]
- const size_t chan = args.channels[stream_i];
- size_t mb_chan = chan, mb_index;
- for (mb_index = 0; mb_index < _mb.size(); mb_index++) {
- const subdev_spec_t &curr_subdev_spec =
- _tree->access<subdev_spec_t>("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "rx_subdev_spec").get();
- if (mb_chan < curr_subdev_spec.size()) {
- break;
- } else {
- mb_chan -= curr_subdev_spec.size();
- }
+ // Loop through all tx streamers. Find all radios connected to one
+ // streamer. Sync those.
+ BOOST_FOREACH(const boost::weak_ptr<uhd::tx_streamer> &streamer_w, _tx_streamers.vals()) {
+ const boost::shared_ptr<sph::send_packet_streamer> streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(streamer_w.lock());
+ if (not streamer) {
+ continue;
}
- // Find the DSP that corresponds to this mainboard and subdev
- UHD_ASSERT_THROW(mb_index < _mb.size());
- mboard_members_t &mb = _mb[mb_index];
- const std::vector<size_t> dsp_map = _tree->access<std::vector<size_t> >("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "rx_chan_dsp_mapping")
- .get(); //.at(mb_chan);
- UHD_ASSERT_THROW(mb_chan < dsp_map.size());
- const size_t radio_index = dsp_map[mb_chan];
- UHD_ASSERT_THROW(radio_index < 2);
- radio_perifs_t &perif = mb.radio_perifs[radio_index];
-
- //setup the dsp transport hints (default to a large recv buff)
- device_addr_t device_addr = mb.recv_args;
- if (not device_addr.has_key("recv_buff_size"))
- {
- if (mb.xport_path != "nirio") {
- //For the ethernet transport, the buffer has to be set before creating
- //the transport because it is independent of the frame size and # frames
- //For nirio, the buffer size is not configurable by the user
- #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD)
- //limit buffer resize on macos or it will error
- device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH_MACOS);
- #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)
- //set to half-a-second of buffering at max rate
- device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH);
- #endif
- }
+ std::vector<rfnoc::x300_radio_ctrl_impl::sptr> radio_ctrl_blks =
+ streamer->get_terminator()->find_downstream_node<rfnoc::x300_radio_ctrl_impl>();
+ try {
+ //UHD_MSG(status) << "[X300] syncing " << radio_ctrl_blks.size() << " radios " << std::endl;
+ rfnoc::x300_radio_ctrl_impl::synchronize_dacs(radio_ctrl_blks);
}
-
- //allocate sid and create transport
- boost::uint8_t dest = (radio_index == 0)? X300_XB_DST_R0 : X300_XB_DST_R1;
- boost::uint32_t data_sid;
- UHD_LOG << "creating rx stream " << device_addr.to_string() << std::endl;
- both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_RX, device_addr, data_sid);
- UHD_LOG << boost::format("data_sid = 0x%08x, actual recv_buff_size = %d\n") % data_sid % xport.recv_buff_size << std::endl;
-
- // 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 = xport.recv->get_recv_frame_size() - X300_RX_MAX_HDR_LEN; // bytes per packet
- const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item
- const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); // samples per packet
-
- //make the new streamer given the samples per packet
- if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
- my_streamer->resize(args.channels.size());
-
- //init some streamer stuff
- std::string conv_endianness;
- if (mb.if_pkt_is_big_endian) {
- 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";
+ catch(const uhd::io_error &ex) {
+ throw uhd::io_error(str(boost::format("Failed to sync DACs! %s ") % ex.what()));
}
-
- //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);
-
- perif.framer->clear();
- perif.framer->set_nsamps_per_packet(spp); //seems to be a good place to set this
- perif.framer->set_sid((data_sid << 16) | (data_sid >> 16));
- perif.framer->setup(args);
- perif.ddc->setup(args);
-
- //flow control setup
- const size_t fc_window = get_rx_flow_control_window(xport.recv->get_recv_frame_size(), xport.recv_buff_size, device_addr);
- const size_t fc_handle_window = std::max<size_t>(1, fc_window / X300_RX_FC_REQUEST_FREQ);
-
- UHD_LOG << "RX Flow Control Window = " << fc_window << ", RX Flow Control Handler Window = " << fc_handle_window << std::endl;
-
- perif.framer->configure_flow_control(fc_window);
-
- boost::shared_ptr<boost::uint32_t> seq32(new boost::uint32_t(0));
- //Give the streamer a functor to get the recv_buffer
- //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency
- my_streamer->set_xport_chan_get_buff(
- stream_i,
- boost::bind(&zero_copy_if::get_recv_buff, xport.recv, _1),
- true /*flush*/
- );
- //Give the streamer a functor to handle overflows
- //bind requires a weak_ptr to break the a streamer->streamer circular dependency
- //Using "this" is OK because we know that x300_impl will outlive the streamer
- my_streamer->set_overflow_handler(
- stream_i,
- boost::bind(&x300_impl::handle_overflow, this, boost::ref(perif), boost::weak_ptr<uhd::rx_streamer>(my_streamer))
- );
- //Give the streamer a functor to send flow control messages
- //handle_rx_flowctrl is static and has no lifetime issues
- my_streamer->set_xport_handle_flowctrl(
- stream_i, boost::bind(&handle_rx_flowctrl, data_sid, xport.send, mb.if_pkt_is_big_endian, seq32, _1),
- fc_handle_window,
- true/*init*/
- );
- //Give the streamer a functor issue stream cmd
- //bind requires a rx_vita_core_3000::sptr to add a streamer->framer lifetime dependency
- my_streamer->set_issue_stream_cmd(
- stream_i, boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)
- );
-
- //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency
- mb.rx_streamers[radio_index] = boost::weak_ptr<sph::recv_packet_streamer>(my_streamer);
-
- //sets all tick and samp rates on this streamer
- const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index);
- _tree->access<double>(mb_path / "tick_rate").update();
- _tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(radio_index) / "rate" / "value").update();
}
-
- return my_streamer;
}
-void x300_impl::handle_overflow(x300_impl::radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer)
-{
- boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::recv_packet_streamer>(streamer.lock());
- if (not my_streamer) return; //If the rx_streamer has expired then overflow handling makes no sense.
-
- if (my_streamer->get_num_channels() == 1)
- {
- perif.framer->handle_overflow();
- return;
- }
-
- /////////////////////////////////////////////////////////////
- // MIMO overflow recovery time
- /////////////////////////////////////////////////////////////
- //find out if we were in continuous mode before stopping
- const bool in_continuous_streaming_mode = perif.framer->in_continuous_streaming_mode();
- //stop streaming
- my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
- //flush transports
- my_streamer->flush_all(0.001);
- //restart streaming
- if (in_continuous_streaming_mode)
- {
- stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
- stream_cmd.stream_now = false;
- stream_cmd.time_spec = perif.time64->get_time_now() + time_spec_t(0.01);
- my_streamer->issue_stream_cmd(stream_cmd);
- }
-}
-
-/***********************************************************************
- * Transmit streamer
- **********************************************************************/
-tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
-{
- boost::mutex::scoped_lock lock(_transport_setup_mutex);
- stream_args_t args = args_;
-
- //setup defaults for unspecified values
- if (not args.otw_format.empty() and args.otw_format != "sc16")
- {
- throw uhd::value_error("x300_impl::get_tx_stream only supports otw_format sc16");
- }
- args.otw_format = "sc16";
- args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
-
- //shared async queue for all channels in streamer
- boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/));
-
- std::vector<radio_perifs_t*> radios_list;
- boost::shared_ptr<sph::send_packet_streamer> my_streamer;
- for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
- {
- // Find the mainboard and subdev that corresponds to channel args.channels[stream_i]
- const size_t chan = args.channels[stream_i];
- size_t mb_chan = chan, mb_index;
- for (mb_index = 0; mb_index < _mb.size(); mb_index++) {
- const subdev_spec_t &curr_subdev_spec =
- _tree->access<subdev_spec_t>("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "tx_subdev_spec").get();
- if (mb_chan < curr_subdev_spec.size()) {
- break;
- } else {
- mb_chan -= curr_subdev_spec.size();
- }
- }
- // Find the DSP that corresponds to this mainboard and subdev
- mboard_members_t &mb = _mb[mb_index];
- const size_t radio_index = _tree->access<std::vector<size_t> >("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "tx_chan_dsp_mapping")
- .get().at(mb_chan);
- radio_perifs_t &perif = mb.radio_perifs[radio_index];
- radios_list.push_back(&perif);
-
- //setup the dsp transport hints (TODO)
- device_addr_t device_addr = mb.send_args;
-
- //allocate sid and create transport
- boost::uint8_t dest = (radio_index == 0)? X300_XB_DST_R0 : X300_XB_DST_R1;
- boost::uint32_t data_sid;
- UHD_LOG << "creating tx stream " << device_addr.to_string() << std::endl;
- both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid);
- UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl;
-
- // 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 = xport.send->get_send_frame_size() - X300_TX_MAX_HDR_LEN;
- const size_t bpi = convert::get_bytes_per_item(args.otw_format);
- const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
-
- //make the new streamer given the samples per packet
- if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
- my_streamer->resize(args.channels.size());
-
- std::string conv_endianness;
- if (mb.if_pkt_is_big_endian) {
- 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);
-
- perif.deframer->clear();
- perif.deframer->setup(args);
- perif.duc->setup(args);
-
- //flow control setup
- size_t fc_window = get_tx_flow_control_window(xport.send->get_send_frame_size(), mb.has_dram_buff, device_addr); //In packets
- const size_t fc_handle_window = std::max<size_t>(1,
- fc_window/ (mb.has_dram_buff ? X300_TX_FC_RESPONSE_FREQ_DRAM : X300_TX_FC_RESPONSE_FREQ_SRAM));
-
- UHD_LOG << "TX Flow Control Window = " << fc_window << ", TX Flow Control Handler Window = " << fc_handle_window << std::endl;
-
- perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window);
- boost::shared_ptr<x300_tx_fc_guts_t> guts(new x300_tx_fc_guts_t());
- guts->stream_channel = stream_i;
- guts->device_channel = chan;
- guts->async_queue = async_md;
- guts->old_async_queue = _async_md;
- task::sptr task = task::make(boost::bind(&handle_tx_async_msgs, guts, xport.recv, mb.if_pkt_is_big_endian, mb.clock));
-
- //Give the streamer a functor to get the send buffer
- //get_tx_buff_with_flowctrl is static so bind has no lifetime issues
- //xport.send (sptr) is required to add streamer->data-transport lifetime dependency
- //task (sptr) is required to add a streamer->async-handler lifetime dependency
- my_streamer->set_xport_chan_get_buff(
- stream_i,
- boost::bind(&get_tx_buff_with_flowctrl, task, guts, xport.send, fc_window, _1)
- );
- //Give the streamer a functor handled received async messages
- my_streamer->set_async_receiver(
- boost::bind(&async_md_type::pop_with_timed_wait, async_md, _1, _2)
- );
- my_streamer->set_xport_chan_sid(stream_i, true, data_sid);
- my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet
-
- //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency
- mb.tx_streamers[radio_index] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer);
-
- //sets all tick and samp rates on this streamer
- const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index);
- _tree->access<double>(mb_path / "tick_rate").update();
- _tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(radio_index) / "rate" / "value").update();
- }
-
- synchronize_dacs(radios_list);
- return my_streamer;
-}
+// vim: sw=4 expandtab:
diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp
new file mode 100644
index 000000000..3a129b334
--- /dev/null
+++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp
@@ -0,0 +1,888 @@
+//
+// Copyright 2015-2016 Ettus Research
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "x300_radio_ctrl_impl.hpp"
+
+#include "x300_dboard_iface.hpp"
+#include "wb_iface_adapter.hpp"
+#include "gpio_atr_3000.hpp"
+#include "apply_corrections.hpp"
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/rfnoc/node_ctrl_base.hpp>
+#include <uhd/transport/chdr.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/date_time/posix_time/posix_time_io.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+using namespace uhd::usrp::x300;
+
+static const size_t IO_MASTER_RADIO = 0;
+
+/****************************************************************************
+ * Structors
+ ***************************************************************************/
+UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(x300_radio_ctrl)
+ , _ignore_cal_file(false)
+{
+ UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::ctor() " << std::endl;
+
+ ////////////////////////////////////////////////////////////////////
+ // 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,
+ radio_ctrl_impl::regs::sr_addr(radio_ctrl_impl::regs::SPI),
+ radio_ctrl_impl::regs::RB_SPI);
+ _leds = gpio_atr::gpio_atr_3000::make_write_only(ctrl, regs::sr_addr(regs::LEDS));
+ _leds->set_atr_mode(usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ _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_FP_GPIO);
+ BOOST_FOREACH(const gpio_atr::gpio_attr_map_t::value_type attr, gpio_atr::gpio_attr_map) {
+ _tree->create<boost::uint32_t>(fs_path("gpio") / "FP0" / attr.second)
+ .set(0)
+ .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, _fp_gpio, attr.first, _1));
+ }
+ _tree->create<boost::uint32_t>(fs_path("gpio") / "FP0" / "READBACK")
+ .set_publisher(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _fp_gpio));
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // 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++) {
+ _rx_fe_map[i].core = rx_frontend_core_3000::make(_get_ctrl(i), regs::sr_addr(x300_regs::RX_RE_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));
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // 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()
+{
+ // 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) {
+ BOOST_FOREACH(const gpio_atr::gpio_attr_map_t::value_type attr, gpio_atr::gpio_attr_map) {
+ _tree->remove(fs_path("gpio") / "FP0" / attr.second);
+ }
+ _tree->remove(fs_path("gpio") / "FP0" / "READBACK");
+ }
+
+ // 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 */)
+{
+ // On X3x0, tick rate can't actually be changed at runtime
+ return get_rate();
+}
+
+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);
+}
+
+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);
+}
+
+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_tx_gain(const double gain, const size_t chan)
+{
+ //TODO: This is extremely hacky!
+ fs_path path("dboards" / _radio_slot / "tx_frontends" / _tx_fe_map.at(chan).db_fe_name / "gains");
+ std::vector<std::string> gain_stages = _tree->list(path);
+ if (gain_stages.size() == 1) {
+ const double actual_gain = _tree->access<double>(path / gain_stages[0] / "value").set(gain).get();
+ radio_ctrl_impl::set_tx_gain(actual_gain, chan);
+ return gain;
+ } else {
+ UHD_MSG(warning) << "set_tx_gain: could not apply gain for this daughterboard.";
+ radio_ctrl_impl::set_tx_gain(0.0, chan);
+ return 0.0;
+ }
+}
+
+double x300_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ //TODO: This is extremely hacky!
+ fs_path path("dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name / "gains");
+ std::vector<std::string> gain_stages = _tree->list(path);
+ if (gain_stages.size() == 1) {
+ const double actual_gain = _tree->access<double>(path / gain_stages[0] / "value").set(gain).get();
+ radio_ctrl_impl::set_rx_gain(actual_gain, chan);
+ return gain;
+ } else {
+ UHD_MSG(warning) << "set_rx_gain: could not apply gain for this daughterboard.";
+ radio_ctrl_impl::set_tx_gain(0.0, chan);
+ return 0.0;
+ }
+}
+
+
+template <typename map_type>
+static size_t _get_chan_from_map(std::map<size_t, map_type> map, const std::string &fe)
+{
+ // TODO replace with 'auto' when possible
+ typedef typename std::map<size_t, map_type>::iterator chan_iterator;
+ for (chan_iterator 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();
+}
+
+/****************************************************************************
+ * Radio control and setup
+ ***************************************************************************/
+void x300_radio_ctrl_impl::setup_radio(uhd::i2c_iface::sptr zpu_i2c, x300_clock_ctrl::sptr clock, bool verbose)
+{
+ _self_cal_adc_capture_delay(verbose);
+
+ ////////////////////////////////////////////////////////////////////
+ // 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 =
+ boost::assign::list_of(RX_EEPROM_ADDR)(TX_EEPROM_ADDR)(GDB_EEPROM_ADDR);
+ const static std::vector<std::string> EEPROM_PATHS =
+ boost::assign::list_of("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(&dboard_eeprom_t::store,
+ _db_eeproms[addr], boost::ref(*zpu_i2c), (BASE_ADDR | addr)));
+ }
+
+ //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_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].id,
+ _db_eeproms[TX_EEPROM_ADDR + DB_OFFSET].id,
+ _db_eeproms[GDB_EEPROM_ADDR + DB_OFFSET].id,
+ db_iface, _tree->subtree(db_path),
+ true // defer daughterboard intitialization
+ );
+
+ size_t rx_chan = 0, tx_chan = 0;
+ BOOST_FOREACH(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++;
+ }
+ BOOST_FOREACH(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()
+ and _tree->exists(db_path / "rx_frontends" / _rx_fe_map[0].db_fe_name / "antenna" / "value")) {
+ _tree->access<std::string>(db_path / "rx_frontends" / _rx_fe_map[0].db_fe_name / "antenna" / "value")
+ .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::_update_atr_leds, this, _1));
+ }
+ _update_atr_leds(""); //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";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)) {
+ _tree->access<double>(db_tx_fe_path / name / "freq" / "value")
+ .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_tx_fe_corrections, this, _radio_slot, _1));
+ }
+ const fs_path db_rx_fe_path = db_path / "rx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) {
+ _tree->access<double>(db_rx_fe_path / name / "freq" / "value")
+ .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_rx_fe_corrections, this, _radio_slot, _1));
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Set tick rate
+ ////////////////////////////////////////////////////////////////
+ const double tick_rate = get_output_samp_rate(0);
+ if (_radio_type==PRIMARY) {
+ // Slot A is the highlander timekeeper
+ _tree->access<double>("tick_rate").set(tick_rate);
+ }
+ radio_ctrl_impl::set_rate(tick_rate);
+}
+
+void x300_radio_ctrl_impl::set_rx_fe_corrections(
+ const std::string &slot_name,
+ const double lo_freq
+) {
+ if (not _ignore_cal_file) {
+ apply_rx_fe_corrections(_tree, slot_name, lo_freq);
+ }
+}
+
+void x300_radio_ctrl_impl::set_tx_fe_corrections(
+ const std::string &slot_name,
+ const double lo_freq
+) {
+ if (not _ignore_cal_file) {
+ apply_tx_fe_corrections(_tree, slot_name, 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(boost::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.
+ boost::this_thread::sleep(boost::posix_time::microsec(1000));
+ _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);
+
+ boost::this_thread::sleep(boost::posix_time::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_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n")
+ % 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++) {
+ //Print date and time
+ boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S");
+ std::ostringstream time_strm;
+ time_strm.imbue(std::locale(std::locale::classic(), facet));
+ time_strm << boost::posix_time::second_clock::local_time();
+ //Run self-test
+ UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1);
+ try {
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->self_test_adc((SECS_PER_ITER*1000)/radios.size());
+ }
+ UHD_MSG(status) << "passed" << std::endl;
+ } catch(std::exception &e) {
+ num_failures++;
+ UHD_MSG(status) << e.what() << std::endl;
+ }
+ }
+ if (num_failures == 0) {
+ UHD_MSG(status) << "Extended ADC Self-Test PASSED\n";
+ } 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.
+
+ //Reinitialize and resync all DACs
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->_dac->reset();
+ }
+
+ //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 + uncertaintly to the command time
+ boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/;
+
+ //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_tick_rate(radios[i]->_radio_clk_rate, IO_MASTER_RADIO);
+ radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0);
+ 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.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 1);
+ radios[i]->_regs->misc_outs_reg.set(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
+ boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us));
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->_dac->verify_sync();
+ }
+}
+
+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_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush;
+
+ //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;
+
+ UHD_MSG(status) << "Measuring..." << std::flush;
+ 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);
+
+ boost::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
+ boost::this_thread::sleep(boost::posix_time::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
+ boost::this_thread::sleep(boost::posix_time::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_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % 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) {
+ UHD_MSG(status) << "Validating..." << std::flush;
+ //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_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") %
+ (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 bool is_txrx = (rx_ant == "TX/RX");
+ const int rx_led = (1 << 2);
+ const int tx_led = (1 << 1);
+ const int txrx_led = (1 << 0);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_IDLE, 0);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, tx_led);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, rx_led | tx_led);
+}
+
+void x300_radio_ctrl_impl::_self_cal_adc_capture_delay(bool print_status)
+{
+ if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush;
+
+ static const boost::uint32_t NUM_DELAY_STEPS = 32; //The IDELAYE2 element has 32 steps
+ static const boost::uint32_t NUM_RETRIES = 2; //Retry self-cal if it fails in warmup situations
+ static const boost::int32_t MIN_WINDOW_LEN = 4;
+
+ boost::int32_t win_start = -1, win_stop = -1;
+ boost::uint32_t iter = 0;
+ while (iter++ < NUM_RETRIES) {
+ for (boost::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);
+
+ boost::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);
+ //10ms @ 200MHz = 2 million samples
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ 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);
+ //10ms @ 200MHz = 2 million samples
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ 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_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % 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;
+ boost::this_thread::sleep(boost::posix_time::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.");
+ }
+
+ boost::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_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter;
+ }
+}
+
+void x300_radio_ctrl_impl::_check_adc(const boost::uint32_t val)
+{
+ //Wait for previous control transaction to flush
+ user_reg_read64(regs::RB_TEST);
+ //Wait for ADC test pattern to propagate
+ boost::this_thread::sleep(boost::posix_time::microsec(5));
+ //Read value of RX readback register and verify
+ boost::uint32_t adc_rb = static_cast<boost::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());
+ }
+}
+
+/****************************************************************************
+ * Helpers
+ ***************************************************************************/
+bool x300_radio_ctrl_impl::check_radio_config()
+{
+ UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::check_radio_config() " << std::endl;
+ const fs_path rx_fe_path = fs_path("dboards" / _radio_slot / "rx_frontends");
+ for (size_t chan = 0; chan < _get_num_radios(); chan++) {
+ if (_tree->exists(rx_fe_path / _rx_fe_map.at(chan).db_fe_name / "enabled")) {
+ const bool chan_active = _is_streamer_active(RX_DIRECTION, chan);
+ _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 < _get_num_radios(); chan++) {
+ if (_tree->exists(tx_fe_path / _rx_fe_map.at(chan).db_fe_name / "enabled")) {
+ const bool chan_active = _is_streamer_active(TX_DIRECTION, chan);
+ _tree->access<bool>(tx_fe_path / _rx_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
new file mode 100644
index 000000000..f2150a982
--- /dev/null
+++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp
@@ -0,0 +1,198 @@
+//
+// Copyright 2015-2016 Ettus Research
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP
+#define INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP
+
+#include "radio_ctrl_impl.hpp"
+#include "x300_clock_ctrl.hpp"
+#include "spi_core_3000.hpp"
+#include "x300_adc_ctrl.hpp"
+#include "x300_dac_ctrl.hpp"
+#include "x300_regs.hpp"
+#include "rx_frontend_core_3000.hpp"
+#include "tx_frontend_core_200.hpp"
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+
+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);
+
+ double set_tx_frequency(const double freq, const size_t chan);
+ double set_rx_frequency(const double freq, const size_t chan);
+ double get_tx_frequency(const size_t chan);
+ double get_rx_frequency(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);
+
+ 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);
+
+ 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 verbose);
+
+ void reset_codec();
+
+ void self_test_adc(
+ boost::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_MISC_IO) { }
+ } misc_ins_reg;
+
+ radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::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_RE_BASE = 232;
+ };
+
+ void _update_atr_leds(const std::string &rx_ant);
+
+ void _self_cal_adc_capture_delay(bool print_status);
+
+ void _check_adc(const boost::uint32_t val);
+
+ void set_rx_fe_corrections(const std::string &fe_name, const double lo_freq);
+ void set_tx_fe_corrections(const std::string &fe_name, const double lo_freq);
+
+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;
+ 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;
+
+}; /* class radio_ctrl_impl */
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp
index 69a8d5d9f..458d6ba0f 100644
--- a/host/lib/usrp/x300/x300_regs.hpp
+++ b/host/lib/usrp/x300/x300_regs.hpp
@@ -22,41 +22,6 @@
#include <stdint.h>
#include <uhd/utils/soft_register.hpp>
-namespace uhd { namespace usrp { namespace radio {
-
-static UHD_INLINE uint32_t sr_addr(const uint32_t offset)
-{
- return offset * 4;
-}
-
-static const uint32_t DACSYNC = 5;
-static const uint32_t LOOPBACK = 6;
-static const uint32_t TEST = 7;
-static const uint32_t SPI = 8;
-static const uint32_t GPIO = 16;
-static const uint32_t MISC_OUTS = 24;
-static const uint32_t READBACK = 32;
-static const uint32_t TX_CTRL = 64;
-static const uint32_t RX_CTRL = 96;
-static const uint32_t TIME = 128;
-static const uint32_t RX_DSP = 144;
-static const uint32_t TX_DSP = 184;
-static const uint32_t LEDS = 195;
-static const uint32_t FP_GPIO = 201;
-static const uint32_t RX_FRONT = 208;
-static const uint32_t TX_FRONT = 216;
-
-static const uint32_t RB32_GPIO = 0;
-static const uint32_t RB32_SPI = 4;
-static const uint32_t RB64_TIME_NOW = 8;
-static const uint32_t RB64_TIME_PPS = 16;
-static const uint32_t RB32_TEST = 24;
-static const uint32_t RB32_RX = 28;
-static const uint32_t RB32_FP_GPIO = 32;
-static const uint32_t RB32_MISC_INS = 36;
-
-}}} // namespace
-
static const int BL_ADDRESS = 0;
static const int BL_DATA = 1;
@@ -87,6 +52,7 @@ static const int ZPU_SR_DRAM_FIFO1 = 80;
static const int ZPU_RB_SPI = 2;
static const int ZPU_RB_CLK_STATUS = 3;
static const int ZPU_RB_COMPAT_NUM = 6;
+static const int ZPU_RB_NUM_CE = 7;
static const int ZPU_RB_SFP0_TYPE = 4;
static const int ZPU_RB_SFP1_TYPE = 5;
static const int ZPU_RB_DRAM_FIFO0 = 10;
@@ -250,49 +216,6 @@ namespace uhd { namespace usrp { namespace x300 {
}
};
- 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]
-
- misc_outs_reg_t(): uhd::soft_reg32_wo_t(uhd::usrp::radio::sr_addr(uhd::usrp::radio::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);
- }
- } misc_outs_reg;
-
- class misc_ins_reg_t : public uhd::soft_reg32_ro_t {
- public:
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0); //[0]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1); //[1]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2); //[2]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3); //[3]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 4); //[4]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 5); //[5]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 6); //[6]
- UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 7); //[7]
-
- misc_ins_reg_t(): uhd::soft_reg32_ro_t(uhd::usrp::radio::RB32_MISC_INS) { }
- } misc_ins_reg;
-
- radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::string>(radio_num) + "_regmap") {
- add_to_map(misc_outs_reg, "misc_outs_reg", PUBLIC);
- add_to_map(misc_ins_reg, "misc_ins_reg", PUBLIC);
- }
- };
-
}}}
#endif /* INCLUDED_X300_REGS_HPP */