diff options
| -rw-r--r-- | host/include/uhd/rfnoc/defaults.hpp | 1 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/noc_block_base.hpp | 61 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/noc_block_make_args.hpp | 3 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/node.hpp | 9 | ||||
| -rw-r--r-- | host/lib/rfnoc/ddc_block_control.cpp | 1 | ||||
| -rw-r--r-- | host/lib/rfnoc/noc_block_base.cpp | 114 | ||||
| -rw-r--r-- | host/lib/rfnoc/radio_control_impl.cpp | 12 | ||||
| -rw-r--r-- | host/lib/rfnoc/rfnoc_graph.cpp | 2 | ||||
| -rw-r--r-- | host/tests/rfnoc_blocks_test.cpp | 21 | 
9 files changed, 218 insertions, 6 deletions
diff --git a/host/include/uhd/rfnoc/defaults.hpp b/host/include/uhd/rfnoc/defaults.hpp index e1046ada2..99ac9791f 100644 --- a/host/include/uhd/rfnoc/defaults.hpp +++ b/host/include/uhd/rfnoc/defaults.hpp @@ -21,6 +21,7 @@ static const std::string PROP_KEY_TYPE("type");  static const std::string PROP_KEY_FREQ("freq");  static const std::string PROP_KEY_TICK_RATE("tick_rate");  static const std::string PROP_KEY_SPP("spp"); +static const std::string PROP_KEY_MTU("mtu");  static const std::string NODE_ID_SEP("SEP"); diff --git a/host/include/uhd/rfnoc/noc_block_base.hpp b/host/include/uhd/rfnoc/noc_block_base.hpp index 485b2c8a3..4a8d2965f 100644 --- a/host/include/uhd/rfnoc/noc_block_base.hpp +++ b/host/include/uhd/rfnoc/noc_block_base.hpp @@ -98,6 +98,20 @@ public:       */      double get_tick_rate() const; +    /*! Return the current MTU on a given edge +     * +     * The MTU is determined by the block itself (i.e., how big of a packet can +     * this block handle on this edge), but also the neighboring block, and +     * possibly the transport medium between the blocks. This value can thus be +     * lower than what the block defines as MTU, but never higher. +     * +     * \param edge The edge on which the MTU is queried. edge.type must be +     *             INPUT_EDGE or OUTPUT_EDGE! +     * \returns the MTU as determined by the overall graph on this edge +     * \throws uhd::value_error if edge is not referring to a valid edge +     */ +    size_t get_mtu(const res_source_info& edge); +      /*! Return the arguments that were passed into this block from the framework       */      uhd::device_addr_t get_block_args() const { return _block_args; } @@ -143,6 +157,43 @@ protected:       */      void set_tick_rate(const double tick_rate); +    /*! Change the way MTUs are forwarded +     * +     * The policy will have the following effect: +     * - DROP: This means that the MTU of one port has no bearing on the MTU +     *   of another port. This is usually a valid choice if the FPGA is +     *   repacking data, for example, a block could be consuming continous +     *   streams of data, and producing small packets of a different type. +     * - ONE_TO_ONE: This means the MTU is passed through from input to output +     *   and vice versa. This is typically a good choice if packets are being +     *   passed through without modifying their size. The DDC/DUC blocks will +     *   choose this policy, because the want to relay MTU information to the +     *   radio. +     * - ONE_TO_ALL: This means the MTU is being set to the same value on all +     *   ports. +     * - ONE_TO_FAN: This means the MTU is forwarded from any input port to +     *   all opposite side ports. This is an appropriate policy for the +     *   split-stream block. +     * +     * The default policy is DROP. +     */ +    void set_mtu_forwarding_policy(const forwarding_policy_t policy); + +    /*! Update the MTU +     * +     * This is another data point in the MTU discovery process. This means that +     * the MTU cannot be increased using the method, only decreased. +     */ +    void set_mtu(const res_source_info& edge, const size_t new_mtu); + +    /*! Return a reference to an MTU property +     * +     * This can be used to make the MTU an input to a property resolver. For +     * example, blocks that have an spp property, such as the radio, can now +     * trigger a property resolver based on the MTU. +     */ +    property_base_t* get_mtu_prop_ref(const res_source_info& edge); +      /*! Get access to the motherboard controller for this block's motherboard       *       * This will return a nullptr if this block doesn't have access to the @@ -211,6 +262,16 @@ private:      // for all in- and output edges.      std::vector<property_t<double>> _tick_rate_props; +    //! Forwarding policy for the MTU properties +    forwarding_policy_t _mtu_fwd_policy = forwarding_policy_t::DROP; + +    //! Container for the 'mtu' property. This will hold one edge property +    // for all in- and output edges. +    std::vector<property_t<size_t> > _mtu_props; + +    //! The actual MTU value +    std::unordered_map<res_source_info, size_t> _mtu; +      //! Reference to the ctrlport clock_iface object shared with the register_iface      std::shared_ptr<clock_iface> _ctrlport_clock_iface; diff --git a/host/include/uhd/rfnoc/noc_block_make_args.hpp b/host/include/uhd/rfnoc/noc_block_make_args.hpp index 7ed191079..8878cc1a8 100644 --- a/host/include/uhd/rfnoc/noc_block_make_args.hpp +++ b/host/include/uhd/rfnoc/noc_block_make_args.hpp @@ -37,6 +37,9 @@ struct noc_block_base::make_args_t      //! Number of output ports (gets reported from the FPGA)      size_t num_output_ports; +    //! Value of the MTU register +    size_t mtu; +      //! Register interface to this block's register space      register_iface::sptr reg_iface; diff --git a/host/include/uhd/rfnoc/node.hpp b/host/include/uhd/rfnoc/node.hpp index eaeea20af..59836fbf6 100644 --- a/host/include/uhd/rfnoc/node.hpp +++ b/host/include/uhd/rfnoc/node.hpp @@ -217,6 +217,15 @@ protected:       *   property access is not violated. All properties can be read during       *   execution, but only properties in the \p outputs list can be written       *   to. +     * - Resolvers are stored and executed in the same order they are added. +     *   That is to say, if two resolvers both share a condition that will +     *   trigger them, the first resolver to be added will be the first resolver +     *   to be executed. This allows to make some assumptions on the order of +     *   execution, in case resolvers have dependencies. +     * - This method has no built-in thread safety, since it is typically only +     *   called in the constructor. If resolvers need to be added at runtime +     *   (which is considered advanced usage), then the block needs to serialize +     *   access to this function itself.       *       * \param inputs The properties that will cause this resolver to run       * \param outputs The properties that this resolver will write to diff --git a/host/lib/rfnoc/ddc_block_control.cpp b/host/lib/rfnoc/ddc_block_control.cpp index 0c2721833..37286b29d 100644 --- a/host/lib/rfnoc/ddc_block_control.cpp +++ b/host/lib/rfnoc/ddc_block_control.cpp @@ -71,6 +71,7 @@ public:                                              << " halfbands and "                                                 "max CIC decimation "                                              << _cic_max_decim); +        set_mtu_forwarding_policy(forwarding_policy_t::ONE_TO_ONE);          // Load list of valid decimation values          std::set<size_t> decims{1}; // 1 is always a valid decimatino          for (size_t hb = 0; hb < _num_halfbands; hb++) { diff --git a/host/lib/rfnoc/noc_block_base.cpp b/host/lib/rfnoc/noc_block_base.cpp index 68093d9b1..8f76ac1d1 100644 --- a/host/lib/rfnoc/noc_block_base.cpp +++ b/host/lib/rfnoc/noc_block_base.cpp @@ -48,14 +48,14 @@ noc_block_base::noc_block_base(make_args_ptr make_args)              {res_source_info::OUTPUT_EDGE, output_port}));      }      // Register all the tick_rate properties and create a default resolver -    prop_ptrs_t prop_refs; -    prop_refs.reserve(_tick_rate_props.size()); +    prop_ptrs_t tick_rate_prop_refs; +    tick_rate_prop_refs.reserve(_tick_rate_props.size());      for (auto& prop : _tick_rate_props) { -        prop_refs.insert(&prop); +        tick_rate_prop_refs.insert(&prop);          register_property(&prop);      }      for (auto& prop : _tick_rate_props) { -        auto prop_refs_copy = prop_refs; +        auto prop_refs_copy = tick_rate_prop_refs;          add_property_resolver(              {&prop}, std::move(prop_refs_copy), [this, source_prop = &prop]() {                  // _set_tick_rate() will update _tick_rate, but only if that's @@ -68,6 +68,68 @@ noc_block_base::noc_block_base(make_args_ptr make_args)                  }              });      } +    // Now, the same thing for MTU props +    // Create one mtu property for every port +    _mtu_props.reserve(_num_input_ports + _num_output_ports); +    for (size_t input_port = 0; input_port < _num_input_ports; input_port++) { +        _mtu_props.push_back(property_t<size_t>( +            PROP_KEY_MTU, make_args->mtu, {res_source_info::INPUT_EDGE, input_port})); +        _mtu.insert({{res_source_info::INPUT_EDGE, input_port}, make_args->mtu}); +    } +    for (size_t output_port = 0; output_port < _num_output_ports; output_port++) { +        _mtu_props.push_back(property_t<size_t>( +            PROP_KEY_MTU, make_args->mtu, {res_source_info::OUTPUT_EDGE, output_port})); +        _mtu.insert({{res_source_info::OUTPUT_EDGE, output_port}, make_args->mtu}); +    } +    // Register all the mtu properties and create a default resolver +    prop_ptrs_t mtu_prop_refs; +    mtu_prop_refs.reserve(_mtu_props.size()); +    for (auto& prop : _mtu_props) { +        mtu_prop_refs.insert(&prop); +        register_property(&prop); +    } +    for (auto& prop : _mtu_props) { +        auto prop_refs_copy = mtu_prop_refs; +        add_property_resolver( +            {&prop}, std::move(prop_refs_copy), [this, source_prop = &prop]() { +                const res_source_info src_edge = source_prop->get_src_info(); +                // First, coerce the MTU to its appropriate min value +                const size_t new_mtu = std::min(source_prop->get(), _mtu.at(src_edge)); +                source_prop->set(new_mtu); +                _mtu.at(src_edge) = source_prop->get(); +                RFNOC_LOG_TRACE("MTU is now " << _mtu.at(src_edge) << " on edge " +                                              << src_edge.to_string()); +                auto update_pred = [src_edge, fwd_policy = _mtu_fwd_policy]( +                                       const res_source_info& mtu_src) -> bool { +                    switch (fwd_policy) { +                        case forwarding_policy_t::DROP: +                            return false; +                        case forwarding_policy_t::ONE_TO_ONE: +                            return res_source_info::invert_edge(mtu_src.type) +                                       == src_edge.type +                                   && mtu_src.instance == src_edge.instance; +                        case forwarding_policy_t::ONE_TO_ALL: +                            return mtu_src.type != src_edge.type && mtu_src.instance +                                   && src_edge.instance; +                        case forwarding_policy_t::ONE_TO_FAN: +                            return res_source_info::invert_edge(mtu_src.type) +                                   == src_edge.type; +                        default: +                            UHD_THROW_INVALID_CODE_PATH(); +                    } +                }; + +                for (auto& mtu_prop : _mtu_props) { +                    if (update_pred(mtu_prop.get_src_info()) +                        && mtu_prop.get() != new_mtu) { +                        RFNOC_LOG_TRACE("Forwarding new MTU value to edge " +                                        << mtu_prop.get_src_info().to_string()); +                        mtu_prop.set(new_mtu); +                        _mtu.at(mtu_prop.get_src_info()) = mtu_prop.get(); +                    } +                } +            }); +    }  }  noc_block_base::~noc_block_base() @@ -137,6 +199,50 @@ void noc_block_base::_set_tick_rate(const double tick_rate)      }  } +void noc_block_base::set_mtu_forwarding_policy(const forwarding_policy_t policy) +{ +    if (policy == forwarding_policy_t::DROP || policy == forwarding_policy_t::ONE_TO_ONE +        || policy == forwarding_policy_t::ONE_TO_ALL +        || policy == forwarding_policy_t::ONE_TO_FAN) { +        _mtu_fwd_policy = policy; +        return; +    } +    RFNOC_LOG_ERROR("Setting invalid MTU forwarding policy!"); +    throw uhd::value_error("MTU forwarding policy must be either DROP, ONE_TO_ONE, " +                           "ONE_TO_ALL, or ONE_TO_FAN!"); +} + +void noc_block_base::set_mtu(const res_source_info& edge, const size_t new_mtu) +{ +    if (edge.type != res_source_info::INPUT_EDGE +        && edge.type != res_source_info::OUTPUT_EDGE) { +        throw uhd::value_error( +            "set_mtu() must be called on either an input or output edge!"); +    } +    set_property<size_t>(PROP_KEY_MTU, new_mtu, edge); +} + + +size_t noc_block_base::get_mtu(const res_source_info& edge) +{ +    if (!_mtu.count(edge)) { +        throw uhd::value_error( +            std::string("Cannot get MTU on edge: ") + edge.to_string()); +    } +    return _mtu.at(edge); +} + +property_base_t* noc_block_base::get_mtu_prop_ref(const res_source_info& edge) +{ +    for (size_t mtu_prop_idx = 0; mtu_prop_idx < _mtu_props.size(); mtu_prop_idx++) { +        if (_mtu_props.at(mtu_prop_idx).get_src_info() == edge) { +            return &_mtu_props.at(mtu_prop_idx); +        } +    } +    throw uhd::value_error( +        std::string("Could not find MTU property for edge: ") + edge.to_string()); +} +  void noc_block_base::shutdown()  {      RFNOC_LOG_TRACE("Calling deinit()"); diff --git a/host/lib/rfnoc/radio_control_impl.cpp b/host/lib/rfnoc/radio_control_impl.cpp index 4ed0c4b60..f71c73289 100644 --- a/host/lib/rfnoc/radio_control_impl.cpp +++ b/host/lib/rfnoc/radio_control_impl.cpp @@ -170,10 +170,20 @@ radio_control_impl::radio_control_impl(make_args_ptr make_args)          register_property(&_samp_rate_out.back());          register_property(&_type_in.back());          register_property(&_type_out.back()); -        add_property_resolver({&_spp_prop.back()}, +        add_property_resolver( +            {&_spp_prop.back(), get_mtu_prop_ref({res_source_info::OUTPUT_EDGE, chan})},              {&_spp_prop.back()},              [this, chan, &spp = _spp_prop.back()]() {                  RFNOC_LOG_TRACE("Calling resolver for spp@" << chan); +                const int mtu = +                    static_cast<int>(get_mtu({res_source_info::OUTPUT_EDGE, chan})); +                const int max_spp_per_mtu = mtu / (_samp_width / 8) - (mtu % _spc); +                if (spp.get() > max_spp_per_mtu) { +                    RFNOC_LOG_WARNING("spp value " << spp.get() << " exceeds MTU of " +                                                   << mtu << "! Coercing to " +                                                   << max_spp_per_mtu); +                    spp = max_spp_per_mtu; +                }                  if (spp.get() % _spc) {                      spp = spp.get() - (spp.get() % _spc);                      RFNOC_LOG_WARNING( diff --git a/host/lib/rfnoc/rfnoc_graph.cpp b/host/lib/rfnoc/rfnoc_graph.cpp index bf49ca28b..511e61f05 100644 --- a/host/lib/rfnoc/rfnoc_graph.cpp +++ b/host/lib/rfnoc/rfnoc_graph.cpp @@ -409,6 +409,8 @@ private:              make_args_uptr->block_id = block_id;              make_args_uptr->num_input_ports    = block_info.num_inputs;              make_args_uptr->num_output_ports   = block_info.num_outputs; +            make_args_uptr->mtu = +                (1 << block_info.data_mtu) * chdr_w_to_bits(mb.get_chdr_w()) / 8;              make_args_uptr->reg_iface          = block_reg_iface;              make_args_uptr->tb_clk_iface       = tb_clk_iface;              make_args_uptr->ctrlport_clk_iface = ctrlport_clk_iface; diff --git a/host/tests/rfnoc_blocks_test.cpp b/host/tests/rfnoc_blocks_test.cpp index caf50f2fa..c5c22f4cc 100644 --- a/host/tests/rfnoc_blocks_test.cpp +++ b/host/tests/rfnoc_blocks_test.cpp @@ -21,6 +21,8 @@ using namespace uhd::rfnoc;  namespace { +constexpr size_t DEFAULT_MTU = 8000; +  noc_block_base::make_args_ptr make_make_args(noc_id_t noc_id,      const std::string& block_id,      const size_t n_inputs, @@ -32,6 +34,7 @@ noc_block_base::make_args_ptr make_make_args(noc_id_t noc_id,      make_args->noc_id             = noc_id;      make_args->num_input_ports    = n_inputs;      make_args->num_output_ports   = n_outputs; +    make_args->mtu                = DEFAULT_MTU;      make_args->reg_iface          = std::make_shared<mock_reg_iface_t>();      make_args->block_id           = block_id;      make_args->ctrlport_clk_iface = std::make_shared<clock_iface>(cp_clock_name); @@ -164,6 +167,7 @@ BOOST_AUTO_TEST_CASE(test_ddc_block)      BOOST_REQUIRE(ddc_reg_iface->write_memory.count(ddc_block_control::SR_DECIM_ADDR));      BOOST_CHECK_EQUAL(          ddc_reg_iface->write_memory.at(ddc_block_control::SR_DECIM_ADDR), 2 << 8 | 5); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::INPUT_EDGE, 0}), DEFAULT_MTU);      // Now plop it in a graph      detail::graph_t graph{}; @@ -183,11 +187,16 @@ BOOST_AUTO_TEST_CASE(test_ddc_block)          "scaling", 1.0, {res_source_info::OUTPUT_EDGE, 0});      mock_source_term.set_edge_property<double>(          "samp_rate", 1.0, {res_source_info::OUTPUT_EDGE, 0}); +    constexpr size_t NEW_MTU = 4000; +    mock_source_term.set_edge_property<size_t>( +        "mtu", NEW_MTU, {res_source_info::OUTPUT_EDGE, 0}); -    UHD_LOG_INFO("TEST", "Creating graph"); +    UHD_LOG_INFO("TEST", "Creating graph...");      graph.connect(&mock_source_term, test_ddc.get(), edge_info);      graph.connect(test_ddc.get(), &mock_sink_term, edge_info); +    UHD_LOG_INFO("TEST", "Committing graph...");      graph.commit(); +    UHD_LOG_INFO("TEST", "Commit complete.");      // We need to set the decimation again, because the rates will screw it      // change it w.r.t. to the previous setting      test_ddc->set_property<int>("decim", TEST_DECIM, 0); @@ -216,5 +225,15 @@ BOOST_AUTO_TEST_CASE(test_ddc_block)      // The frequency word is the phase increment, which will halve. We skirt      // around fixpoint/floating point accuracy issues by using CLOSE.      BOOST_CHECK_CLOSE(double(freq_word_1) / double(freq_word_2), 2.0, 1e-6); + +    UHD_LOG_INFO("TEST", "Testing DDC MTU propagation"); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::INPUT_EDGE, 0}), NEW_MTU); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::OUTPUT_EDGE, 0}), NEW_MTU); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::INPUT_EDGE, 1}), DEFAULT_MTU); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::OUTPUT_EDGE, 1}), DEFAULT_MTU); +    mock_source_term.set_edge_property<size_t>( +        "mtu", NEW_MTU / 2, {res_source_info::OUTPUT_EDGE, 0}); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::INPUT_EDGE, 0}), NEW_MTU / 2); +    BOOST_CHECK_EQUAL(test_ddc->get_mtu({res_source_info::OUTPUT_EDGE, 0}), NEW_MTU / 2);  }  | 
