aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/rfnoc
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 /host/lib/rfnoc
parentb5be620d1922e924eb5da5bf26955b570eab2584 (diff)
downloaduhd-1ae324575c203dc5fdb7ac4833562fd8c9860235.tar.gz
uhd-1ae324575c203dc5fdb7ac4833562fd8c9860235.tar.bz2
uhd-1ae324575c203dc5fdb7ac4833562fd8c9860235.zip
RFNoC: Add FIR, Null, and Window block controllers
Diffstat (limited to 'host/lib/rfnoc')
-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
4 files changed, 282 insertions, 0 deletions
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");