From cb40ecf2f5d1a0c3381b29f425fdf30f89c8ec29 Mon Sep 17 00:00:00 2001 From: Aaron Rossetto Date: Thu, 4 Jun 2020 16:13:13 -0500 Subject: tests: Add unit test for FFT RFNoC block --- host/tests/rfnoc_block_tests/fft_block_test.cpp | 187 ++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 host/tests/rfnoc_block_tests/fft_block_test.cpp (limited to 'host/tests/rfnoc_block_tests') diff --git a/host/tests/rfnoc_block_tests/fft_block_test.cpp b/host/tests/rfnoc_block_tests/fft_block_test.cpp new file mode 100644 index 000000000..0eac8cf43 --- /dev/null +++ b/host/tests/rfnoc_block_tests/fft_block_test.cpp @@ -0,0 +1,187 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "../rfnoc_graph_mock_nodes.hpp" +#include +#include +#include +#include +#include +#include +#include + +using namespace uhd::rfnoc; + +// Redeclare this here, since it's only defined outside of UHD_API +noc_block_base::make_args_t::~make_args_t() = default; + +/* + * This class extends mock_reg_iface_t, adding a register poke override + * that monitors the reset strobe address and sets a flag when written. + */ +class fft_mock_reg_iface_t : public mock_reg_iface_t +{ +public: + virtual void _poke_cb( + uint32_t addr, uint32_t /*data*/, uhd::time_spec_t /*time*/, bool /*ack*/) + { + if (addr == fft_block_control::REG_RESET_ADDR) { + fft_was_reset = true; + } + } + + void reset_strobe() + { + fft_was_reset = false; + } + + bool fft_was_reset = false; +}; + +/* fft_block_fixture is a class which is instantiated before each test case + * is run. It sets up the block container, mock register interface, and + * fft_block_control object, all of which are accessible to the test case. + * The instance of the object is destroyed at the end of each test case. + */ + +namespace { +constexpr size_t DEFAULT_MTU = 8000; +}; + +struct fft_block_fixture +{ + //! Create an FFT block and all related infrastructure for unit testsing. + fft_block_fixture() + : reg_iface(std::make_shared()) + , block_container(get_mock_block( + FFT_BLOCK, 1, 1, uhd::device_addr_t(), DEFAULT_MTU, ANY_DEVICE, reg_iface)) + , test_fft(block_container.get_block()) + { + node_accessor.init_props(test_fft.get()); + } + + std::shared_ptr reg_iface; + mock_block_container block_container; + std::shared_ptr test_fft; + node_accessor_t node_accessor{}; +}; + +/* + * This test case ensures that the hardware is programmed correctly with + * defaults when the FFT block is constructed. + */ +BOOST_FIXTURE_TEST_CASE(fft_test_construction, fft_block_fixture) +{ + // These are the defaults from the FFT block controller. They are not + // exported, so duplicate them here. Obviously, if these defaults are + // changed in fft_block_control, those changes must be reflected here. + constexpr int fft_default_size_log2 = 8; // log2(256) + constexpr fft_shift fft_default_shift = fft_shift::NORMAL; + constexpr fft_direction fft_default_direction = fft_direction::FORWARD; + constexpr fft_magnitude fft_default_magnitude = fft_magnitude::COMPLEX; + constexpr int fft_default_scaling = 1706; + + BOOST_CHECK(reg_iface->fft_was_reset); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_LENGTH_LOG2_ADDR], + fft_default_size_log2); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_MAGNITUDE_OUT_ADDR], + static_cast(fft_default_magnitude)); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_DIRECTION_ADDR], + static_cast(fft_default_direction)); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_SCALING_ADDR], + fft_default_scaling); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_SHIFT_CONFIG_ADDR], + static_cast(fft_default_shift)); +} + +/* + * This test case exercises the remainder of the FFT block API with valid, + * in-range values and ensures that the appropriate register is programmed + * appropriately. + */ +BOOST_FIXTURE_TEST_CASE(fft_test_api, fft_block_fixture) +{ + constexpr fft_direction direction = fft_direction::REVERSE; + test_fft->set_direction(direction); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_DIRECTION_ADDR], + static_cast(direction)); + BOOST_CHECK(test_fft->get_direction() == direction); + + constexpr fft_magnitude magnitude = fft_magnitude::MAGNITUDE_SQUARED; + test_fft->set_magnitude(magnitude); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_MAGNITUDE_OUT_ADDR], + static_cast(magnitude)); + BOOST_CHECK(test_fft->get_magnitude() == magnitude); + + constexpr fft_shift shift = fft_shift::NATURAL; + test_fft->set_shift_config(shift); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_SHIFT_CONFIG_ADDR], + static_cast(shift)); + BOOST_CHECK(test_fft->get_shift_config() == shift); + + constexpr uint16_t scaling = 256; + test_fft->set_scaling(scaling); + BOOST_CHECK_EQUAL( + reg_iface->write_memory[fft_block_control::REG_SCALING_ADDR], scaling); + BOOST_CHECK(test_fft->get_scaling() == scaling); + + constexpr size_t length_log2 = 10; + test_fft->set_length(1 << length_log2); + BOOST_CHECK_EQUAL( + reg_iface->write_memory[fft_block_control::REG_LENGTH_LOG2_ADDR], length_log2); + BOOST_CHECK_EQUAL(test_fft->get_length(), (1 << length_log2)); + + // Make sure that the FFT length parameter is coerced to the closest + // power of two smaller than the desired value. + constexpr size_t coerced_length_log2 = 9; + test_fft->set_length((1 << coerced_length_log2) + (1 << (coerced_length_log2 - 1))); + BOOST_CHECK_EQUAL(reg_iface->write_memory[fft_block_control::REG_LENGTH_LOG2_ADDR], + coerced_length_log2); + BOOST_CHECK_EQUAL(test_fft->get_length(), (1 << coerced_length_log2)); +} + +/* + * This test case exercises the range checking performed on several of the + * FFT block properties, ensuring that the appropriate exception is thrown. + */ +BOOST_FIXTURE_TEST_CASE(fft_test_range_errors, fft_block_fixture) +{ + BOOST_CHECK_THROW(test_fft->set_property("direction", 12345), uhd::value_error); + BOOST_CHECK_THROW(test_fft->set_property("magnitude", 54321), uhd::value_error); + BOOST_CHECK_THROW( + test_fft->set_property("shift_config", 31337), uhd::value_error); + BOOST_CHECK_THROW(test_fft->set_scaling(32767), uhd::value_error); + BOOST_CHECK_THROW(test_fft->set_length(65535), uhd::value_error); +} + +/* + * This test case ensures that the FFT block controller can be added + * to a graph. + */ +BOOST_FIXTURE_TEST_CASE(fft_test_graph, fft_block_fixture) +{ + detail::graph_t graph{}; + detail::graph_t::graph_edge_t edge_info{ + 0, 0, detail::graph_t::graph_edge_t::DYNAMIC, true}; + + mock_radio_node_t mock_radio_block{0}; + mock_ddc_node_t mock_ddc_block{}; + mock_terminator_t mock_sink_term(2, {}, "MOCK_SINK"); + + UHD_LOG_INFO("TEST", "Priming mock block properties"); + node_accessor.init_props(&mock_radio_block); + node_accessor.init_props(&mock_ddc_block); + mock_sink_term.set_edge_property( + "type", "sc16", {res_source_info::INPUT_EDGE, 0}); + + UHD_LOG_INFO("TEST", "Creating graph..."); + graph.connect(&mock_radio_block, &mock_ddc_block, edge_info); + graph.connect(&mock_ddc_block, test_fft.get(), edge_info); + graph.connect(test_fft.get(), &mock_sink_term, edge_info); + UHD_LOG_INFO("TEST", "Committing graph..."); + graph.commit(); + UHD_LOG_INFO("TEST", "Commit complete."); +} -- cgit v1.2.3