diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-07-26 15:08:18 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:33 -0800 |
commit | 9b8e4e652c168e76f7cf2ca0a699640961b8e6ce (patch) | |
tree | c7ed65d9898aca0cddcc469b403dfe19c7292609 | |
parent | ab87597e9e76854237b5d78f7d35959b14e9737b (diff) | |
download | uhd-9b8e4e652c168e76f7cf2ca0a699640961b8e6ce.tar.gz uhd-9b8e4e652c168e76f7cf2ca0a699640961b8e6ce.tar.bz2 uhd-9b8e4e652c168e76f7cf2ca0a699640961b8e6ce.zip |
rfnoc: Add MTU tracking
MTUs are now tracked through the framework for all childs of
noc_block_base. Every edge gets an 'mtu' property. MTU can be set and
get either through the prop API, or through new API calls (get_mtu(),
set_mtu()). It is also possible to create custom properties that depend
on the MTU by asking for a reference to the MTU property, and then
adding that to the input list of a property resolver.
The radio_control_impl includes a change in this commit where it sets
the spp based on the MTU.
Blocks can also set an MTU forwarding policy. The DDC block includes a
change in this commit that sets a forwarding policy of ONE_TO_ONE,
meaning that the MTU on an input edge is forwarded to the corresponding
output edge (but not the other edges, as with the tick rate).
-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); } |