From 261197a18ffd36fafddf21158f92ed34a07abf77 Mon Sep 17 00:00:00 2001 From: Jesse Zhang <65556515+jessezhang-ni@users.noreply.github.com> Date: Wed, 22 Jul 2020 19:14:19 -0500 Subject: rfnoc: Add Switchboard block unit tests --- host/tests/CMakeLists.txt | 4 + .../rfnoc_block_tests/switchboard_block_test.cpp | 157 +++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 host/tests/rfnoc_block_tests/switchboard_block_test.cpp diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index daf9f2976..19047eea7 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -319,6 +319,10 @@ UHD_ADD_RFNOC_BLOCK_TEST( TARGET split_stream_block_test.cpp ) +UHD_ADD_RFNOC_BLOCK_TEST( + TARGET switchboard_block_test.cpp +) + UHD_ADD_RFNOC_BLOCK_TEST( TARGET vector_iir_block_test.cpp ) diff --git a/host/tests/rfnoc_block_tests/switchboard_block_test.cpp b/host/tests/rfnoc_block_tests/switchboard_block_test.cpp new file mode 100644 index 000000000..693923693 --- /dev/null +++ b/host/tests/rfnoc_block_tests/switchboard_block_test.cpp @@ -0,0 +1,157 @@ +// +// 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 +#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 by adding poke and peek hooks that + * monitor writes and reads to the registers implemented within the + * Switchboard RFNoC block hardware and emulating the expected behavior of + * the hardware when those registers are read and written. + */ +const size_t NUM_INPUTS = 4; +const size_t NUM_OUTPUTS = 4; +constexpr size_t DEFAULT_MTU = 8000; + +class switchboard_mock_reg_iface_t : public mock_reg_iface_t +{ +public: + switchboard_mock_reg_iface_t() + { + for (size_t in = 0; in < NUM_INPUTS; in++) + output_select.push_back(0); + for (size_t out = 0; out < NUM_OUTPUTS; out++) + input_select.push_back(0); + } + + virtual void _poke_cb( + uint32_t addr, uint32_t data, uhd::time_spec_t /*time*/, bool /*ack*/) + { + size_t chan = addr / switchboard_block_control::REG_BLOCK_SIZE; + size_t offset = addr % switchboard_block_control::REG_BLOCK_SIZE; + if (offset == switchboard_block_control::REG_DEMUX_SELECT_ADDR) { + output_select[chan] = data; + } else if (offset == switchboard_block_control::REG_MUX_SELECT_ADDR) { + input_select[chan] = data; + } else { + throw uhd::assertion_error("Invalid write from out of bounds address"); + } + } + + virtual void _peek_cb(uint32_t addr, uhd::time_spec_t /*time*/) + { + size_t chan = addr / switchboard_block_control::REG_BLOCK_SIZE; + size_t offset = addr % switchboard_block_control::REG_BLOCK_SIZE; + if (offset == switchboard_block_control::REG_DEMUX_SELECT_ADDR) { + read_memory[addr] = output_select.at(chan); + } else if (offset == switchboard_block_control::REG_MUX_SELECT_ADDR) { + read_memory[addr] = input_select.at(chan); + } else { + throw uhd::assertion_error("Invalid read from out of bounds address"); + } + } + + std::vector input_select{}; + std::vector output_select{}; +}; + +/* switchboard_block_fixture is a class which is instantiated before each test + * case is run. It sets up the block container, mock register interface, + * and mux_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. + */ + +struct switchboard_block_fixture +{ + switchboard_block_fixture() + : reg_iface(std::make_shared()) + , block_container(get_mock_block(SWITCHBOARD_BLOCK, + NUM_INPUTS, + NUM_OUTPUTS, + uhd::device_addr_t(), + DEFAULT_MTU, + ANY_DEVICE, + reg_iface)) + , test_switchboard(block_container.get_block()) + { + node_accessor.init_props(test_switchboard.get()); + } + + std::shared_ptr reg_iface; + mock_block_container block_container; + std::shared_ptr test_switchboard; + node_accessor_t node_accessor{}; +}; + +BOOST_FIXTURE_TEST_CASE(swboard_test_construction, switchboard_block_fixture) +{ + // Check that default register values correctly initialized + for (size_t i = 0; i < NUM_INPUTS; i++) + BOOST_CHECK_EQUAL(reg_iface->output_select.at(i), 0); + for (size_t i = 0; i < NUM_OUTPUTS; i++) + BOOST_CHECK_EQUAL(reg_iface->input_select.at(i), 0); +} + +BOOST_FIXTURE_TEST_CASE(swboard_test_connect, switchboard_block_fixture) +{ + // Check that connect() correctly sets register values + for (size_t i = 0; i < NUM_INPUTS; i++) { + for (size_t o = 0; o < NUM_OUTPUTS; o++) { + test_switchboard->connect(i, o); + BOOST_CHECK_EQUAL( + reg_iface->output_select.at(i), o); + BOOST_CHECK_EQUAL( + reg_iface->input_select.at(o), i); + } + } +} + +BOOST_FIXTURE_TEST_CASE(swboard_test_exceptions, switchboard_block_fixture) +{ + // Check that out of bounds value is not written and exception thrown + BOOST_CHECK_THROW(test_switchboard->connect(0, NUM_OUTPUTS), std::exception); + BOOST_CHECK_THROW(test_switchboard->connect(NUM_INPUTS, 0), std::exception); + BOOST_CHECK_EQUAL(reg_iface->output_select.at(0), 0); + BOOST_CHECK_EQUAL(reg_iface->input_select.at(0), 0); +} + +BOOST_FIXTURE_TEST_CASE(swboard_test_graph, switchboard_block_fixture) +{ + detail::graph_t graph{}; + + mock_edge_node_t source{0, NUM_INPUTS, "MOCK_SOURCE"}; + mock_edge_node_t sink{NUM_OUTPUTS, 0, "MOCK_SINK"}; + + UHD_LOG_INFO("TEST", "Creating graph..."); + for (size_t port = 0; port < NUM_INPUTS; port++) { + graph.connect(&source, + test_switchboard.get(), + graph_edge_t{port, port, detail::graph_t::graph_edge_t::DYNAMIC, true}); + } + for (size_t port = 0; port < NUM_OUTPUTS; port++) { + graph.connect(test_switchboard.get(), + &sink, + graph_edge_t{port, port, detail::graph_t::graph_edge_t::DYNAMIC, true}); + } + UHD_LOG_INFO("TEST", "Committing graph..."); + graph.commit(); + UHD_LOG_INFO("TEST", "Commit complete."); +} -- cgit v1.2.3