aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-07-26 15:08:18 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:33 -0800
commit9b8e4e652c168e76f7cf2ca0a699640961b8e6ce (patch)
treec7ed65d9898aca0cddcc469b403dfe19c7292609
parentab87597e9e76854237b5d78f7d35959b14e9737b (diff)
downloaduhd-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.hpp1
-rw-r--r--host/include/uhd/rfnoc/noc_block_base.hpp61
-rw-r--r--host/include/uhd/rfnoc/noc_block_make_args.hpp3
-rw-r--r--host/include/uhd/rfnoc/node.hpp9
-rw-r--r--host/lib/rfnoc/ddc_block_control.cpp1
-rw-r--r--host/lib/rfnoc/noc_block_base.cpp114
-rw-r--r--host/lib/rfnoc/radio_control_impl.cpp12
-rw-r--r--host/lib/rfnoc/rfnoc_graph.cpp2
-rw-r--r--host/tests/rfnoc_blocks_test.cpp21
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);
}