diff options
author | Ashish Chaudhari <ashish@ettus.com> | 2019-05-28 13:21:08 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:20 -0800 |
commit | b99d0f348eada0500ea5d668d5eba283afa6c4a4 (patch) | |
tree | bed87ff733aab37c7d5d3851f5cc5cbcffe056d4 /host/lib/rfnoc/link_stream_manager.cpp | |
parent | 374b30cc0eb0842e567d01486d9be8155e361d9f (diff) | |
download | uhd-b99d0f348eada0500ea5d668d5eba283afa6c4a4.tar.gz uhd-b99d0f348eada0500ea5d668d5eba283afa6c4a4.tar.bz2 uhd-b99d0f348eada0500ea5d668d5eba283afa6c4a4.zip |
rfnoc: Added link/graph specific stream managers
- Fleshed out mb_iface
- Managers currently only export ctrl APIs. Data APIs TBD
Diffstat (limited to 'host/lib/rfnoc/link_stream_manager.cpp')
-rw-r--r-- | host/lib/rfnoc/link_stream_manager.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/host/lib/rfnoc/link_stream_manager.cpp b/host/lib/rfnoc/link_stream_manager.cpp new file mode 100644 index 000000000..0faec94e3 --- /dev/null +++ b/host/lib/rfnoc/link_stream_manager.cpp @@ -0,0 +1,266 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/exception.hpp> +#include <uhd/transport/muxed_zero_copy_if.hpp> +#include <uhd/utils/log.hpp> +#include <uhdlib/rfnoc/chdr_ctrl_endpoint.hpp> +#include <uhdlib/rfnoc/link_stream_manager.hpp> +#include <uhdlib/rfnoc/mgmt_portal.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::rfnoc; +using namespace uhd::transport; +using namespace uhd::rfnoc::chdr; +using namespace uhd::rfnoc::mgmt; +using namespace uhd::rfnoc::detail; + +constexpr sep_inst_t SEP_INST_MGMT_CTRL = 0; +constexpr sep_inst_t SEP_INST_DATA_BASE = 1; + +link_stream_manager::~link_stream_manager() = default; + +class link_stream_manager_impl : public link_stream_manager +{ +public: + link_stream_manager_impl(const chdr::chdr_packet_factory& pkt_factory, + mb_iface& mb_if, + const epid_allocator::sptr& epid_alloc, + device_id_t device_id) + : _pkt_factory(pkt_factory) + , _my_device_id(device_id) + , _mb_iface(mb_if) + , _epid_alloc(epid_alloc) + , _data_ep_inst(0) + { + // Sanity check if we can access our device ID from this motherboard + const auto& mb_devs = _mb_iface.get_local_device_ids(); + if (std::find(mb_devs.begin(), mb_devs.end(), _my_device_id) == mb_devs.end()) { + throw uhd::rfnoc_error("The device bound to this link manager cannot be " + "accessed from this motherboard"); + } + + // Sanity check the protocol version and CHDR width + if ((_pkt_factory.get_protover() & 0xFF00) + != (_mb_iface.get_proto_ver() & 0xFF00)) { + throw uhd::rfnoc_error("RFNoC protocol mismatch between SW and HW"); + } + if (_pkt_factory.get_chdr_w() != _mb_iface.get_chdr_w()) { + throw uhd::rfnoc_error("RFNoC CHDR width mismatch between SW and HW"); + } + + // Create a transport and EPID for management and control traffic + _my_mgmt_ctrl_epid = + epid_alloc->allocate_epid(sep_addr_t(_my_device_id, SEP_INST_MGMT_CTRL)); + _allocated_epids.insert(_my_mgmt_ctrl_epid); + + // Create a muxed transport to share between the mgmt_portal and + // chdr_ctrl_endpoint. We have to use the same base transport here to ensure that + // the route setup logic in the FPGA transport works correctly. + // TODO: This needs to be cleaned up. A muxed_zero_copy_if is excessive here + chdr_ctrl_xport_t base_xport = + _mb_iface.make_ctrl_transport(_my_device_id, _my_mgmt_ctrl_epid); + UHD_ASSERT_THROW(base_xport.send.get() == base_xport.recv.get()) + + auto classify_fn = [&pkt_factory](void* buff, size_t) -> uint32_t { + if (buff) { + chdr_packet::cuptr pkt = pkt_factory.make_generic(); + pkt->refresh(buff); + return (pkt->get_chdr_header().get_pkt_type() == PKT_TYPE_MGMT) ? 0 : 1; + } else { + throw uhd::assertion_error("null pointer"); + } + }; + _muxed_xport = muxed_zero_copy_if::make(base_xport.send, classify_fn, 2); + + // Create child transports + chdr_ctrl_xport_t mgmt_xport = base_xport; + mgmt_xport.send = _muxed_xport->make_stream(0); + mgmt_xport.recv = mgmt_xport.send; + _ctrl_xport = base_xport; + _ctrl_xport.send = _muxed_xport->make_stream(1); + _ctrl_xport.recv = _ctrl_xport.send; + + // Create management portal using one of the child transports + _mgmt_portal = mgmt_portal::make(mgmt_xport, + _pkt_factory, + sep_addr_t(_my_device_id, SEP_INST_MGMT_CTRL), + _my_mgmt_ctrl_epid); + } + + virtual ~link_stream_manager_impl() + { + for (const auto& epid : _allocated_epids) { + _epid_alloc->deallocate_epid(epid); + } + } + + virtual device_id_t get_self_device_id() const + { + return _my_device_id; + } + + virtual const std::set<sep_addr_t>& get_reachable_endpoints() const + { + return _mgmt_portal->get_reachable_endpoints(); + } + + virtual sep_id_pair_t init_ctrl_stream(sep_addr_t dst_addr) + { + // Allocate EPIDs + sep_id_t dst_epid = _epid_alloc->allocate_epid(dst_addr); + UHD_LOGGER_DEBUG("RFNOC::LINK_MGR") + << boost::format("Initializing control stream to Endpoint %d:%d with EPID " + "%d...") + % dst_addr.first % dst_addr.second % dst_epid; + _ensure_ep_is_reachable(dst_addr); + + // Make sure that the software side of the endpoint is initialized and reachable + if (_ctrl_ep == nullptr) { + // Create a control endpoint with that xport + _ctrl_ep = + chdr_ctrl_endpoint::make(_ctrl_xport, _pkt_factory, _my_mgmt_ctrl_epid); + } + + // Setup a route to the EPID + _mgmt_portal->initialize_endpoint(dst_addr, dst_epid); + _mgmt_portal->setup_local_route(dst_epid); + if (!_mgmt_portal->get_endpoint_info(dst_epid).has_ctrl) { + throw uhd::rfnoc_error( + "Downstream endpoint does not support control traffic"); + } + + // Create a client zero instance + if (_client_zero_map.count(dst_epid) == 0) { + _client_zero_map.insert( + std::make_pair(dst_epid, client_zero::make(*_ctrl_ep, dst_epid))); + } + return sep_id_pair_t(_my_mgmt_ctrl_epid, dst_epid); + } + + virtual ctrlport_endpoint::sptr get_block_register_iface(sep_id_t dst_epid, + uint16_t block_index, + const clock_iface& client_clk, + const clock_iface& timebase_clk) + { + // Ensure that the endpoint is initialized for control at the specified EPID + if (_ctrl_ep == nullptr) { + throw uhd::runtime_error("Software endpoint not initialized for control"); + } + if (_client_zero_map.count(dst_epid) == 0) { + throw uhd::runtime_error( + "Control for the specified EPID was not initialized"); + } + const client_zero::sptr& c0_ctrl = _client_zero_map.at(dst_epid); + uint16_t dst_port = 1 + c0_ctrl->get_num_stream_endpoints() + block_index; + if (block_index >= c0_ctrl->get_num_blocks()) { + throw uhd::value_error("Requested block index out of range"); + } + + // Create control endpoint + return _ctrl_ep->get_ctrlport_ep(dst_epid, + dst_port, + (size_t(1) << c0_ctrl->get_block_info(dst_port).ctrl_fifo_size), + c0_ctrl->get_block_info(dst_port).ctrl_max_async_msgs, + client_clk, + timebase_clk); + } + + virtual client_zero::sptr get_client_zero(sep_id_t dst_epid) const + { + if (_client_zero_map.count(dst_epid) == 0) { + throw uhd::runtime_error( + "Control for the specified EPID was not initialized"); + } + return _client_zero_map.at(dst_epid); + } + + virtual chdr_data_xport_t create_data_stream( + sep_addr_t dst_addr, sep_vc_t /*vc*/, const device_addr_t& xport_args) + { + // Create a new source endpoint and EPID + sep_addr_t sw_epid_addr(_my_device_id, SEP_INST_DATA_BASE + (_data_ep_inst++)); + sep_id_t src_epid = _epid_alloc->allocate_epid(sw_epid_addr); + _allocated_epids.insert(src_epid); + _ensure_ep_is_reachable(dst_addr); + + // Generate a new destination EPID instance + sep_id_t dst_epid = _epid_alloc->allocate_epid(dst_addr); + + // Create the data transport that we will return to the client + chdr_data_xport_t xport = + _mb_iface.make_data_transport(_my_device_id, src_epid, xport_args); + xport.src_epid = src_epid; + xport.dst_epid = dst_epid; + + // Create new temporary management portal with the transports used for this stream + // TODO: This is a bit excessive. Maybe we can pair down the functionality of the + // portal just for route setup purposes. Whatever we do, we *must* use xport in it + // though otherwise the transport will not behave correctly. + mgmt_portal::uptr data_mgmt_portal = + mgmt_portal::make(xport, _pkt_factory, sw_epid_addr, src_epid); + + // Setup a route to the EPID + data_mgmt_portal->initialize_endpoint(dst_addr, dst_epid); + data_mgmt_portal->setup_local_route(dst_epid); + if (!_mgmt_portal->get_endpoint_info(dst_epid).has_data) { + throw uhd::rfnoc_error("Downstream endpoint does not support data traffic"); + } + + // TODO: Implement data transport setup logic here + + + // Delete the portal when done + data_mgmt_portal.reset(); + return xport; + } + +private: + void _ensure_ep_is_reachable(const sep_addr_t& ep_addr_) + { + for (const auto& ep_addr : _mgmt_portal->get_reachable_endpoints()) { + if (ep_addr == ep_addr_) + return; + } + throw uhd::routing_error("Specified endpoint is not reachable"); + } + + // A reference to the packet factor + const chdr::chdr_packet_factory& _pkt_factory; + // The device address of this software endpoint + const device_id_t _my_device_id; + + // Motherboard interface + mb_iface& _mb_iface; + // A pointer to the EPID allocator + epid_allocator::sptr _epid_alloc; + // A set of all allocated EPIDs + std::set<sep_id_t> _allocated_epids; + // The software EPID for all management and control traffic + sep_id_t _my_mgmt_ctrl_epid; + // Transports + muxed_zero_copy_if::sptr _muxed_xport; + chdr_ctrl_xport_t _ctrl_xport; + // Management portal for control endpoints + mgmt_portal::uptr _mgmt_portal; + // The CHDR control endpoint + chdr_ctrl_endpoint::uptr _ctrl_ep; + // A map of all client zero instances indexed by the destination + std::map<sep_id_t, client_zero::sptr> _client_zero_map; + // Data endpoint instance + sep_inst_t _data_ep_inst; +}; + +link_stream_manager::uptr link_stream_manager::make( + const chdr::chdr_packet_factory& pkt_factory, + mb_iface& mb_if, + const epid_allocator::sptr& epid_alloc, + device_id_t device_id) +{ + return std::make_unique<link_stream_manager_impl>( + pkt_factory, mb_if, epid_alloc, device_id); +} |