diff options
Diffstat (limited to 'host/lib')
-rw-r--r-- | host/lib/rfnoc/noc_block_base.cpp | 78 |
1 files changed, 70 insertions, 8 deletions
diff --git a/host/lib/rfnoc/noc_block_base.cpp b/host/lib/rfnoc/noc_block_base.cpp index 4245ea5db..dd9ac2455 100644 --- a/host/lib/rfnoc/noc_block_base.cpp +++ b/host/lib/rfnoc/noc_block_base.cpp @@ -88,6 +88,61 @@ noc_block_base::noc_block_base(make_args_ptr make_args) mtu_prop_refs.insert(&prop); register_property(&prop); } + // If an MTU edge property value changes, this resolver will coerce the + // value to the smaller of the new value or the existing MTU value on that + // edge (`_mtu`). This is default behavior that *must* be present for all + // MTU edge properties on all NoC blocks regardless of the current MTU + // forwarding policy. Yes, this even happens with the default `DROP` + // forwarding policy. + // + // Why is this behavior implemented in its own separate resolver and not + // rolled into `set_mtu_forwarding_policy()`, which is responsible for + // registering resolvers that have accurate output sensitivity lists based + // on the desired MTU forwarding policy? The reason is to allow blocks to + // implement custom MTU forwarding policies, but to ensure that this + // default behavior is always present (remember, this behavior should + // happen regardless of the MTU forwarding policy). + // + // Let's take the DDC block for example, whose MTU forwarding policy is + // `ONE_TO_ONE`. When a DDC block is constructed, `noc_block_base` is + // constructed first because it is a superclass of the DDC block. The + // default MTU property resolvers are added to the list of resolvers for + // the block. Then the DDC block constructor runs, and calls + // `set_mtu_forwarding_policy(ONE_TO_ONE)`. We can't go and remove the + // existing MTU edge property resolvers to modify them, or even modify + // them in place--there's simply no way to do that given that the list of + // property resolvers is private to `node_t` (`noc_block_base`'s + // superclass), and there is no way to modify that list except to add new + // entries to it via `add_property_resolver()`. So calling + // `set_mtu_forwarding_policy()` adds a *new* set of resolvers for each MTU + // property that adds the *additional* behaviors dictated by the forwarding + // policy. + // + // This implies there is now an ordering dependency on how dirty properties + // are resolved. The default coercing resolver *MUST* execute first, to + // determine what the MTU value should be, and *THEN* any propagation of + // that value based on the forwarding policy can take place. UHD satisfies + // this ordering dependency because: + // a) The list of property resolvers for a node is maintained in a + // vector, and adding new resolvers is always performed by a + // `push_back()` (see `node_t::add_property_resolver()`), so it is + // guaranteed that the default coercing resolver is added first since + // it's done in the `noc_block_base` constructor. + // b) `resolve_props()` and `init_props()`, `node_t` functions which + // perform property resolution, always iterate the resolver vector + // in order (i.e., items 0..n-1), ensuring that the resolvers are + // called in the same order in which they were added to the vector. + for (auto& prop : _mtu_props) { + add_property_resolver({&prop}, {&prop}, [this, source_prop = &prop]() { + const res_source_info src_edge = source_prop->get_src_info(); + // 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()); + }); + } } noc_block_base::~noc_block_base() @@ -178,11 +233,24 @@ void noc_block_base::set_mtu_forwarding_policy(const forwarding_policy_t policy) "ONE_TO_ALL, or ONE_TO_FAN!"); } + // The behavior for DROP is already implemented in the default resolvers + // that are registered in the `noc_block_base` constructor, so calling + // `set_mtu_forwarding_policy(DROP)` is effectively a no-op (but legal, + // so we should support it). + if (policy == forwarding_policy_t::DROP) { + return; + } + // Determine the set of MTU properties that should be in the output - // sensitivity list based on the selected MTU forwarding policy + // sensitivity list based on the selected MTU forwarding policy. Note + // that the input property itself (`prop` in the iteration below) is + // not added to the output sensitivity list because it will have already + // been resolved by its own separate default coercing resolver which was + // registered at `noc_block_base` construction time. See the massive + // comment in the constructor for why. for (auto& prop : _mtu_props) { const res_source_info src_edge = prop.get_src_info(); - prop_ptrs_t output_props{&prop}; + prop_ptrs_t output_props{}; for (auto& other_prop : _mtu_props) { const res_source_info dst_edge = other_prop.get_src_info(); bool add_to_output_props = false; @@ -213,13 +281,7 @@ void noc_block_base::set_mtu_forwarding_policy(const forwarding_policy_t policy) std::move(output_props), [this, dst_props = output_props, 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()); - // Set the new MTU on all the dependent output MTU edge props for (auto& dst_prop : dst_props) { auto mtu_prop = |