diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-04-30 18:26:52 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:17 -0800 |
commit | fb0175a89ff85456f0efa59e726cf3abe1a87967 (patch) | |
tree | 251c585f38d984d37bbe0c6d82ffb063bb527350 /host | |
parent | 636dad30696b68fc26bb91f370873fb3f938b020 (diff) | |
download | uhd-fb0175a89ff85456f0efa59e726cf3abe1a87967.tar.gz uhd-fb0175a89ff85456f0efa59e726cf3abe1a87967.tar.bz2 uhd-fb0175a89ff85456f0efa59e726cf3abe1a87967.zip |
rfnoc: Add rfnoc_graph class
This replaces device3() for RFNoC applications.
Diffstat (limited to 'host')
-rw-r--r-- | host/include/uhd/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/include/uhd/rfnoc_graph.hpp | 233 | ||||
-rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/rfnoc/rfnoc_graph.cpp | 160 |
4 files changed, 395 insertions, 0 deletions
diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index 5bb736567..cdee1c921 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -25,6 +25,7 @@ UHD_INSTALL(FILES exception.hpp property_tree.ipp property_tree.hpp + rfnoc_graph.hpp stream.hpp ${CMAKE_CURRENT_BINARY_DIR}/version.hpp DESTINATION ${INCLUDE_DIR}/uhd diff --git a/host/include/uhd/rfnoc_graph.hpp b/host/include/uhd/rfnoc_graph.hpp new file mode 100644 index 000000000..bdbac69f5 --- /dev/null +++ b/host/include/uhd/rfnoc_graph.hpp @@ -0,0 +1,233 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOCDEV_GRAPH_HPP +#define INCLUDED_LIBUHD_RFNOCDEV_GRAPH_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/utils/noncopyable.hpp> +#include <uhd/rfnoc/graph_edge.hpp> +#include <uhd/rfnoc/block_id.hpp> +#include <uhd/rfnoc/noc_block_base.hpp> +#include <uhd/types/device_addr.hpp> +#include <boost/units/detail/utility.hpp> // for demangle +#include <memory> +#include <vector> + +namespace uhd { namespace rfnoc { + +/*! The core class for a UHD session with (an) RFNoC device(s) + * + * This class is a superset of uhd::device. It does not only hold a device + * session, but also manages the RFNoC blocks on those devices. Only devices + * compatible with a modern version of RFNoC can be addressed by this class. + */ +class UHD_API rfnoc_graph : public uhd::noncopyable +{ +public: + /*! A shared pointer to allow easy access to this class and for + * automatic memory management. + */ + using sptr = std::shared_ptr<rfnoc_graph>; + + + virtual ~rfnoc_graph() {} + + /****************************************** + * Factory + ******************************************/ + /*! Make a new USRP graph from the specified device address(es). + * + * \param dev_addr the device address + * \return A new rfnoc_graph object + * + * \throws uhd::key_error no device found + * \throws uhd::index_error fewer devices found than expected + */ + static sptr make(const device_addr_t& dev_addr); + + /****************************************** + * Block Discovery/Retrieval + ******************************************/ + /*! Returns the block ids of all blocks that match the specified hint + * Uses block_id_t::match() internally. + * If no matching block is found, it returns an empty vector. + * + * To access specialized block controller classes (i.e. derived from noc_block_base), + * use the templated version of this function, e.g. + * \code{.cpp} + * // Assume DEV is an rfnoc_graph::sptr + * auto null_blocks = DEV->find_blocks<null_noc_block>("NullSrcSink"); + * if (null_blocks.empty()) { cout << "No null blocks found!" << endl; } + * \endcode + * \note this access is not thread safe if performed during block enumeration + */ + virtual std::vector<block_id_t> find_blocks(const std::string& block_id_hint) const = 0; + + /*! Type-cast version of find_blocks(). + */ + template <typename T> + std::vector<block_id_t> find_blocks(const std::string& block_id_hint) const + { + std::vector<block_id_t> all_block_ids = find_blocks(block_id_hint); + std::vector<block_id_t> filt_block_ids; + for (size_t i = 0; i < all_block_ids.size(); i++) { + if (has_block<T>(all_block_ids[i])) { + filt_block_ids.push_back(all_block_ids[i]); + } + } + return filt_block_ids; + } + + /*! \brief Checks if a specific NoC block exists on the device. + * + * \param block_id Canonical block name (e.g. "0/FFT#1"). + * \return true if a block with the specified id exists + * \note this access is not thread safe if performed during block enumeration + */ + virtual bool has_block(const block_id_t& block_id) const = 0; + + /*! Same as has_block(), but with a type check. + * + * \return true if a block of type T with the specified id exists + * \note this access is not thread safe if performed during block enumeration + */ + template <typename T> + bool has_block(const block_id_t& block_id) const + { + return has_block(block_id) + && bool(std::dynamic_pointer_cast<T>(get_block(block_id))); + } + + /*! \brief Returns a block controller class for an NoC block. + * + * If the given block ID is not valid (i.e. such a block does not exist + * on this device), it will throw a uhd::lookup_error. + * + * \param block_id Canonical block name (e.g. "0/FFT#1"). + * \note this access is not thread safe if peformed during block enumeration + */ + noc_block_base::sptr get_block(const block_id_t& block_id) const; + + /*! Same as get_block(), but with a type cast. + * + * If you have a block controller class that is derived from noc_block_base, + * use this function to access its specific methods. + * If the given block ID is not valid (i.e. such a block does not exist + * on this device) or if the type does not match, it will throw a uhd::lookup_error. + * + * \code{.cpp} + * // Assume DEV is a device3::sptr + * auto block_controller = get_block<my_noc_block>("0/MyBlock#0"); + * block_controller->my_own_block_method(); + * \endcode + * \note this access is not thread safe if performed during block enumeration + */ + template <typename T> + std::shared_ptr<T> get_block(const block_id_t& block_id) const + { + std::shared_ptr<T> blk = + std::dynamic_pointer_cast<T>(get_block(block_id)); + if (blk) { + return blk; + } else { + throw uhd::lookup_error( + std::string("This device does not have a block of type ") + + boost::units::detail::demangle(typeid(T).name()) + + " with ID: " + block_id.to_string()); + } + } + + /************************************************************************** + * Connection APIs + *************************************************************************/ + /*! Connect a RFNOC block with block ID \p src_block to another with block ID \p + * dst_block. + * + * \param src_blk The block ID of the source block to connect. + * \param src_port The port of the source block to connect. + * \param dst_blk The block ID of the destination block to connect to. + * \param dst_port The port of the destination block to connect to. + * \param skip_property_propagation Skip property propagation for this edge + * + * \throws connect_disallowed_on_src + * if the source port is statically connected to a *different* block + * \throws connect_disallowed_on_dst + * if the destination port is statically connected to a *different* block + */ + void connect(const block_id_t& src_blk, + size_t src_port, + const block_id_t& dst_blk, + size_t dst_port, + bool skip_property_propagation = false); + + /*! Connect TX streamer to an input of an NoC block + * + * \param streamer The streamer to connect. + * \param strm_port The port of the streamer to connect. + * \param dst_blk The block ID of the destination block to connect to. + * \param dst_port The port of the destination block to connect to. + * + * \throws connect_disallowed_on_dst + * if the destination port is statically connected to a *different* block + */ + void connect(uhd::tx_streamer& streamer, + size_t strm_port, + const block_id_t& dst_blk, + size_t dst_port); + + /*! Connect RX streamer to an output of an NoC block + * + * \param src_blk The block ID of the destination block to connect to. + * \param src_port The port of the destination block to connect to. + * \param streamer The streamer to connect. + * \param strm_port The port of the streamer to connect. + * + * \throws connect_disallowed_on_src + * if the source port is statically connected to a *different* block + */ + void connect(const block_id_t& src_blk, + size_t src_port, + uhd::rx_streamer& streamer, + size_t strm_port); + + /*! Enumerate all the connections in the graph + * + * \return A vector containing all the edges in the graph. + */ + std::vector<graph_edge_t> enumerate_connections(); + + /****************************************** + * Streaming + ******************************************/ + + /*! Create a new receive streamer from the streamer arguments + * The created streamer is still not connected to anything yet. + * The graph::connect call has to be made on this streamer to + * start using it. If a different streamer is already connected + * to the intended source then that call may fail. + * + * \param args Arguments to aid the construction of the streamer + * \return a shared pointer to a new streamer + */ + //virtual rx_streamer::sptr create_rx_streamer(const stream_args_t& args) = 0; + + /*! Create a new transmit streamer from the streamer arguments + * The created streamer is still not connected to anything yet. + * The graph::connect call has to be made on this streamer to + * start using it. If a different streamer is already connected + * to the intended sink then that call may fail. + * + * \param args Arguments to aid the construction of the streamer + * \return a shared pointer to a new streamer + */ + //virtual tx_streamer::sptr create_tx_streamer(const stream_args_t& args) = 0; +}; // class rfnoc_graph + +}}; // namespace uhd::rfnoc + +#endif /* INCLUDED_LIBUHD_RFNOCDEV_GRAPH_HPP */ diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt index 57a5253a7..1cf4a4280 100644 --- a/host/lib/rfnoc/CMakeLists.txt +++ b/host/lib/rfnoc/CMakeLists.txt @@ -32,6 +32,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/ctrlport_endpoint.cpp ${CMAKE_CURRENT_SOURCE_DIR}/chdr_ctrl_endpoint.cpp ${CMAKE_CURRENT_SOURCE_DIR}/registry_factory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rfnoc_graph.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rx_stream_terminator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scalar_node_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sink_block_ctrl_base.cpp diff --git a/host/lib/rfnoc/rfnoc_graph.cpp b/host/lib/rfnoc/rfnoc_graph.cpp new file mode 100644 index 000000000..fef2ccccb --- /dev/null +++ b/host/lib/rfnoc/rfnoc_graph.cpp @@ -0,0 +1,160 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/node.hpp> +#include <uhd/rfnoc_graph.hpp> +#include <uhdlib/rfnoc/block_container.hpp> +#include <uhdlib/rfnoc/graph.hpp> +#include <uhdlib/rfnoc/rfnoc_device.hpp> +#include <boost/shared_ptr.hpp> // FIXME remove when rfnoc_device is ready + +using namespace uhd::rfnoc; + + +class rfnoc_graph_impl : public rfnoc_graph +{ +public: + /************************************************************************** + * Structors + *************************************************************************/ + rfnoc_graph_impl(const uhd::device_addr_t& dev_addr) + { + setup_graph(dev_addr); + } + + ~rfnoc_graph_impl() + { + _graph.reset(); + } + + /************************************************************************** + * Block Discovery/Retrieval + *************************************************************************/ + std::vector<block_id_t> find_blocks(const std::string& block_id_hint) const + { + return _block_registry->find_blocks(block_id_hint); + } + + bool has_block(const block_id_t& block_id) const + { + return _block_registry->has_block(block_id); + } + + noc_block_base::sptr get_block(const block_id_t& block_id) const + { + return _block_registry->get_block(block_id); + } + + /************************************************************************** + * Graph Connections + *************************************************************************/ + void connect(const block_id_t& src_blk, + size_t src_port, + const block_id_t& dst_blk, + size_t dst_port, + bool skip_property_propagation) + { + if (!has_block(src_blk)) { + throw uhd::lookup_error( + std::string("Cannot connect blocks, source block not found: ") + + src_blk.to_string()); + } + if (!has_block(dst_blk)) { + throw uhd::lookup_error( + std::string("Cannot connect blocks, source block not found: ") + + src_blk.to_string()); + } + _connect(get_block(src_blk), + src_port, + get_block(dst_blk), + dst_port, + skip_property_propagation); + } + + void connect(uhd::tx_streamer& /*streamer*/, + size_t /*strm_port*/, + const block_id_t& /*dst_blk*/, + size_t /*dst_port*/) + { + throw uhd::not_implemented_error(""); + } + + void connect(const block_id_t& /*src_blk*/, + size_t /*src_port*/, + uhd::rx_streamer& /*streamer*/, + size_t /*strm_port*/) + { + throw uhd::not_implemented_error(""); + } + +private: + /************************************************************************** + * Device Setup + *************************************************************************/ + void setup_graph(const uhd::device_addr_t& dev_addr) + { + // Phase I: Initialize the motherboards + auto dev = uhd::device::make(dev_addr); + _device = boost::dynamic_pointer_cast<detail::rfnoc_device>(dev); + if (!_device) { + throw uhd::key_error(std::string("Found no RFNoC devices for ----->\n") + + dev_addr.to_pp_string()); + } + + // Configure endpoint_manager, make sure all routes are established + // FIXME + + // Enumerate blocks, load them into the block registry + // FIXME + + // Create graph, connect all static routes + // FIXME + } + + + /************************************************************************** + * Helpers + *************************************************************************/ + /*! Internal connection helper + * + * Prerequisite: \p src_blk and \p dst_blk need to point to valid nodes + */ + void _connect(std::shared_ptr<node_t> src_blk, + size_t src_port, + std::shared_ptr<node_t> dst_blk, + size_t dst_port, + bool skip_property_propagation) + { + graph_edge_t edge_info( + src_port, dst_port, graph_edge_t::DYNAMIC, not skip_property_propagation); + edge_info.src_blockid = src_blk->get_unique_id(); + edge_info.dst_blockid = dst_blk->get_unique_id(); + _graph->connect(src_blk.get(), dst_blk.get(), edge_info); + } + + + /************************************************************************** + * Attributes + *************************************************************************/ + //! Reference to the underlying device implementation + detail::rfnoc_device::sptr _device; + + //! Registry for the blocks (it's a separate class) + std::unique_ptr<detail::block_container_t> _block_registry; + + //! Reference to the graph + std::unique_ptr<detail::graph_t> _graph; + +}; /* class rfnoc_graph_impl */ + + +/****************************************************************************** + * Factory + *****************************************************************************/ +rfnoc_graph::sptr rfnoc_graph::make(const uhd::device_addr_t& device_addr) +{ + return std::make_shared<rfnoc_graph_impl>(device_addr); +} |