diff options
| author | Martin Braun <martin.braun@ettus.com> | 2019-04-24 18:23:31 -0700 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:14 -0800 | 
| commit | c97bdc6c94c98753215a90cf499af4bdf06db8e2 (patch) | |
| tree | 67f623ae84acb045d145bd22036df60a1724b789 /host/lib/include/uhdlib | |
| parent | f0371292a43c3e4e3c68d8631c57d64ab10faf4c (diff) | |
| download | uhd-c97bdc6c94c98753215a90cf499af4bdf06db8e2.tar.gz uhd-c97bdc6c94c98753215a90cf499af4bdf06db8e2.tar.bz2 uhd-c97bdc6c94c98753215a90cf499af4bdf06db8e2.zip  | |
rfnoc: Add property propagation, Boost.Graph storage
- Adds a detail::graph_t class, which handles the propagation
- Adds methods to node_t to aid with propagation
- Adds unit tests
- Adds dynamic property forwarding:
  Nodes are now able to forward properties they don't know about by
  providing a forwarding policy. A good example is the FIFO block which
  simply forwards most properties verbatim.
- node: Temporarily disabling consistency check at init
Diffstat (limited to 'host/lib/include/uhdlib')
| -rw-r--r-- | host/lib/include/uhdlib/rfnoc/graph.hpp | 271 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/rfnoc/node_accessor.hpp | 5 | 
2 files changed, 274 insertions, 2 deletions
diff --git a/host/lib/include/uhdlib/rfnoc/graph.hpp b/host/lib/include/uhdlib/rfnoc/graph.hpp new file mode 100644 index 000000000..f9fb7ac41 --- /dev/null +++ b/host/lib/include/uhdlib/rfnoc/graph.hpp @@ -0,0 +1,271 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_GRAPH_HPP +#define INCLUDED_LIBUHD_GRAPH_HPP + +#include <uhd/rfnoc/node.hpp> +#include <boost/graph/adjacency_list.hpp> +#include <tuple> +#include <memory> + +namespace uhd { namespace rfnoc { + +/*! A container that holds information about a graph edge + */ +struct graph_edge_t +{ +    enum edge_t { +        STATIC, ///< A static connection between two blocks in the FPGA +        DYNAMIC, ///< A user (dynamic) connection between two blocks in the FPGA +        RX_STREAM, ///< A connection from an FPGA block to a software RX streamer +        TX_STREAM ///< A connection from a software TX streamer and an FPGA block +    }; + +    graph_edge_t() = default; + +    graph_edge_t(const size_t src_port_, +        const size_t dst_port_, +        const edge_t edge_, +        const bool ppa) +        : src_port(src_port_) +        , dst_port(dst_port_) +        , edge(edge_) +        , property_propagation_active(ppa) +    { +    } + +    //! The block ID of the source block for this edge +    std::string src_blockid; +    //! The port number of the source block for this edge +    size_t src_port = 0; +    //! The block ID of the destination block for this edge +    std::string dst_blockid; +    //! The port number of the destination block for this edge +    size_t dst_port = 0; +    //! The type of edge +    edge_t edge = DYNAMIC; +    //! When true, the framework will use this edge for property propagation +    bool property_propagation_active = true; + +    bool operator==(const graph_edge_t& rhs) const +    { +        return std::tie(src_blockid, +                   src_port, +                   dst_blockid, +                   dst_port, +                   edge, +                   property_propagation_active) +               == std::tie(rhs.src_blockid, +                      rhs.src_port, +                      rhs.dst_blockid, +                      rhs.dst_port, +                      rhs.edge, +                      rhs.property_propagation_active); +    } +}; + + +namespace detail { + +//! Container for the logical graph within an uhd::rfnoc_graph +class graph_t +{ +public: +    using uptr = std::unique_ptr<graph_t>; +    //! A shorthand for a pointer to a node +    using node_ref_t = uhd::rfnoc::node_t*; + +    using graph_edge_t = uhd::rfnoc::graph_edge_t; + +    /*! Add a connection to the graph +     * +     * After this function returns, the nodes will be considered connected +     * along the ports specified in \p edge_info. +     * +     * \param src_node A reference to the source node +     * \param dst_node A reference to the destination node +     * \param edge_info Information about the type of edge +     */ +    void connect(node_ref_t src_node, node_ref_t dst_node, graph_edge_t edge_info); + +    //void disconnect(node_ref_t src_node, +        //node_ref_t dst_node, +        //const size_t src_port, +        //const size_t dst_port); +        // + +    /*! Run initial checks for graph +     * +     * This method can be called anytime, but it's intended to be called when +     * the graph has been committed. It will run checks on the graph and run a +     * property propagation. +     * +     * \throws uhd::resolve_error if the properties fail to resolve. +     */ +    void initialize(); + + +private: +    friend class graph_accessor_t; + +    /************************************************************************** +     * Graph-related types +     *************************************************************************/ +    // Naming conventions: +    // - 'vertex' and 'node' are generally ambiguous in a graph context, but +    //   we'll use vertex for BGL related items, and node for RFNoC nodes +    // - We may use CamelCase occasionally if it fits the BGL examples and/or +    //   reference designs, in case someone needs to learn BGL to understand +    //   this code + +    struct vertex_property_t +    { +        enum { num = 4000 }; +        typedef boost::vertex_property_tag kind; +    }; +    using RfnocVertexProperty = boost::property<vertex_property_t, node_ref_t>; + +    struct edge_property_t +    { +        enum { num = 4001 }; +        typedef boost::edge_property_tag kind; +    }; +    using RfnocEdgeProperty = boost::property<edge_property_t, graph_edge_t>; + +    /*! The type of the BGL graph we're using +     * +     * - It is bidirectional because we need to access both in_edges and +     *   out_edges +     * - All container types are according to the BGL manual recommendations for +     *   this kind of graph +     */ +    using rfnoc_graph_t = boost::adjacency_list<boost::vecS, +        boost::vecS, +        boost::bidirectionalS, +        RfnocVertexProperty, +        RfnocEdgeProperty>; + +    using vertex_list_t = std::list<rfnoc_graph_t::vertex_descriptor>; + +    template <bool forward_edges_only = true> +    struct ForwardBackwardEdgePredicate +    { +        ForwardBackwardEdgePredicate() {} // Default ctor is required +        ForwardBackwardEdgePredicate(rfnoc_graph_t& graph) : _graph(&graph) {} + +        template <typename Edge> +        bool operator()(const Edge& e) const +        { +            graph_edge_t edge_info = boost::get(edge_property_t(), *_graph, e); +            return edge_info.property_propagation_active == forward_edges_only; +        } + +    private: +        // Don't make any attribute const, because default assignment operator +        // is also required +        rfnoc_graph_t* _graph; +    }; + +    using ForwardEdgePredicate = ForwardBackwardEdgePredicate<true>; +    using BackEdgePredicate = ForwardBackwardEdgePredicate<false>; + +    //! Vertex predicate, only selects nodes with dirty props +    struct DirtyNodePredicate; + +    //! Vertex predicate, returns specific existing nodes +    struct FindNodePredicate; + +    /************************************************************************** +     * Other private types +     *************************************************************************/ +    using node_map_t = std::map<node_ref_t, rfnoc_graph_t::vertex_descriptor>; + +    /************************************************************************** +     * The Algorithm +     *************************************************************************/ +    /*! Implementation of the property propagation algorithm +     */ +    void resolve_all_properties(); + +    /************************************************************************** +     * Private graph helpers +     *************************************************************************/ +    template <typename VertexContainerType> +    std::vector<node_ref_t> _vertices_to_nodes(VertexContainerType&& vertex_container) +    { +        std::vector<node_ref_t> result{}; +        result.reserve(vertex_container.size()); +        for (const auto& vertex_descriptor : vertex_container) { +            result.push_back(boost::get(vertex_property_t(), _graph, vertex_descriptor)); +        } +        return result; +    } + +    /*! Returns a list of all nodes that have dirty properties. +     */ +    vertex_list_t _find_dirty_nodes(); + +    /*! Returns nodes in topologically sorted order +     * +     * +     * \throws uhd::runtime_error if the graph was not sortable +     */ +    vertex_list_t _get_topo_sorted_nodes(); + +    /*! Add a node, but only if it's not already in the graph. +     * +     * If it's already there, do nothing. +     */ +    void _add_node(node_ref_t node); + +    /*! Find the neighbouring node for \p origin based on \p port_info +     * +     * This function will check port_info to identify the port number and the +     * direction (input or output) from \p port_info. It will then return a +     * reference to the node that is attached to the node \p origin if such a +     * node exists, and the edge info. +     * +     * If port_info.type == res_source_info::INPUT_EDGE, then port_info.instance +     * will equal the return value's dst_port value. +     * +     * \returns A valid reference to the neighbouring node, or nullptr if no +     *          such node exists, and the corresponding edge info. +     */ +    std::pair<node_ref_t, graph_edge_t> _find_neighbour( +        rfnoc_graph_t::vertex_descriptor origin, res_source_info port_info); + +    /*! Forward all edge properties from this node (\p origin) to the +     * neighbouring ones +     * +     */ +    void _forward_edge_props(rfnoc_graph_t::vertex_descriptor origin); + +    /*! Check that the edge properties on both sides of the edge are equal +     * +     * \returns false if edge properties are not consistent +     */ +    bool _assert_edge_props_consistent(rfnoc_graph_t::edge_descriptor edge); + +    /************************************************************************** +     * Attributes +     *************************************************************************/ +    //! Storage for the actual graph +    rfnoc_graph_t _graph; + +    //! Map to do a lookup node_ref_t -> vertex descriptor. +    // +    // This is technically redundant, but helps us check quickly and easily if +    // a node is already in the graph, and to yank out the appropriate node +    // descriptor without having to traverse the graph. The rfnoc_graph_t is not +    // efficient for lookups of vertices. +    node_map_t _node_map; +}; + + +}}} /* namespace uhd::rfnoc::detail */ + +#endif /* INCLUDED_LIBUHD_GRAPH_HPP */ diff --git a/host/lib/include/uhdlib/rfnoc/node_accessor.hpp b/host/lib/include/uhdlib/rfnoc/node_accessor.hpp index 26e6a5607..554cc8f4f 100644 --- a/host/lib/include/uhdlib/rfnoc/node_accessor.hpp +++ b/host/lib/include/uhdlib/rfnoc/node_accessor.hpp @@ -72,9 +72,10 @@ public:       *       * See node_t::forward_edge_property() for details.       */ -    void forward_edge_property(node_t* dst_node, property_base_t* incoming_prop) +    void forward_edge_property( +        node_t* dst_node, const size_t dst_port, property_base_t* incoming_prop)      { -        dst_node->forward_edge_property(incoming_prop); +        dst_node->forward_edge_property(incoming_prop, dst_port);      }  };  | 
