aboutsummaryrefslogtreecommitdiffstats
path: root/host/tests/tx_streamer_test.cpp
diff options
context:
space:
mode:
authorCiro Nishiguchi <ciro.nishiguchi@ni.com>2019-05-23 20:38:07 -0500
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:29 -0800
commit75a090543b8fb8e7c875387eee6d3fe7227e4450 (patch)
tree2904b48607cc07158aa6b068ada35ab56c4da516 /host/tests/tx_streamer_test.cpp
parentd8e9705bc6c34b8d015b56a76955ee2f15426bd8 (diff)
downloaduhd-75a090543b8fb8e7c875387eee6d3fe7227e4450.tar.gz
uhd-75a090543b8fb8e7c875387eee6d3fe7227e4450.tar.bz2
uhd-75a090543b8fb8e7c875387eee6d3fe7227e4450.zip
rfnoc: add rx and tx transports, and amend rfnoc_graph
transports: Transports build on I/O service and implements flow control and sequence number checking. The rx streamer subclass extends the streamer implementation to connect it to the rfnoc graph. It receives configuration values from property propagation and configures the streamer accordingly. It also implements the issue_stream_cmd rx_streamer API method. Add implementation of rx streamer creation and method to connect it to an rfnoc block. rfnoc_graph: Cache more connection info, clarify contract Summary of changes: - rfnoc_graph stores more information about static connections at the beginning. Some search algorithms are replaced by simpler lookups. - The contract for connect() was clarified. It is required to call connect, even for static connections.
Diffstat (limited to 'host/tests/tx_streamer_test.cpp')
-rw-r--r--host/tests/tx_streamer_test.cpp393
1 files changed, 393 insertions, 0 deletions
diff --git a/host/tests/tx_streamer_test.cpp b/host/tests/tx_streamer_test.cpp
new file mode 100644
index 000000000..cb07cffad
--- /dev/null
+++ b/host/tests/tx_streamer_test.cpp
@@ -0,0 +1,393 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "../common/mock_link.hpp"
+#include <uhdlib/transport/tx_streamer_impl.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+
+namespace uhd { namespace transport {
+
+/*!
+ * Mock tx data xport which doesn't use I/O service, and just interacts with
+ * the link directly. Transport copies packet info directly into the frame
+ * buffer.
+ */
+class mock_tx_data_xport
+{
+public:
+ using uptr = std::unique_ptr<mock_tx_data_xport>;
+ using buff_t = uhd::transport::frame_buff;
+
+ struct packet_info_t
+ {
+ bool eob = false;
+ bool has_tsf = false;
+ uint64_t tsf = 0;
+ size_t payload_bytes = 0;
+ };
+
+ mock_tx_data_xport(mock_send_link::sptr send_link) : _send_link(send_link) {}
+
+ buff_t::uptr get_send_buff(const int32_t timeout_ms)
+ {
+ return _send_link->get_send_buff(timeout_ms);
+ }
+
+ std::pair<void*, size_t> write_packet_header(
+ buff_t::uptr& buff, const packet_info_t& info)
+ {
+ uint8_t* data = static_cast<uint8_t*>(buff->data());
+ *(reinterpret_cast<packet_info_t*>(data)) = info;
+ return std::make_pair(data + sizeof(info), sizeof(info) + info.payload_bytes);
+ }
+
+ void release_send_buff(buff_t::uptr buff)
+ {
+ _send_link->release_send_buff(std::move(buff));
+ }
+
+ size_t get_max_payload_size() const
+ {
+ return _send_link->get_send_frame_size() - sizeof(packet_info_t);
+ ;
+ }
+
+private:
+ mock_send_link::sptr _send_link;
+};
+
+/*!
+ * Mock tx streamer for testing
+ */
+class mock_tx_streamer : public tx_streamer_impl<mock_tx_data_xport>
+{
+public:
+ mock_tx_streamer(const size_t num_chans, const uhd::stream_args_t& stream_args)
+ : tx_streamer_impl(num_chans, stream_args)
+ {
+ }
+
+ void set_tick_rate(double rate)
+ {
+ tx_streamer_impl::set_tick_rate(rate);
+ }
+
+ void set_samp_rate(double rate)
+ {
+ tx_streamer_impl::set_samp_rate(rate);
+ }
+
+ void set_scale_factor(const size_t chan, const double scale_factor)
+ {
+ tx_streamer_impl::set_scale_factor(chan, scale_factor);
+ }
+};
+
+}} // namespace uhd::transport
+
+using namespace uhd::transport;
+
+using tx_streamer = tx_streamer_impl<mock_tx_data_xport>;
+
+static const double TICK_RATE = 100e6;
+static const double SAMP_RATE = 10e6;
+static const size_t FRAME_SIZE = 1000;
+static const double SCALE_FACTOR = 2;
+
+/*!
+ * Helper functions
+ */
+static std::vector<mock_send_link::sptr> make_links(const size_t num)
+{
+ const mock_send_link::link_params params = {FRAME_SIZE, 1};
+
+ std::vector<mock_send_link::sptr> links;
+
+ for (size_t i = 0; i < num; i++) {
+ links.push_back(std::make_shared<mock_send_link>(params));
+ }
+
+ return links;
+}
+
+static boost::shared_ptr<mock_tx_streamer> make_tx_streamer(
+ std::vector<mock_send_link::sptr> send_links, const std::string& format)
+{
+ const uhd::stream_args_t stream_args(format, "sc16");
+ auto streamer = boost::make_shared<mock_tx_streamer>(send_links.size(), stream_args);
+ streamer->set_tick_rate(TICK_RATE);
+ streamer->set_samp_rate(SAMP_RATE);
+
+ for (size_t i = 0; i < send_links.size(); i++) {
+ mock_tx_data_xport::uptr xport(
+ std::make_unique<mock_tx_data_xport>(send_links[i]));
+
+ streamer->set_scale_factor(i, SCALE_FACTOR);
+ streamer->connect_channel(i, std::move(xport));
+ }
+
+ return streamer;
+}
+
+std::tuple<mock_tx_data_xport::packet_info_t, std::complex<uint16_t>*, size_t, boost::shared_array<uint8_t>>
+pop_send_packet(mock_send_link::sptr send_link)
+{
+ auto packet = send_link->pop_send_packet();
+
+ const size_t packet_samps =
+ (packet.second - sizeof(mock_tx_data_xport::packet_info_t))
+ / sizeof(std::complex<uint16_t>);
+
+ uint8_t* buff_ptr = packet.first.get();
+ auto info = *(reinterpret_cast<mock_tx_data_xport::packet_info_t*>(buff_ptr));
+
+ std::complex<uint16_t>* data = reinterpret_cast<std::complex<uint16_t>*>(
+ buff_ptr + sizeof(mock_tx_data_xport::packet_info_t));
+
+ return std::make_tuple(info, data, packet_samps, packet.first);
+}
+
+/*!
+ * Tests
+ */
+BOOST_AUTO_TEST_CASE(test_send_one_channel_one_packet)
+{
+ const size_t NUM_PKTS_TO_TEST = 30;
+ const std::string format("fc32");
+
+ auto send_links = make_links(1);
+ auto streamer = make_tx_streamer(send_links, format);
+
+ // Allocate metadata
+ uhd::tx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.time_spec = uhd::time_spec_t(0.0);
+
+ // Allocate buffer and write data
+ std::vector<std::complex<float>> buff(20);
+ for (size_t i = 0; i < buff.size(); i++) {
+ buff[i] = std::complex<float>(i * 2, i * 2 + 1);
+ }
+
+ // Send packets and check data
+ size_t num_accum_samps = 0;
+
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++) {
+ std::cout << "sending packet " << i << std::endl;
+
+ // Vary num_samps for each packet
+ const size_t num_samps = 10 + i % 10;
+ metadata.end_of_burst = (i == NUM_PKTS_TO_TEST - 1);
+ const size_t num_sent = streamer->send(&buff.front(), num_samps, metadata, 1.0);
+ BOOST_CHECK_EQUAL(num_sent, num_samps);
+ metadata.time_spec += uhd::time_spec_t(0, num_sent, SAMP_RATE);
+
+ mock_tx_data_xport::packet_info_t info;
+ std::complex<uint16_t>* data;
+ size_t packet_samps;
+ boost::shared_array<uint8_t> frame_buff;
+
+ std::tie(info, data, packet_samps, frame_buff) = pop_send_packet(send_links[0]);
+ BOOST_CHECK_EQUAL(num_samps, packet_samps);
+
+ // Check data
+ for (size_t j = 0; j < num_samps; j++) {
+ const std::complex<uint16_t> value(
+ (j * 2) * SCALE_FACTOR, (j * 2 + 1) * SCALE_FACTOR);
+ BOOST_CHECK_EQUAL(value, data[j]);
+ }
+
+ BOOST_CHECK_EQUAL(num_samps, info.payload_bytes / sizeof(std::complex<uint16_t>));
+ BOOST_CHECK(info.has_tsf);
+ BOOST_CHECK_EQUAL(info.tsf, num_accum_samps * TICK_RATE / SAMP_RATE);
+ BOOST_CHECK_EQUAL(info.eob, i == NUM_PKTS_TO_TEST - 1);
+ num_accum_samps += num_samps;
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_send_one_channel_multi_packet)
+{
+ const size_t NUM_BUFFS_TO_TEST = 5;
+ const std::string format("fc64");
+
+ auto send_links = make_links(1);
+ auto streamer = make_tx_streamer(send_links, format);
+
+ // Allocate metadata
+ uhd::tx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.time_spec = uhd::time_spec_t(0.0);
+
+ // Allocate buffer and write data
+ const size_t spp = streamer->get_max_num_samps();
+ const size_t num_samps = spp * 4;
+ std::vector<std::complex<double>> buff(num_samps);
+ for (size_t i = 0; i < buff.size(); i++) {
+ buff[i] = std::complex<double>(i * 2, i * 2 + 1);
+ }
+
+ // Send packets and check data
+ size_t num_accum_samps = 0;
+
+ for (size_t i = 0; i < NUM_BUFFS_TO_TEST; i++) {
+ std::cout << "sending packet " << i << std::endl;
+
+ metadata.end_of_burst = true;
+ const size_t num_sent = streamer->send(&buff.front(), num_samps, metadata, 1.0);
+ BOOST_CHECK_EQUAL(num_sent, num_samps);
+ metadata.time_spec += uhd::time_spec_t(0, num_sent, SAMP_RATE);
+
+ size_t samps_checked = 0;
+
+ while (samps_checked < num_samps) {
+ mock_tx_data_xport::packet_info_t info;
+ std::complex<uint16_t>* data;
+ size_t packet_samps;
+ boost::shared_array<uint8_t> frame_buff;
+
+ std::tie(info, data, packet_samps, frame_buff) = pop_send_packet(send_links[0]);
+
+ for (size_t j = 0; j < packet_samps; j++) {
+ const size_t n = j + samps_checked;
+ const std::complex<uint16_t> value(
+ (n * 2) * SCALE_FACTOR, (n * 2 + 1) * SCALE_FACTOR);
+ BOOST_CHECK_EQUAL(value, data[j]);
+ }
+
+ BOOST_CHECK_EQUAL(
+ packet_samps, info.payload_bytes / sizeof(std::complex<uint16_t>));
+ BOOST_CHECK(info.has_tsf);
+ BOOST_CHECK_EQUAL(
+ info.tsf, (num_accum_samps + samps_checked) * TICK_RATE / SAMP_RATE);
+ samps_checked += packet_samps;
+
+ BOOST_CHECK_EQUAL(info.eob, samps_checked == num_samps);
+ }
+
+ BOOST_CHECK_EQUAL(samps_checked, num_samps);
+ num_accum_samps += samps_checked;
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_send_two_channel_one_packet)
+{
+ const size_t NUM_PKTS_TO_TEST = 30;
+ const std::string format("sc16");
+
+ auto send_links = make_links(2);
+ auto streamer = make_tx_streamer(send_links, format);
+
+ // Allocate metadata
+ uhd::tx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.time_spec = uhd::time_spec_t(0.0);
+
+ // Allocate buffer and write data
+ std::vector<std::complex<uint16_t>> buff(20);
+ for (size_t i = 0; i < buff.size(); i++) {
+ buff[i] = std::complex<uint16_t>(i * 2, i * 2 + 1);
+ }
+ std::vector<void*> buffs;
+ for (size_t ch = 0; ch < 2; ch++) {
+ buffs.push_back(buff.data()); // same buffer for each channel
+ }
+
+ // Send packets and check data
+ size_t num_accum_samps = 0;
+
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++) {
+ std::cout << "sending packet " << i << std::endl;
+
+ // Vary num_samps for each packet
+ const size_t num_samps = 10 + i % 10;
+ metadata.end_of_burst = (i == NUM_PKTS_TO_TEST - 1);
+ const size_t num_sent = streamer->send(buffs, num_samps, metadata, 1.0);
+ BOOST_CHECK_EQUAL(num_sent, num_samps);
+ metadata.time_spec += uhd::time_spec_t(0, num_sent, SAMP_RATE);
+
+ for (size_t ch = 0; ch < 2; ch++) {
+ mock_tx_data_xport::packet_info_t info;
+ std::complex<uint16_t>* data;
+ size_t packet_samps;
+ boost::shared_array<uint8_t> frame_buff;
+
+ std::tie(info, data, packet_samps, frame_buff) = pop_send_packet(send_links[ch]);
+ BOOST_CHECK_EQUAL(num_samps, packet_samps);
+
+ // Check data
+ for (size_t j = 0; j < num_samps; j++) {
+ const std::complex<uint16_t> value((j * 2), (j * 2 + 1));
+ BOOST_CHECK_EQUAL(value, data[j]);
+ }
+
+ BOOST_CHECK_EQUAL(
+ num_samps, info.payload_bytes / sizeof(std::complex<uint16_t>));
+ BOOST_CHECK(info.has_tsf);
+ BOOST_CHECK_EQUAL(info.tsf, num_accum_samps * TICK_RATE / SAMP_RATE);
+ BOOST_CHECK_EQUAL(info.eob, i == NUM_PKTS_TO_TEST - 1);
+ }
+ num_accum_samps += num_samps;
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_meta_data_cache)
+{
+ auto send_links = make_links(1);
+ auto streamer = make_tx_streamer(send_links, "fc32");
+
+ // Allocate metadata
+ uhd::tx_metadata_t metadata;
+ metadata.start_of_burst = true;
+ metadata.end_of_burst = true;
+ metadata.has_time_spec = true;
+ metadata.time_spec = uhd::time_spec_t(0.0);
+
+ // Allocate buffer and write data
+ std::vector<std::complex<float>> buff(20);
+
+ size_t num_sent = streamer->send(buff.data(), 0, metadata, 1.0);
+ BOOST_CHECK_EQUAL(send_links[0]->get_num_packets(), 0);
+ BOOST_CHECK_EQUAL(num_sent, 0);
+ uhd::tx_metadata_t metadata2;
+ num_sent = streamer->send(buff.data(), 10, metadata2, 1.0);
+
+ mock_tx_data_xport::packet_info_t info;
+ size_t packet_samps;
+ boost::shared_array<uint8_t> frame_buff;
+
+ std::tie(info, std::ignore, packet_samps, frame_buff) = pop_send_packet(send_links[0]);
+ BOOST_CHECK_EQUAL(packet_samps, num_sent);
+ BOOST_CHECK(info.has_tsf);
+ BOOST_CHECK(info.eob);
+}
+
+BOOST_AUTO_TEST_CASE(test_spp)
+{
+ // Test the spp calculation when it is limited by the stream args
+ {
+ auto send_links = make_links(1);
+ uhd::stream_args_t stream_args("fc64", "sc16");
+ stream_args.args["spp"] = std::to_string(10);
+ auto streamer = boost::make_shared<mock_tx_streamer>(send_links.size(), stream_args);
+ mock_tx_data_xport::uptr xport(std::make_unique<mock_tx_data_xport>(send_links[0]));
+ streamer->connect_channel(0, std::move(xport));
+ BOOST_CHECK_EQUAL(streamer->get_max_num_samps(), 10);
+ }
+
+ // Test the spp calculation when it is limited by the frame size
+ {
+ auto send_links = make_links(1);
+ uhd::stream_args_t stream_args("fc64", "sc16");
+ stream_args.args["spp"] = std::to_string(10000);
+ auto streamer = boost::make_shared<mock_tx_streamer>(send_links.size(), stream_args);
+ mock_tx_data_xport::uptr xport(std::make_unique<mock_tx_data_xport>(send_links[0]));
+ const size_t max_pyld = xport->get_max_payload_size();
+ streamer->connect_channel(0, std::move(xport));
+ BOOST_CHECK_EQUAL(streamer->get_max_num_samps(), max_pyld / sizeof(std::complex<uint16_t>));
+ }
+}