rfnoc: Fix disconnecting back-edges from graphs

This fixes a bug where calling rfnoc_graph::disconnect() fails when the
edge is not a forward edge, because the code would try and find an edge
to remove that was a forward edge, even if source/destination ports
would match.

Error was discovered during use of X410_X4_400 image. The prevented the
following use case:
- Stream data from host to Replay Block
- FDx Play and Record data via radio from/to replaz block
- Stream recorded data from replay block to host

The FDx use case requires one connection to be marked as back-edge (not
is_forward_edge). The X410_X4_400 fpga does not include DDC/DUC block
and the Radio and Replay blocks are directly connected to each other. To
enable streaming data back to the host the required back-edge connecting
needs to be disconnected.  Commit also adds 2 additional rfnoc units
tests modeling this use case w/o (X410_X4_400) and with (all 200MHz
images) DDC/DUC blocks.
This commit is contained in:
Martin Braun 2023-02-27 11:47:58 +01:00 committed by michael-west
parent 873c48184a
commit 085aff591f
2 changed files with 99 additions and 2 deletions

View File

@ -208,9 +208,11 @@ void graph_t::disconnect(node_ref_t src_node, node_ref_t dst_node, graph_edge_t
edge_info.src_blockid = src_node->get_unique_id();
edge_info.dst_blockid = dst_node->get_unique_id();
boost::remove_out_edge_if(src_vertex_desc,
boost::remove_out_edge_if(
src_vertex_desc,
[this, edge_info](rfnoc_graph_t::edge_descriptor edge_desc) {
return (edge_info == boost::get(edge_property_t(), this->_graph, edge_desc));
return (edge_info.is_equal(
boost::get(edge_property_t(), this->_graph, edge_desc), false));
},
_graph);

View File

@ -256,3 +256,98 @@ BOOST_AUTO_TEST_CASE(test_graph_disconnect_reconnect)
BOOST_CHECK_EQUAL(graph.enumerate_edges().size(), 1);
}
BOOST_AUTO_TEST_CASE(test_graph_backedge_w_ddc_disconnect_reconnect)
{
graph_t graph{};
node_accessor_t node_accessor{};
// Define some mock nodes:
// radio
mock_radio_node_t mock_radio(0);
// Replay (use mock_edge_node instead of replay)
mock_edge_node_t mock_replay_node(1, 1, "MOCK_EDGE_NODE<replay>");
// TX streamer
mock_streamer_t mock_rx_streamer(1);
// RX streamer
mock_streamer_t mock_tx_streamer(1);
// DUC
mock_ddc_node_t mock_ddc_node{};
// DDC
mock_ddc_node_t mock_duc_node{};
// These init calls would normally be done by the framework
node_accessor.init_props(&mock_radio);
node_accessor.init_props(&mock_replay_node);
node_accessor.init_props(&mock_rx_streamer);
node_accessor.init_props(&mock_tx_streamer);
node_accessor.init_props(&mock_ddc_node);
node_accessor.init_props(&mock_duc_node);
uhd::rfnoc::detail::graph_t::graph_edge_t edge_info_ddc(
0, 0, graph_t::graph_edge_t::STATIC, true);
uhd::rfnoc::detail::graph_t::graph_edge_t edge_info(
0, 0, graph_t::graph_edge_t::DYNAMIC, true);
uhd::rfnoc::detail::graph_t::graph_edge_t edge_info_backedge(
0, 0, graph_t::graph_edge_t::DYNAMIC, false);
// Now create the graph (FDx Replay and Record):
// RX: radio (src) >> DDC >> Replay Block (sink)
graph.connect(&mock_radio, &mock_ddc_node, edge_info);
graph.connect(&mock_ddc_node, &mock_replay_node, edge_info);
// TX: Replay Block (src) >> DUC >> radio (sink)
graph.connect(&mock_replay_node, &mock_duc_node, edge_info);
graph.connect(&mock_duc_node, &mock_radio, edge_info_backedge);
graph.commit();
BOOST_CHECK_EQUAL(graph.enumerate_edges().size(), 4);
// Disconnect Replay Block source (used for TX Radio)
// in preparation for connecting to RX streamer
graph.disconnect(&mock_replay_node, &mock_duc_node, edge_info);
graph.commit();
BOOST_CHECK_EQUAL(graph.enumerate_edges().size(), 3);
}
BOOST_AUTO_TEST_CASE(test_graph_backedge_wo_ddc_disconnect_reconnect)
{
graph_t graph{};
node_accessor_t node_accessor{};
// Define some mock nodes:
// radio
mock_radio_node_t mock_radio(0);
// Replay (use mock_edge_node instead of replay)
mock_edge_node_t mock_replay_node(1, 1, "MOCK_EDGE_NODE<replay>");
// TX streamer
mock_streamer_t mock_rx_streamer(1);
// RX streamer
mock_streamer_t mock_tx_streamer(1);
// These init calls would normally be done by the framework
node_accessor.init_props(&mock_radio);
node_accessor.init_props(&mock_replay_node);
node_accessor.init_props(&mock_rx_streamer);
node_accessor.init_props(&mock_tx_streamer);
uhd::rfnoc::detail::graph_t::graph_edge_t edge_info(
0, 0, graph_t::graph_edge_t::DYNAMIC, true);
uhd::rfnoc::detail::graph_t::graph_edge_t edge_info_backedge(
0, 0, graph_t::graph_edge_t::DYNAMIC, false);
// Now create the graph (FDx Replay and Record):
// RX: radio (src) sends data to Replay Block (sink)
graph.connect(&mock_radio, &mock_replay_node, edge_info);
// TX: Replay Block (src) sends data to radio (sink)
graph.connect(&mock_replay_node, &mock_radio, edge_info_backedge);
graph.commit();
BOOST_CHECK_EQUAL(graph.enumerate_edges().size(), 2);
// Disconnect Replay Block source (used for TX Radio)
// in preparation for connecting to RX streamer
graph.disconnect(&mock_replay_node, &mock_radio, edge_info);
graph.commit();
BOOST_CHECK_EQUAL(graph.enumerate_edges().size(), 1);
}