aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/rfnoc/CMakeLists.txt1
-rw-r--r--host/include/uhd/rfnoc/null_block_control.hpp88
-rw-r--r--host/lib/rfnoc/CMakeLists.txt1
-rw-r--r--host/lib/rfnoc/null_block_control.cpp182
-rw-r--r--host/tests/CMakeLists.txt1
-rw-r--r--host/tests/rfnoc_blocks_test.cpp87
6 files changed, 360 insertions, 0 deletions
diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt
index 6251a555d..f5cb4006b 100644
--- a/host/include/uhd/rfnoc/CMakeLists.txt
+++ b/host/include/uhd/rfnoc/CMakeLists.txt
@@ -41,6 +41,7 @@ if(ENABLE_RFNOC)
duc_block_ctrl.hpp
fir_block_ctrl.hpp
null_block_ctrl.hpp
+ null_block_control.hpp
radio_ctrl.hpp
radio_control.hpp
replay_block_ctrl.hpp
diff --git a/host/include/uhd/rfnoc/null_block_control.hpp b/host/include/uhd/rfnoc/null_block_control.hpp
new file mode 100644
index 000000000..74f6db2a3
--- /dev/null
+++ b/host/include/uhd/rfnoc/null_block_control.hpp
@@ -0,0 +1,88 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_NULL_BLOCK_CONTROL_HPP
+#define INCLUDED_LIBUHD_NULL_BLOCK_CONTROL_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/rfnoc/noc_block_base.hpp>
+#include <uhd/types/stream_cmd.hpp>
+
+namespace uhd { namespace rfnoc {
+
+class UHD_API null_block_control : public noc_block_base
+{
+public:
+ RFNOC_DECLARE_BLOCK(null_block_control)
+
+ enum port_type_t { SINK, SOURCE, LOOP };
+ enum count_type_t { LINES, PACKETS };
+
+ static const uint32_t REG_CTRL_STATUS;
+ static const uint32_t REG_SRC_LINES_PER_PKT;
+ static const uint32_t REG_SRC_BYTES_PER_PKT;
+ static const uint32_t REG_SRC_THROTTLE_CYC;
+ static const uint32_t REG_SNK_LINE_CNT_LO;
+ static const uint32_t REG_SNK_LINE_CNT_HI;
+ static const uint32_t REG_SNK_PKT_CNT_LO;
+ static const uint32_t REG_SNK_PKT_CNT_HI;
+ static const uint32_t REG_SRC_LINE_CNT_LO;
+ static const uint32_t REG_SRC_LINE_CNT_HI;
+ static const uint32_t REG_SRC_PKT_CNT_LO;
+ static const uint32_t REG_SRC_PKT_CNT_HI;
+ static const uint32_t REG_LOOP_LINE_CNT_LO;
+ static const uint32_t REG_LOOP_LINE_CNT_HI;
+ static const uint32_t REG_LOOP_PKT_CNT_LO;
+ static const uint32_t REG_LOOP_PKT_CNT_HI;
+
+ /*! Start/stop the null source (port 0)
+ */
+ virtual void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd) = 0;
+
+ /*! Reset the counters
+ */
+ virtual void reset_counters() = 0;
+
+ /*! Set bytes per packet
+ *
+ * Note: This sets the entire packet size, including header.
+ */
+ virtual void set_bytes_per_packet(const uint32_t bpp) = 0;
+
+ /*! Set throttle cycles
+ *
+ * The block will wait this many cycles between packets. Useful for reducing
+ * the data output.
+ */
+ virtual void set_throttle_cycles(const uint32_t cycs) = 0;
+
+ /*! Get lines per packet (including the header!)
+ */
+ virtual uint32_t get_lines_per_packet() = 0;
+
+ /*! Get bytes per packet
+ */
+ virtual uint32_t get_bytes_per_packet() = 0;
+
+ /*! Get throttle cycles
+ */
+ virtual uint32_t get_throttle_cycles() = 0;
+
+ /*! Returns the number of lines that have been consumed on port 0 since the
+ * last reset
+ *
+ * \param port_type The port for which the counter value should be returned,
+ * sink, source, or loop.
+ * \param count_type If we want the packet count, or the line count
+ */
+ virtual uint64_t get_count(
+ const port_type_t port_type, const count_type_t count_type) = 0;
+};
+
+}} // namespace uhd::rfnoc
+
+#endif /* INCLUDED_LIBUHD_NULL_BLOCK_CONTROL_HPP */
+
diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt
index d62489484..1b627d304 100644
--- a/host/lib/rfnoc/CMakeLists.txt
+++ b/host/lib/rfnoc/CMakeLists.txt
@@ -57,6 +57,7 @@ LIBUHD_APPEND_SOURCES(
${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}/null_block_control.cpp
${CMAKE_CURRENT_SOURCE_DIR}/window_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/radio_control_impl.cpp
diff --git a/host/lib/rfnoc/null_block_control.cpp b/host/lib/rfnoc/null_block_control.cpp
new file mode 100644
index 000000000..be0738b76
--- /dev/null
+++ b/host/lib/rfnoc/null_block_control.cpp
@@ -0,0 +1,182 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/rfnoc/null_block_control.hpp>
+#include <uhd/rfnoc/registry.hpp>
+#include <uhd/rfnoc/defaults.hpp>
+#include <atomic>
+
+namespace {
+
+} // namespace
+
+using namespace uhd::rfnoc;
+using uhd::stream_cmd_t;
+
+
+const uint32_t null_block_control::REG_CTRL_STATUS = 0x00;
+const uint32_t null_block_control::REG_SRC_LINES_PER_PKT = 0x04;
+const uint32_t null_block_control::REG_SRC_BYTES_PER_PKT = 0x08;
+const uint32_t null_block_control::REG_SRC_THROTTLE_CYC = 0x0C;
+const uint32_t null_block_control::REG_SNK_LINE_CNT_LO = 0x10;
+const uint32_t null_block_control::REG_SNK_LINE_CNT_HI = 0x14;
+const uint32_t null_block_control::REG_SNK_PKT_CNT_LO = 0x18;
+const uint32_t null_block_control::REG_SNK_PKT_CNT_HI = 0x1C;
+const uint32_t null_block_control::REG_SRC_LINE_CNT_LO = 0x20;
+const uint32_t null_block_control::REG_SRC_LINE_CNT_HI = 0x24;
+const uint32_t null_block_control::REG_SRC_PKT_CNT_LO = 0x28;
+const uint32_t null_block_control::REG_SRC_PKT_CNT_HI = 0x2C;
+const uint32_t null_block_control::REG_LOOP_LINE_CNT_LO = 0x30;
+const uint32_t null_block_control::REG_LOOP_LINE_CNT_HI = 0x34;
+const uint32_t null_block_control::REG_LOOP_PKT_CNT_LO = 0x38;
+const uint32_t null_block_control::REG_LOOP_PKT_CNT_HI = 0x3C;
+
+
+class null_block_control_impl : public null_block_control
+{
+public:
+ RFNOC_BLOCK_CONSTRUCTOR(null_block_control)
+ {
+ uint32_t initial_state = regs().peek32(REG_CTRL_STATUS);
+ _streaming = initial_state & 0x2;
+ _nipc = (initial_state >> 24) & 0xFF;
+ _item_width = (initial_state >> 16) & 0xFF;
+ reset_counters();
+ register_issue_stream_cmd();
+ }
+
+ void issue_stream_cmd(const stream_cmd_t& stream_cmd)
+ {
+ if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS) {
+ RFNOC_LOG_TRACE("Received start stream request!");
+ regs().poke32(REG_CTRL_STATUS, 0x2);
+ _streaming = true;
+ } else if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) {
+ RFNOC_LOG_TRACE("Received stop stream request!");
+ regs().poke32(REG_CTRL_STATUS, 0x0);
+ _streaming = false;
+ } else {
+ throw uhd::runtime_error("Null source can only do continuous streaming!");
+ }
+ }
+
+ void reset_counters()
+ {
+ const uint32_t streaming_flag = _streaming ? 0x2 : 0x0;
+ regs().poke32(REG_CTRL_STATUS, streaming_flag | 0x1);
+ regs().poke32(REG_CTRL_STATUS, streaming_flag | 0x0);
+ }
+
+ void set_lines_per_packet(const uint32_t lpp)
+ {
+ if (lpp < 2) {
+ throw uhd::value_error("Null source lines per packet must be at "
+ "least one line in the payload!");
+ }
+ if (lpp > 0xFFF) {
+ throw uhd::value_error("Null source lines per packet cannot exceed 12 bits!");
+ }
+ // The register value is decreased by one line for the header, and one
+ // line for encoding (if we write a 0 here, the payload will still be at
+ // least one line long).
+ regs().poke32(REG_SRC_LINES_PER_PKT, lpp - 2);
+ }
+
+ void set_bytes_per_packet(const uint32_t bpp)
+ {
+ if (bpp > 0xFFFF) {
+ throw uhd::value_error("Null source lines per packet cannot exceed 16 bits!");
+ }
+ regs().poke32(REG_SRC_BYTES_PER_PKT, bpp);
+ const uint32_t bytes_per_line = (_item_width * _nipc) / 8;
+ // If bpp is not an integer multiple of bytes_per_line, then we add a
+ // full additional line!
+ const uint32_t lpp = bpp / bytes_per_line + (bpp % bytes_per_line ? 1 : 0);
+ set_lines_per_packet(lpp);
+ }
+
+ void set_throttle_cycles(const uint32_t cycs)
+ {
+ if (cycs > 0x3FF) {
+ throw uhd::value_error("Null source throttle cycles cannot exceed 10 bits!");
+ }
+ regs().poke32(REG_SRC_THROTTLE_CYC, cycs);
+ }
+
+ uint32_t get_lines_per_packet()
+ {
+ return regs().peek32(REG_SRC_LINES_PER_PKT) + 2;
+ }
+
+ uint32_t get_bytes_per_packet()
+ {
+ return regs().peek32(REG_SRC_BYTES_PER_PKT);
+ }
+
+ uint32_t get_throttle_cycles()
+ {
+ return regs().peek32(REG_SRC_THROTTLE_CYC);
+ }
+
+ uint64_t get_count(
+ const port_type_t port_type, const count_type_t count_type)
+ {
+ const uint32_t count_addr_lo = [&](){
+ switch (port_type) {
+ case SOURCE:
+ return count_type == LINES ? REG_SRC_LINE_CNT_LO : REG_SRC_PKT_CNT_LO;
+ case SINK:
+ return count_type == LINES ? REG_SNK_LINE_CNT_LO : REG_SNK_PKT_CNT_LO;
+ case LOOP:
+ return count_type == LINES ? REG_LOOP_LINE_CNT_LO : REG_LOOP_PKT_CNT_LO;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ }();
+ return regs().peek64(count_addr_lo);
+ }
+
+private:
+
+ /*! Action API: Register a handler for stream commands
+ */
+ void register_issue_stream_cmd()
+ {
+ register_action_handler(ACTION_KEY_STREAM_CMD,
+ [this](const res_source_info& src, action_info::sptr action) {
+ stream_cmd_action_info::sptr stream_cmd_action =
+ std::dynamic_pointer_cast<stream_cmd_action_info>(action);
+ if (!stream_cmd_action) {
+ throw uhd::runtime_error(
+ "Received stream_cmd of invalid action type!");
+ }
+ if (src.instance != 0 || src.type != res_source_info::OUTPUT_EDGE) {
+ throw uhd::runtime_error(
+ "The null source can only stream from output port 0!");
+ }
+ RFNOC_LOG_DEBUG("Received stream command action request!");
+ issue_stream_cmd(stream_cmd_action->stream_cmd);
+ });
+ }
+
+ /**************************************************************************
+ * FPGA communication (register IO)
+ *************************************************************************/
+
+ /**************************************************************************
+ * Attributes
+ *************************************************************************/
+ //! True if the source port 0 is producing data
+ std::atomic_bool _streaming{false};
+
+ //! Number of items per clock
+ uint32_t _nipc;
+
+ //! Bits per item
+ uint32_t _item_width;
+};
+
+UHD_RFNOC_BLOCK_REGISTER_DIRECT(null_block_control, 0x00000001, "NullSrcSink")
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index 484eeb903..df891b7e2 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -250,6 +250,7 @@ UHD_ADD_NONAPI_TEST(
${CMAKE_SOURCE_DIR}/lib/rfnoc/block_id.cpp
${CMAKE_SOURCE_DIR}/lib/utils/compat_check.cpp
${CMAKE_SOURCE_DIR}/lib/rfnoc/ddc_block_control.cpp
+ ${CMAKE_SOURCE_DIR}/lib/rfnoc/null_block_control.cpp
${CMAKE_SOURCE_DIR}/lib/rfnoc/radio_control_impl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/cores/dsp_core_utils.cpp
)
diff --git a/host/tests/rfnoc_blocks_test.cpp b/host/tests/rfnoc_blocks_test.cpp
index 4b393eb36..1832fc6ae 100644
--- a/host/tests/rfnoc_blocks_test.cpp
+++ b/host/tests/rfnoc_blocks_test.cpp
@@ -5,10 +5,13 @@
//
#include "rfnoc_mock_reg_iface.hpp"
+#include <uhd/rfnoc/null_block_control.hpp>
#include <uhd/rfnoc/ddc_block_control.hpp>
+#include <uhd/rfnoc/actions.hpp>
#include <uhdlib/rfnoc/clock_iface.hpp>
#include <uhdlib/rfnoc/node_accessor.hpp>
#include <uhdlib/rfnoc/clock_iface.hpp>
+#include <uhdlib/utils/narrow.hpp>
#include <boost/test/unit_test.hpp>
#include <iostream>
@@ -38,8 +41,92 @@ noc_block_base::make_args_ptr make_make_args(noc_block_base::noc_id_t noc_id,
uhd::rfnoc::noc_block_base::sptr BLOCK_NAME##_make( \
uhd::rfnoc::noc_block_base::make_args_ptr make_args);
+MOCK_REGISTER(null_block_control)
MOCK_REGISTER(ddc_block_control)
+BOOST_AUTO_TEST_CASE(test_null_block)
+{
+ node_accessor_t node_accessor{};
+ constexpr size_t num_chans = 2;
+ constexpr uint32_t nipc = 2;
+ constexpr uint32_t item_width = 32;
+ constexpr noc_block_base::noc_id_t mock_id = 0x7E570000;
+
+ auto make_args = make_make_args(mock_id, "0/NullSrcSink#0", num_chans, num_chans);
+ auto reg_iface = std::dynamic_pointer_cast<mock_reg_iface_t>(make_args->reg_iface);
+ auto set_mem = [&](const uint32_t addr, const uint32_t data) {
+ reg_iface->read_memory[addr] = data;
+ };
+ auto get_mem = [&](const uint32_t addr) { return reg_iface->write_memory[addr]; };
+ auto copy_mem = [&](const uint32_t addr) { set_mem(addr, get_mem(addr)); };
+
+ set_mem(null_block_control::REG_CTRL_STATUS, (nipc << 24) | (item_width << 16));
+ auto test_null = std::dynamic_pointer_cast<null_block_control>(
+ null_block_control_make(std::move(make_args)));
+
+ using uhd::stream_cmd_t;
+ node_accessor.init_props(test_null.get());
+ uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ test_null->issue_stream_cmd(stream_cmd);
+ stream_cmd.stream_mode = stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE;
+ BOOST_REQUIRE_THROW(test_null->issue_stream_cmd(stream_cmd), uhd::runtime_error);
+
+ constexpr uint64_t snk_count = 1000000000;
+ constexpr uint64_t snk_count_pkts = 5;
+ constexpr uint64_t src_count = 2323232323;
+ constexpr uint64_t loop_count = 4242424242;
+ set_mem(null_block_control::REG_SNK_LINE_CNT_LO,
+ uhd::narrow_cast<uint32_t>(snk_count & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_SNK_LINE_CNT_HI,
+ uhd::narrow_cast<uint32_t>((snk_count >> 32) & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_SNK_PKT_CNT_LO,
+ uhd::narrow_cast<uint32_t>(snk_count_pkts & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_SNK_PKT_CNT_HI,
+ uhd::narrow_cast<uint32_t>((snk_count_pkts >> 32) & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_SRC_LINE_CNT_LO,
+ uhd::narrow_cast<uint32_t>(src_count & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_SRC_LINE_CNT_HI,
+ uhd::narrow_cast<uint32_t>((src_count >> 32) & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_LOOP_LINE_CNT_LO,
+ uhd::narrow_cast<uint32_t>(loop_count & 0xFFFFFFFF));
+ set_mem(null_block_control::REG_LOOP_LINE_CNT_HI,
+ uhd::narrow_cast<uint32_t>((loop_count >> 32) & 0xFFFFFFFF));
+ BOOST_CHECK_EQUAL(
+ test_null->get_count(null_block_control::SINK, null_block_control::LINES),
+ snk_count);
+ BOOST_CHECK_EQUAL(
+ test_null->get_count(null_block_control::SINK, null_block_control::PACKETS),
+ snk_count_pkts);
+ BOOST_CHECK_EQUAL(
+ test_null->get_count(null_block_control::SOURCE, null_block_control::LINES),
+ src_count);
+ BOOST_CHECK_EQUAL(
+ test_null->get_count(null_block_control::LOOP, null_block_control::LINES),
+ loop_count);
+
+ constexpr uint32_t lpp = 3;
+ constexpr uint32_t bpp = nipc * item_width / 8 * lpp;
+ test_null->set_bytes_per_packet(bpp);
+ copy_mem(null_block_control::REG_SRC_LINES_PER_PKT);
+ copy_mem(null_block_control::REG_SRC_BYTES_PER_PKT);
+ BOOST_CHECK_EQUAL(test_null->get_lines_per_packet(), lpp);
+ BOOST_CHECK_EQUAL(test_null->get_bytes_per_packet(), bpp);
+
+ auto sca = stream_cmd_action_info::make(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ node_accessor.send_action(test_null.get(), {res_source_info::OUTPUT_EDGE, 0}, sca);
+ BOOST_CHECK_EQUAL(get_mem(null_block_control::REG_CTRL_STATUS) & 0x2, 0x2);
+ BOOST_REQUIRE_THROW(
+ node_accessor.send_action(test_null.get(), {res_source_info::OUTPUT_EDGE, 1}, sca),
+ uhd::runtime_error);
+ BOOST_REQUIRE_THROW(
+ node_accessor.send_action(test_null.get(), {res_source_info::INPUT_EDGE, 0}, sca),
+ uhd::runtime_error);
+
+ stream_cmd.stream_mode = stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
+ test_null->issue_stream_cmd(stream_cmd);
+ BOOST_CHECK_EQUAL(get_mem(null_block_control::REG_CTRL_STATUS) & 0x2, 0x2);
+}
+
BOOST_AUTO_TEST_CASE(test_ddc_block)
{
node_accessor_t node_accessor{};