diff options
Diffstat (limited to 'host/lib/usrp/device3/device3_impl.cpp')
-rw-r--r-- | host/lib/usrp/device3/device3_impl.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
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() + ); +} + |