aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2018-06-22 14:50:19 +0100
committerMartin Braun <martin.braun@ettus.com>2018-06-22 09:13:11 -0700
commit1ae324575c203dc5fdb7ac4833562fd8c9860235 (patch)
treec3487e234b7f13e3954ce503277537dbba0e2ed2
parentb5be620d1922e924eb5da5bf26955b570eab2584 (diff)
downloaduhd-1ae324575c203dc5fdb7ac4833562fd8c9860235.tar.gz
uhd-1ae324575c203dc5fdb7ac4833562fd8c9860235.tar.bz2
uhd-1ae324575c203dc5fdb7ac4833562fd8c9860235.zip
RFNoC: Add FIR, Null, and Window block controllers
-rw-r--r--host/include/uhd/rfnoc/fir_block_ctrl.hpp46
-rw-r--r--host/include/uhd/rfnoc/null_block_ctrl.hpp70
-rw-r--r--host/include/uhd/rfnoc/window_block_ctrl.hpp54
-rw-r--r--host/lib/rfnoc/CMakeLists.txt4
-rw-r--r--host/lib/rfnoc/fir_block_ctrl_impl.cpp77
-rw-r--r--host/lib/rfnoc/null_block_ctrl_impl.cpp107
-rw-r--r--host/lib/rfnoc/window_block_ctrl_impl.cpp94
7 files changed, 452 insertions, 0 deletions
diff --git a/host/include/uhd/rfnoc/fir_block_ctrl.hpp b/host/include/uhd/rfnoc/fir_block_ctrl.hpp
new file mode 100644
index 000000000..bfed5e067
--- /dev/null
+++ b/host/include/uhd/rfnoc/fir_block_ctrl.hpp
@@ -0,0 +1,46 @@
+//
+// Copyright 2014-2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_fir_block_ctrl_HPP
+#define INCLUDED_LIBUHD_RFNOC_fir_block_ctrl_HPP
+
+#include <uhd/rfnoc/source_block_ctrl_base.hpp>
+#include <uhd/rfnoc/sink_block_ctrl_base.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Block controller for the standard FIR RFNoC block.
+ *
+ * The standard FIR has the following features:
+ * - One input- and output-port
+ * - Configurable taps, but fixed number of taps
+ * - Supports data type sc16 (16-Bit fix-point complex samples)
+ *
+ * This block requires packets to be the same size as the FFT length.
+ * It will perform one FFT operation per incoming packet, treating it
+ * as a vector of samples.
+ */
+class UHD_RFNOC_API fir_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base
+{
+public:
+ UHD_RFNOC_BLOCK_OBJECT(fir_block_ctrl)
+
+ //! Configure the filter taps.
+ //
+ // The length of \p taps must correspond the number of taps
+ // in this block. If it's shorter, zeros will be padded.
+ // If it's longer, throws a uhd::value_error.
+ virtual void set_taps(const std::vector<int> &taps) = 0;
+
+ //! Returns the number of filter taps in this block.
+ virtual size_t get_n_taps() const = 0;
+}; /* class fir_block_ctrl*/
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_fir_block_ctrl_HPP */
+// vim: sw=4 et:
diff --git a/host/include/uhd/rfnoc/null_block_ctrl.hpp b/host/include/uhd/rfnoc/null_block_ctrl.hpp
new file mode 100644
index 000000000..8a982b3e0
--- /dev/null
+++ b/host/include/uhd/rfnoc/null_block_ctrl.hpp
@@ -0,0 +1,70 @@
+//
+// Copyright 2014-2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_NULL_BLOCK_CTRL_HPP
+#define INCLUDED_LIBUHD_RFNOC_NULL_BLOCK_CTRL_HPP
+
+#include <uhd/rfnoc/source_block_ctrl_base.hpp>
+#include <uhd/rfnoc/sink_block_ctrl_base.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Provide access to a 'null block'.
+ *
+ * A 'null block' is a specific block, which comes with a couple
+ * of features useful for testing:
+ * - It can produce data at a given line rate, with a configurable
+ * packet size.
+ * - It can be used to dump packets ("null sink", "bit bucket")
+ *
+ * This block also serves as an example of how to create your own
+ * C++ classes to control your block.
+ *
+ * As a true source, it understands the following stream commands:
+ * - STREAM_MODE_START_CONTINUOUS
+ * - STREAM_MODE_STOP_CONTINUOUS
+ *
+ * Other stream commands are not understood and issue_stream_cmd()
+ * will throw if it receives them.
+ */
+class null_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base
+{
+public:
+ // This macro must always be at the top of the public section in an RFNoC block class
+ UHD_RFNOC_BLOCK_OBJECT(null_block_ctrl)
+
+ //! Set this register to number of lines per packet
+ static const uint32_t SR_LINES_PER_PACKET = 129;
+ //! Set this register to number of cycles between producing a line
+ static const uint32_t SR_LINE_RATE = 130;
+ //! Set this register to non-zero to start producing data
+ static const uint32_t SR_ENABLE_STREAM = 131;
+
+ static const size_t DEFAULT_LINES_PER_PACKET = 32;
+ static const size_t BYTES_PER_LINE = 8;
+
+ //! Custom function to set the rate at which data is produced.
+ // Note: This is 'cycles per line', so the bit rate is actually
+ // 64 times this value (byte/s is 8*rate etc.)
+ //
+ // Equivalent to writing to line_rate/value in the property tree.
+ //
+ // \param The rate you want to set this to
+ // \param The clock rate of this block's clock domain
+ // \returns the actual line rate (will find closest possible).
+ virtual double set_line_rate(double rate, double clock_rate=166.6e6) = 0;
+
+ //! Return the current line rate. Equivalent to reading line_rate/value
+ // from the property tree.
+ virtual double get_line_rate(double clock_rate=166.6e6) const = 0;
+
+}; /* class null_block_ctrl*/
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_NULL_BLOCK_CTRL_HPP */
+// vim: sw=4 et:
diff --git a/host/include/uhd/rfnoc/window_block_ctrl.hpp b/host/include/uhd/rfnoc/window_block_ctrl.hpp
new file mode 100644
index 000000000..093b34e67
--- /dev/null
+++ b/host/include/uhd/rfnoc/window_block_ctrl.hpp
@@ -0,0 +1,54 @@
+//
+// Copyright 2014-2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_WINDOW_BLOCK_CTRL_HPP
+#define INCLUDED_LIBUHD_RFNOC_WINDOW_BLOCK_CTRL_HPP
+
+#include <uhd/rfnoc/source_block_ctrl_base.hpp>
+#include <uhd/rfnoc/sink_block_ctrl_base.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Block controller for the standard windowing RFNoC block.
+ *
+ * The standard windowing block has the following features:
+ * - One input- and output-port
+ * - Configurable window length and coefficients
+ * - Supports data type sc16 (16-Bit fix-point complex samples)
+ *
+ * This block requires packets to be the same size as the downstream FFT length.
+ * It will perform one window operation per incoming packet, treating it
+ * as a vector of samples.
+ */
+class UHD_RFNOC_API window_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base
+{
+public:
+ UHD_RFNOC_BLOCK_OBJECT(window_block_ctrl)
+
+ static const size_t MAX_COEFF_VAL = 32767;
+ static const uint32_t SR_WINDOW_LEN = 131; // Note: AXI config bus uses 129 & 130
+ static const uint32_t RB_MAX_WINDOW_LEN = 0;
+ static const uint32_t AXIS_WINDOW_LOAD = AXIS_CONFIG_BUS+0; // 2*0+0
+ static const uint32_t AXIS_WINDOW_LOAD_TLAST = AXIS_CONFIG_BUS+1; // 2*0+1
+
+ //! Configure the window coefficients
+ //
+ // \p coeffs size determines the window length. If it longer than
+ // the maximum window length, throws a uhd::value_error.
+ virtual void set_window(const std::vector<int> &coeffs) = 0;
+
+ //! Returns the maximum window length.
+ virtual size_t get_max_len() const = 0;
+
+ //! Returns the current window length.
+ virtual size_t get_window_len() const = 0;
+
+}; /* class window_block_ctrl*/
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_WINDOW_BLOCK_CTRL_HPP */
diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt
index 9ee4970a3..527c99e14 100644
--- a/host/lib/rfnoc/CMakeLists.txt
+++ b/host/lib/rfnoc/CMakeLists.txt
@@ -34,7 +34,11 @@ LIBUHD_APPEND_SOURCES(
# Default block control classes:
${CMAKE_CURRENT_SOURCE_DIR}/ddc_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/duc_block_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fir_block_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/null_block_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/window_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/siggen_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_block_ctrl_impl.cpp
)
diff --git a/host/lib/rfnoc/fir_block_ctrl_impl.cpp b/host/lib/rfnoc/fir_block_ctrl_impl.cpp
new file mode 100644
index 000000000..4267e0b22
--- /dev/null
+++ b/host/lib/rfnoc/fir_block_ctrl_impl.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright 2014-2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/rfnoc/fir_block_ctrl.hpp>
+#include <uhd/convert.hpp>
+#include <uhd/utils/log.hpp>
+
+using namespace uhd::rfnoc;
+
+class fir_block_ctrl_impl : public fir_block_ctrl
+{
+public:
+ static const uint32_t RB_NUM_TAPS = 0;
+ static const uint32_t SR_RELOAD = 128;
+ static const uint32_t SR_RELOAD_TLAST = 129;
+ static const uint32_t SR_CONFIG = 130;
+
+ UHD_RFNOC_BLOCK_CONSTRUCTOR(fir_block_ctrl),
+ _item_type("sc16") // We only support sc16 in this block
+ {
+ _n_taps = uint32_t(user_reg_read64(RB_NUM_TAPS));
+ UHD_LOGGER_DEBUG(unique_id())
+ << "fir_block::fir_block() n_taps ==" << _n_taps << std::endl;
+ UHD_ASSERT_THROW(_n_taps);
+
+ // Default to Dirac impulse
+ std::vector<int> default_taps(1, 20000);
+ set_taps(default_taps);
+ }
+
+ void set_taps(const std::vector<int> &taps_)
+ {
+ UHD_LOGGER_TRACE(unique_id()) << "fir_block::set_taps()" << std::endl;
+ if (taps_.size() > _n_taps) {
+ throw uhd::value_error(str(
+ boost::format("FIR block: Too many filter coefficients! Provided %d, FIR allows %d.\n")
+ % taps_.size() % _n_taps
+ ));
+ }
+ for (size_t i = 0; i < taps_.size(); i++) {
+ if (taps_[i] > 32767 || taps_[i] < -32768) {
+ throw uhd::value_error(str(
+ boost::format("FIR block: Coefficient %d out of range! Value %d, Allowed range [-32768,32767].\n")
+ % i % taps_[i]));
+ }
+ }
+ std::vector<int> taps = taps_;
+ if (taps.size() < _n_taps) {
+ taps.resize(_n_taps, 0);
+ }
+
+ // Write taps via the reload bus
+ for (size_t i = 0; i < taps.size() - 1; i++) {
+ sr_write(SR_RELOAD, uint32_t(taps[i]));
+ }
+ // Assert tlast when sending the spinal tap (haha, it's actually the final tap).
+ sr_write(SR_RELOAD_TLAST, uint32_t(taps.back()));
+ // Send the configuration word to replace the existing coefficients with the new ones.
+ // Note: This configuration bus does not require tlast
+ sr_write(SR_CONFIG, 0);
+ }
+
+ //! Returns the number of filter taps in this block.
+ size_t get_n_taps() const
+ {
+ return _n_taps;
+ }
+
+private:
+ const std::string _item_type;
+ size_t _n_taps;
+};
+
+UHD_RFNOC_BLOCK_REGISTER(fir_block_ctrl, "FIR");
diff --git a/host/lib/rfnoc/null_block_ctrl_impl.cpp b/host/lib/rfnoc/null_block_ctrl_impl.cpp
new file mode 100644
index 000000000..7e62a2b3e
--- /dev/null
+++ b/host/lib/rfnoc/null_block_ctrl_impl.cpp
@@ -0,0 +1,107 @@
+//
+// Copyright 2014-2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/utils/log.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/rfnoc/null_block_ctrl.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd::rfnoc;
+
+class null_block_ctrl_impl : public null_block_ctrl
+{
+public:
+ UHD_RFNOC_BLOCK_CONSTRUCTOR(null_block_ctrl)
+ {
+ // Register hooks for line_rate:
+ _tree->access<int>(_root_path / "args" / 0 / "line_rate" / "value")
+ .add_coerced_subscriber([this](const int delay){
+ this->set_line_delay_cycles(delay);
+ })
+ .update()
+ ;
+ // Register hooks for bpp:
+ _tree->access<int>(_root_path / "args" / 0 / "bpp" / "value")
+ .add_coerced_subscriber([this](const int bpp){
+ this->set_bytes_per_packet(bpp);
+ })
+ .update()
+ ;
+ }
+
+ void set_line_delay_cycles(int cycles)
+ {
+ sr_write(SR_LINE_RATE, uint32_t(cycles));
+ }
+
+ void set_bytes_per_packet(int bpp)
+ {
+ sr_write(SR_LINES_PER_PACKET, uint32_t(bpp / BYTES_PER_LINE));
+ }
+
+ double set_line_rate(double rate, double clock_rate)
+ {
+ int cycs_between_lines = clock_rate / rate - 1;
+ if (cycs_between_lines > 0xFFFF) {
+ cycs_between_lines = 0xFFFF;
+ UHD_LOGGER_WARNING(unique_id())
+ << str(boost::format("Requested rate %f is larger than possible "
+ "with the current clock rate (%.2f MHz).")
+ % rate % (clock_rate / 1e6))
+ << std::endl;
+ }
+ cycs_between_lines = std::max(0, cycs_between_lines);
+ set_arg<int>("line_rate", cycs_between_lines);
+ return _line_rate_from_reg_val(cycs_between_lines, clock_rate);
+ }
+
+ double get_line_rate(double clock_rate) const
+ {
+ return _line_rate_from_reg_val(get_arg<int>("line_rate"), clock_rate);
+ }
+
+ double _line_rate_from_reg_val(uint32_t reg_val, double clock_rate) const
+ {
+ return clock_rate / (reg_val + 1);
+ }
+
+ void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t)
+ {
+ if (not stream_cmd.stream_now) {
+ throw uhd::not_implemented_error("null_block does not support timed commands.");
+ }
+ switch (stream_cmd.stream_mode) {
+ case uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS:
+ sr_write(SR_ENABLE_STREAM, true);
+ break;
+
+ case uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS:
+ sr_write(SR_ENABLE_STREAM, false);
+ break;
+
+ case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE:
+ case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE:
+ throw uhd::not_implemented_error("null_block does not support streaming modes other than CONTINUOUS");
+
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void set_destination(
+ uint32_t next_address,
+ size_t output_block_port
+ ) {
+ uhd::sid_t sid(next_address);
+ if (sid.get_src() == 0) {
+ sid.set_src(get_address());
+ }
+ sr_write(SR_NEXT_DST_SID, sid.get(), output_block_port);
+ }
+};
+
+UHD_RFNOC_BLOCK_REGISTER(null_block_ctrl, "NullSrcSink");
+
diff --git a/host/lib/rfnoc/window_block_ctrl_impl.cpp b/host/lib/rfnoc/window_block_ctrl_impl.cpp
new file mode 100644
index 000000000..74ebd146d
--- /dev/null
+++ b/host/lib/rfnoc/window_block_ctrl_impl.cpp
@@ -0,0 +1,94 @@
+//
+// Copyright 2014-2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/rfnoc/window_block_ctrl.hpp>
+#include <uhd/convert.hpp>
+#include <uhd/utils/log.hpp>
+
+using namespace uhd::rfnoc;
+
+class window_block_ctrl_impl : public window_block_ctrl
+{
+public:
+ UHD_RFNOC_BLOCK_CONSTRUCTOR(window_block_ctrl),
+ _item_type("sc16"), // We only support sc16 in this block
+ _bpi(uhd::convert::get_bytes_per_item("sc16"))
+ {
+ _max_len = uint32_t(user_reg_read64(RB_MAX_WINDOW_LEN));
+ UHD_LOGGER_DEBUG(unique_id())
+ << "window_block::window_block() max_len ==" << _max_len << std::endl;
+ UHD_ASSERT_THROW(_max_len);
+
+ // TODO we need a coercer to check that spp on the prop tree doesn't get set to anything invalid
+ _set_default_window(std::min<size_t>(get_arg<int>("spp"), _max_len));
+ }
+
+ //! Set window coefficients and length
+ void set_window(const std::vector<int> &coeffs)
+ {
+ UHD_LOGGER_TRACE(unique_id())
+ << "window_block::set_window()" << std::endl;
+ if (coeffs.size() > _max_len) {
+ throw uhd::value_error(str(
+ boost::format("window_block::set_window(): Too many window "
+ "coefficients! Provided %d, window allows up to %d.\n")
+ % coeffs.size() % _max_len
+ ));
+ }
+
+ size_t window_len = coeffs.size();
+
+ // Window block can take complex coefficients in sc16 format, but typical usage is
+ // to have real(coeffs) == imag(coeffs)
+ std::vector<uint32_t> coeffs_;
+ for (size_t i = 0; i < window_len - 1; i++) {
+ if (coeffs[i] > 32767 || coeffs[i] < -32768) {
+ throw uhd::value_error(str(
+ boost::format("window_block::set_window(): Coefficient %d "
+ "(index %d) outside coefficient range [-32768,32767].\n")
+ % coeffs[i] % i));
+ }
+ coeffs_.push_back(coeffs[i]);
+ }
+
+ // Write coefficients via the load bus
+ for (size_t i = 0; i < window_len - 1; i++) {
+ sr_write(AXIS_WINDOW_LOAD, coeffs_[i]);
+ }
+ // Assert tlast when sending the final coefficient (sorry, no joke here)
+ sr_write(AXIS_WINDOW_LOAD_TLAST, coeffs_.back());
+ // Set the window length
+ sr_write(SR_WINDOW_LEN, window_len);
+
+ // This block requires spp to match the window length:
+ set_arg<int>("spp", int(window_len));
+ }
+
+ //! Returns the maximum window length of this block.
+ size_t get_max_len() const
+ {
+ return _max_len;
+ }
+
+ size_t get_window_len() const
+ {
+ return size_t(get_arg<int>("spp"));
+ }
+
+
+private:
+ const std::string _item_type;
+ const size_t _bpi;
+ size_t _max_len;
+
+ //! Default is a rectangular window
+ void _set_default_window(size_t window_len) {
+ std::vector<int> default_coeffs(window_len, (1 << 15)-1);
+ set_window(default_coeffs);
+ }
+};
+
+UHD_RFNOC_BLOCK_REGISTER(window_block_ctrl, "Window");