aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-05-30 16:17:49 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:21 -0800
commitfff4fd59b7b7995904ecb2b010f05f78e9d0a0de (patch)
tree6bac20c68dbe9759471d17ef70cf576e71415aff
parent31449a44699b6484442c91394e8ff14e22cea246 (diff)
downloaduhd-fff4fd59b7b7995904ecb2b010f05f78e9d0a0de.tar.gz
uhd-fff4fd59b7b7995904ecb2b010f05f78e9d0a0de.tar.bz2
uhd-fff4fd59b7b7995904ecb2b010f05f78e9d0a0de.zip
rfnoc: noc_block_base: Handle the tick_rate property internally
All noc_block_base derivatives are now plugged into the tick rate system. Connected nodes can only have one tick rate among them. This implies there is also only ever one tick rate per block. set_tick_rate() is a protected API call which can be called by blocks such as radio blocks to actually set a tick rate. Other blocks would only ever read the tick rate, which is handled by the get_tick_rate() API call.
-rw-r--r--host/include/uhd/rfnoc/noc_block_base.hpp32
-rw-r--r--host/include/uhd/rfnoc/noc_block_make_args.hpp4
-rw-r--r--host/lib/rfnoc/ddc_block_control.cpp40
-rw-r--r--host/lib/rfnoc/noc_block_base.cpp62
-rw-r--r--host/tests/rfnoc_blocks_test.cpp1
5 files changed, 99 insertions, 40 deletions
diff --git a/host/include/uhd/rfnoc/noc_block_base.hpp b/host/include/uhd/rfnoc/noc_block_base.hpp
index b671e6525..0959e5bdd 100644
--- a/host/include/uhd/rfnoc/noc_block_base.hpp
+++ b/host/include/uhd/rfnoc/noc_block_base.hpp
@@ -22,6 +22,8 @@
namespace uhd { namespace rfnoc {
+class clock_iface;
+
/*!
* The primary interface to a NoC block in the FPGA
*
@@ -83,10 +85,30 @@ public:
*/
const block_id_t& get_block_id() const { return _block_id; }
+ /*! Returns the tick rate of the current time base
+ *
+ * Note there is only ever one time base (or tick rate) per block.
+ */
+ double get_tick_rate() const { return _tick_rate; }
+
protected:
noc_block_base(make_args_ptr make_args);
+ /*! Update tick rate for this node and all the connected nodes
+ *
+ * Careful: Calling this function will trigger a property propagation to any
+ * block this block is connected to.
+ */
+ void set_tick_rate(const double tick_rate);
+
private:
+ /*! Update the tick rate of this block
+ *
+ * This will make sure that the underlying register_iface is notified of the
+ * change in timebase.
+ */
+ void _set_tick_rate(const double tick_rate);
+
//! This block's Noc-ID
noc_id_t _noc_id;
@@ -100,6 +122,16 @@ private:
//! Number of output ports
size_t _num_output_ports;
+
+ //! Container for the 'tick rate' property. This will hold one edge property
+ // for all in- and output edges.
+ std::vector<property_t<double>> _tick_rate_props;
+
+ //! The actual tick rate of the current time base
+ double _tick_rate;
+
+ std::shared_ptr<clock_iface> _clock_iface;
+
}; // class noc_block_base
}} /* namespace uhd::rfnoc */
diff --git a/host/include/uhd/rfnoc/noc_block_make_args.hpp b/host/include/uhd/rfnoc/noc_block_make_args.hpp
index c9b530589..d3679f973 100644
--- a/host/include/uhd/rfnoc/noc_block_make_args.hpp
+++ b/host/include/uhd/rfnoc/noc_block_make_args.hpp
@@ -14,6 +14,7 @@
namespace uhd { namespace rfnoc {
class clock_iface;
+
/*! Data structure to hold the arguments passed into the noc_block_base ctor
*
* We want to hide these from the user, so she can't futz around with them.
@@ -21,6 +22,8 @@ class clock_iface;
*/
struct noc_block_base::make_args_t
{
+ ~make_args_t();
+
//! Noc-ID
noc_id_t noc_id;
@@ -38,6 +41,7 @@ struct noc_block_base::make_args_t
//! Clock interface object that is shared with the reg_iface
std::shared_ptr<clock_iface> clk_iface;
+
//! The subtree for this block
uhd::property_tree::sptr tree;
};
diff --git a/host/lib/rfnoc/ddc_block_control.cpp b/host/lib/rfnoc/ddc_block_control.cpp
index 4562585d8..0b6fd7d89 100644
--- a/host/lib/rfnoc/ddc_block_control.cpp
+++ b/host/lib/rfnoc/ddc_block_control.cpp
@@ -56,8 +56,7 @@ public:
, _fpga_compat(regs().peek32(RB_COMPAT_NUM)),
_num_halfbands(regs().peek32(RB_NUM_HB)),
_cic_max_decim(regs().peek32(RB_CIC_MAX_DECIM)),
- _residual_scaling(get_num_input_ports(), DEFAULT_SCALING),
- _tick_rate(get_num_input_ports(), DEFAULT_RATE)
+ _residual_scaling(get_num_input_ports(), DEFAULT_SCALING)
{
UHD_ASSERT_THROW(get_num_input_ports() == get_num_output_ports());
UHD_ASSERT_THROW(_cic_max_decim > 0 && _cic_max_decim <= 0xFF);
@@ -87,8 +86,6 @@ public:
// space, because we use push_back() further down, and properties must
// not change their base address after registration and resolver
// creation.
- _tick_rate_in.reserve(get_num_ports());
- _tick_rate_out.reserve(get_num_ports());
_samp_rate_in.reserve(get_num_ports());
_samp_rate_out.reserve(get_num_ports());
_scaling_in.reserve(get_num_ports());
@@ -194,10 +191,6 @@ private:
void _register_props(const size_t chan)
{
// Create actual properties and store them
- _tick_rate_in.push_back(property_t<double>(
- PROP_KEY_TICK_RATE, DEFAULT_RATE, {res_source_info::INPUT_EDGE, chan}));
- _tick_rate_out.push_back(property_t<double>(
- PROP_KEY_TICK_RATE, DEFAULT_RATE, {res_source_info::OUTPUT_EDGE, chan}));
_samp_rate_in.push_back(property_t<double>(
PROP_KEY_SAMP_RATE, DEFAULT_RATE, {res_source_info::INPUT_EDGE, chan}));
_samp_rate_out.push_back(property_t<double>(
@@ -214,8 +207,6 @@ private:
PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::INPUT_EDGE, chan}));
_type_out.emplace_back(property_t<std::string>(
PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::OUTPUT_EDGE, chan}));
- UHD_ASSERT_THROW(_tick_rate_in.size() == chan + 1);
- UHD_ASSERT_THROW(_tick_rate_out.size() == chan + 1);
UHD_ASSERT_THROW(_samp_rate_in.size() == chan + 1);
UHD_ASSERT_THROW(_samp_rate_out.size() == chan + 1);
UHD_ASSERT_THROW(_scaling_in.size() == chan + 1);
@@ -226,8 +217,6 @@ private:
UHD_ASSERT_THROW(_type_out.size() == chan + 1);
// give us some shorthands for the rest of this function
- property_t<double>* tick_rate_in = &_tick_rate_in.back();
- property_t<double>* tick_rate_out = &_tick_rate_out.back();
property_t<double>* samp_rate_in = &_samp_rate_in.back();
property_t<double>* samp_rate_out = &_samp_rate_out.back();
property_t<double>* scaling_in = &_scaling_in.back();
@@ -238,8 +227,6 @@ private:
property_t<std::string>* type_out = &_type_out.back();
// register them
- register_property(tick_rate_in);
- register_property(tick_rate_out);
register_property(samp_rate_in);
register_property(samp_rate_out);
register_property(scaling_in);
@@ -252,24 +239,6 @@ private:
/**********************************************************************
* Add resolvers
*********************************************************************/
- // Resolver for the tick rate: The input and output edges can have
- // different time bases. However, we might want to forward the time base,
- // e.g., if the upstream block is a radio, but the downstream block is a
- // streamer and wouldn't know about time bases itself.
- // We therefore propagate tick rates only if the opposite edge has the
- // same tick rate as we do. For the output, we accept any tick rate,
- // so it gets no resolver.
- add_property_resolver({tick_rate_in},
- {tick_rate_out},
- [this,
- chan,
- &tick_rate_in = *tick_rate_in,
- &tick_rate_out = *tick_rate_out]() {
- if (tick_rate_out.get() == _tick_rate.at(chan)) {
- tick_rate_out = tick_rate_in.get();
- }
- _tick_rate[chan] = tick_rate_in.get();
- });
// Resolver for the output scaling: This cannot be updated, we reset it
// to its previous value.
add_property_resolver({scaling_out},
@@ -529,17 +498,10 @@ private:
//! Cache the current residual scaling
std::vector<double> _residual_scaling;
- //! Tick rate for every input channel
- std::vector<double> _tick_rate;
-
//! Properties for type_in (one per port)
std::vector<property_t<std::string>> _type_in;
//! Properties for type_out (one per port)
std::vector<property_t<std::string>> _type_out;
- //! Properties for tick_rate_in (one per port)
- std::vector<property_t<double>> _tick_rate_in;
- //! Properties for tick_rate_out (one per port)
- std::vector<property_t<double>> _tick_rate_out;
//! Properties for samp_rate_in (one per port)
std::vector<property_t<double>> _samp_rate_in;
//! Properties for samp_rate_out (one per port)
diff --git a/host/lib/rfnoc/noc_block_base.cpp b/host/lib/rfnoc/noc_block_base.cpp
index 838e05e74..2bbf52928 100644
--- a/host/lib/rfnoc/noc_block_base.cpp
+++ b/host/lib/rfnoc/noc_block_base.cpp
@@ -4,12 +4,16 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
+#include <uhd/exception.hpp>
+#include <uhd/rfnoc/defaults.hpp>
#include <uhd/rfnoc/noc_block_base.hpp>
#include <uhd/rfnoc/register_iface.hpp>
-#include <uhd/exception.hpp>
+#include <uhdlib/rfnoc/clock_iface.hpp>
using namespace uhd::rfnoc;
+noc_block_base::make_args_t::~make_args_t() = default;
+
/******************************************************************************
* Structors
*****************************************************************************/
@@ -19,7 +23,39 @@ noc_block_base::noc_block_base(make_args_ptr make_args)
, _block_id(make_args->block_id)
, _num_input_ports(make_args->num_input_ports)
, _num_output_ports(make_args->num_output_ports)
+ , _clock_iface(make_args->clk_iface)
{
+ // First, create one tick_rate property for every port
+ _tick_rate_props.reserve(get_num_input_ports() + get_num_output_ports());
+ for (size_t input_port = 0; input_port < get_num_input_ports(); input_port++) {
+ _tick_rate_props.push_back(property_t<double>(PROP_KEY_TICK_RATE,
+ DEFAULT_TICK_RATE,
+ {res_source_info::INPUT_EDGE, input_port}));
+ }
+ for (size_t output_port = 0; output_port < get_num_output_ports(); output_port++) {
+ _tick_rate_props.push_back(property_t<double>(PROP_KEY_TICK_RATE,
+ DEFAULT_TICK_RATE,
+ {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());
+ for (auto& prop : _tick_rate_props) {
+ prop_refs.insert(&prop);
+ register_property(&prop);
+ }
+ for (auto& prop : _tick_rate_props) {
+ auto prop_refs_copy = prop_refs;
+ add_property_resolver(
+ {&prop}, std::move(prop_refs_copy), [this, source_prop = &prop]() {
+ for (property_t<double>& tick_rate_prop : _tick_rate_props) {
+ tick_rate_prop = source_prop->get();
+ }
+ this->_set_tick_rate(source_prop->get());
+ });
+ }
+ // Now enable this clock iface
+ _clock_iface->set_running(true);
}
noc_block_base::~noc_block_base()
@@ -27,3 +63,27 @@ noc_block_base::~noc_block_base()
// nop
}
+
+void noc_block_base::set_tick_rate(const double tick_rate)
+{
+ if (_tick_rate == tick_rate) {
+ return;
+ }
+ // Update this node
+ _set_tick_rate(tick_rate);
+ // Now trigger property propagation
+ if (!_tick_rate_props.empty()) {
+ auto src_info = _tick_rate_props.at(0).get_src_info();
+ set_property<double>(PROP_KEY_TICK_RATE, tick_rate, src_info);
+ }
+}
+
+void noc_block_base::_set_tick_rate(const double tick_rate)
+{
+ if (tick_rate == _tick_rate) {
+ return;
+ }
+ RFNOC_LOG_TRACE("Updating tick rate to " << (tick_rate / 1e6) << " MHz");
+ _clock_iface->set_freq(tick_rate);
+ _tick_rate = tick_rate;
+}
diff --git a/host/tests/rfnoc_blocks_test.cpp b/host/tests/rfnoc_blocks_test.cpp
index b9ffcb5f9..113fbf630 100644
--- a/host/tests/rfnoc_blocks_test.cpp
+++ b/host/tests/rfnoc_blocks_test.cpp
@@ -8,6 +8,7 @@
#include <uhd/rfnoc/ddc_block_control.hpp>
#include <uhdlib/rfnoc/clock_iface.hpp>
#include <uhdlib/rfnoc/node_accessor.hpp>
+#include <uhdlib/rfnoc/clock_iface.hpp>
#include <boost/test/unit_test.hpp>
#include <iostream>