aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/rfnoc
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/rfnoc')
-rw-r--r--host/lib/rfnoc/block_ctrl_base.cpp86
1 files changed, 84 insertions, 2 deletions
diff --git a/host/lib/rfnoc/block_ctrl_base.cpp b/host/lib/rfnoc/block_ctrl_base.cpp
index 0b9f6d281..c7884f291 100644
--- a/host/lib/rfnoc/block_ctrl_base.cpp
+++ b/host/lib/rfnoc/block_ctrl_base.cpp
@@ -19,6 +19,8 @@
#include <uhdlib/rfnoc/wb_iface_adapter.hpp>
#include <boost/format.hpp>
#include <boost/bind.hpp>
+#include <chrono>
+#include <thread>
using namespace uhd;
using namespace uhd::rfnoc;
@@ -72,6 +74,20 @@ block_ctrl_base::block_ctrl_base(
_tree->create<uint64_t>(_root_path / "noc_id").set(_noc_id);
/*** Reset block state *******************************************/
+ // We don't know the state of the data-path of this block before
+ // we initialize. If everything tore down properly, the data-path
+ // should be disconnected and thus idle. Reconfiguration of parameters
+ // like SIDs is safe to do in that scenario.
+ // However, if data is still streaming, block configuration
+ // can potentially lock up noc_shell. So we flush the data-path here.
+
+ // Flush is a block-level operation that can be triggered
+ // from any block port.
+ // Do it once before clearing...
+ if (get_ctrl_ports().size() > 0) {
+ _flush(get_ctrl_ports().front());
+ }
+ // Clear flow control and misc state
clear();
/*** Configure ports ****************************************************/
@@ -129,6 +145,17 @@ block_ctrl_base::block_ctrl_base(
block_ctrl_base::~block_ctrl_base()
{
+ if (get_ctrl_ports().size() > 0) {
+ // Notify the data-path gatekeeper in noc_shell that we are done
+ // with this block. This operation disconnects the noc_block
+ // data-path from noc_shell which dumps all input and output
+ // packets that are in flight, for now and until the setting is
+ // disabled. This prevents long-running blocks without a tear-down
+ // mechanism to gracefully flush.
+ const size_t port = get_ctrl_ports().front();
+ sr_write(SR_CLEAR_TX_FC, 0x2, port); // Disconnect TX data-path
+ sr_write(SR_CLEAR_RX_FC, 0x2, port); // Disconnect RX data-path
+ }
_tree->remove(_root_path);
}
@@ -558,6 +585,59 @@ stream_sig_t block_ctrl_base::_resolve_port_def(const blockdef::port_t &port_def
return stream_sig;
}
+bool block_ctrl_base::_flush(const size_t port)
+{
+ UHD_LOG_DEBUG(unique_id(), "block_ctrl_base::_flush()");
+
+ auto is_data_streaming = [this](int time_ms) -> bool {
+ // noc_shell has 2 16-bit counters (one for TX and one for RX) in the top
+ // 32 bits of the SR_READBACK_REG_GLOBAL_PARAMS. For all the checks below
+ // we want to make sure that the counts are not changing i.e. no data is
+ // streaming. So we just look at the two counters together as a single
+ // 32-bit quantity.
+ auto old_cnts = static_cast<uint32_t>(this->sr_read64(SR_READBACK_REG_GLOBAL_PARAMS) >> 32);
+ std::this_thread::sleep_for(std::chrono::milliseconds(time_ms));
+ auto new_cnts = static_cast<uint32_t>(this->sr_read64(SR_READBACK_REG_GLOBAL_PARAMS) >> 32);
+ return (new_cnts != old_cnts);
+ };
+
+ // Initial check for activity
+ // We use a 10ms window to check for activity which detects a stream with approx
+ // 100 packets per second
+ constexpr int INITIAL_CHK_WINDOW_MS = 10;
+ if (not is_data_streaming(INITIAL_CHK_WINDOW_MS)) return true;
+
+ UHD_LOG_DEBUG(unique_id(), "block_ctrl_base::_flush(recovery mode)");
+ // We noticed streaming data. This is most likely because the last
+ // session terminated abnormally or if logic in a noc_block is
+ // misbehaving. This is a situation that we may not be able to
+ // recover from because we are in a partially initialized state.
+ // We will try to at least not lock up the FPGA.
+
+ // Disconnect the RX and TX data paths and let them flush.
+ // A timeout of 2s is chosen to be conservative. It needs to account for:
+ // - Upstream blocks that weren't terminated to run out of FC credits
+ // - This block which might be finishing up with its data output
+ constexpr int FLUSH_TIMEOUT_MS = 2000; // This is approximate
+ bool success = false;
+ sr_write(SR_CLEAR_TX_FC, 0x2, port); // Disconnect TX data-path
+ sr_write(SR_CLEAR_RX_FC, 0x2, port); // Disconnect RX data-path
+ for (int i = 0; i < FLUSH_TIMEOUT_MS/10; i++) {
+ if (not is_data_streaming(10)) {
+ success = true;
+ break;
+ }
+ }
+ sr_write(SR_CLEAR_TX_FC, 0x0, port); // Enable TX data-path
+ sr_write(SR_CLEAR_RX_FC, 0x0, port); // Enable RX data-path
+
+ UHD_LOGGER_WARNING(unique_id()) <<
+ "This block seems to be busy most likely due to the abnormal termination of a previous session. " <<
+ "Attempted recovery but it may not have worked depending on the behavior of other blocks in the design. " <<
+ "Please restart the application.";
+ return success;
+}
+
/***********************************************************************
* Hooks & Derivables
@@ -565,8 +645,10 @@ stream_sig_t block_ctrl_base::_resolve_port_def(const blockdef::port_t &port_def
void block_ctrl_base::_clear(const size_t port)
{
UHD_LOG_TRACE(unique_id(), "block_ctrl_base::_clear()");
- sr_write(SR_CLEAR_TX_FC, 0x00C1EA12, port); // 'CLEAR', but we can write anything, really
- sr_write(SR_CLEAR_RX_FC, 0x00C1EA12, port); // 'CLEAR', but we can write anything, really
+ sr_write(SR_CLEAR_TX_FC, 0x1, port); // Write 1 to trigger a single cycle clear event
+ sr_write(SR_CLEAR_TX_FC, 0x0, port); // Write 0 to reset the clear flag
+ sr_write(SR_CLEAR_RX_FC, 0x1, port); // Write 1 to trigger a single cycle clear event
+ sr_write(SR_CLEAR_RX_FC, 0x0, port); // Write 0 to reset the clear flag
}
void block_ctrl_base::_set_command_time(const time_spec_t & /*time_spec*/, const size_t /*port*/)