//
// 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 .
//
#include "device3_impl.hpp"
#include "graph_impl.hpp"
#include "ctrl_iface.hpp"
#include
#include
#include
#include
#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(lhs) < boost::lexical_cast(rhs);
}
void device3_impl::merge_channel_defs(
const std::vector &chan_ids,
const std::vector &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 chan_idxs;
// 1. Get sorted list of currently defined channels
std::vector 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(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(chan_idx));
}
}
}
size_t last_chan_idx = curr_channels.empty() ? 0 : (boost::lexical_cast(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(chans_root / chan_idxs[i]);
}
_tree->access(chans_root / chan_idxs[i]).set(chan_ids[i]);
if (not _tree->exists(chans_root / chan_idxs[i] / "args")) {
_tree->create(chans_root / chan_idxs[i] / "args");
}
_tree->access(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(
name,
shared_from_this()
);
}