// // Copyright 2020 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: GPL-3.0-or-later // #include #include #include #include using namespace uhd::usrp::zbx; struct mock_reg_iface_type { uint32_t last_addr = 0; zbx_cpld_ctrl::chan_t last_chan; std::map memory; uhd::time_spec_t sleep_counter = uhd::time_spec_t(0.0); }; struct zbx_cpld_fixture { zbx_cpld_fixture() : cpld( [&](const uint32_t addr, const uint32_t data, const zbx_cpld_ctrl::chan_t chan) { std::cout << "[MOCKREGS] poke32(" << addr << ", " << data << ")" << std::endl; mock_reg_iface.last_addr = addr; mock_reg_iface.last_chan = chan; mock_reg_iface.memory[addr] = data; }, [&](const uint32_t addr) -> uint32_t { std::cout << "[MOCKREGS] peek32(" << addr << ") => " << mock_reg_iface.memory.at(addr) << std::endl; return mock_reg_iface.memory.at(addr); }, [&](const uhd::time_spec_t& time) { mock_reg_iface.sleep_counter += time; }, "TEST::CPLD") { // nop } mock_reg_iface_type mock_reg_iface; zbx_cpld_ctrl cpld; }; BOOST_FIXTURE_TEST_CASE(zbx_cpld_ctrl_test, zbx_cpld_fixture) { cpld.set_scratch(23); BOOST_CHECK_EQUAL(cpld.get_scratch(), 23); cpld.pulse_lo_sync(0, {zbx_lo_t::TX0_LO1}); BOOST_CHECK_EQUAL(mock_reg_iface.memory[0x1024], 1); mock_reg_iface.memory[0x1024] = 0; // Make sure there are no caching issues: cpld.pulse_lo_sync(0, {zbx_lo_t::TX0_LO1}); BOOST_CHECK_EQUAL(mock_reg_iface.memory[0x1024], 1); // Now all: cpld.pulse_lo_sync(0, {zbx_lo_t::TX0_LO1, zbx_lo_t::TX0_LO2, zbx_lo_t::TX1_LO1, zbx_lo_t::TX1_LO2, zbx_lo_t::RX0_LO1, zbx_lo_t::RX0_LO2, zbx_lo_t::RX1_LO1, zbx_lo_t::RX1_LO2}); BOOST_CHECK_EQUAL(mock_reg_iface.memory[0x1024], 0xFF); mock_reg_iface.memory[0x1024] = 0; cpld.set_lo_sync_bypass(true); BOOST_CHECK_THROW(cpld.pulse_lo_sync(0, {zbx_lo_t::TX0_LO1}), uhd::runtime_error); BOOST_CHECK_EQUAL(mock_reg_iface.memory[0x1024], 0x100); } BOOST_FIXTURE_TEST_CASE(zbx_tx_amp_test, zbx_cpld_fixture) { cpld.set_tx_antenna_switches( 0, 0, uhd::usrp::zbx::DEFAULT_TX_ANTENNA, tx_amp::HIGHBAND); BOOST_CHECK(tx_amp::HIGHBAND == cpld.get_tx_amp_settings(0, 0, false)); mock_reg_iface.memory[0x2000] = 0; BOOST_CHECK(tx_amp::HIGHBAND == cpld.get_tx_amp_settings(0, 0, false)); BOOST_CHECK(tx_amp::HIGHBAND != cpld.get_tx_amp_settings(0, 0, true)); BOOST_CHECK(tx_amp::BYPASS == cpld.get_tx_amp_settings(0, 0, false)); } BOOST_FIXTURE_TEST_CASE(zbx_get_set_dsa_test, zbx_cpld_fixture) { // We only test the first table index constexpr size_t dsa_table_index = 0; for (const size_t chan : {0, 1}) { const uint32_t tx_dsa_table_addr = 0x3000 + chan * 0x400; const uint32_t rx_dsa_table_addr = 0x3800 + chan * 0x400; auto& tx_dsa_reg = mock_reg_iface.memory[tx_dsa_table_addr]; auto& rx_dsa_reg = mock_reg_iface.memory[rx_dsa_table_addr]; // We'll skip DSA3A/B, because they work just like the rest and would // make this test much longer and less readable without adding much test // coverage. for (const auto dsa : {zbx_cpld_ctrl::dsa_type::DSA1, zbx_cpld_ctrl::dsa_type::DSA2}) { const size_t tx_shift = (dsa == zbx_cpld_ctrl::dsa_type::DSA1) ? 0 : 8; const size_t rx_shift = (dsa == zbx_cpld_ctrl::dsa_type::DSA1) ? 0 : 4; // 0xB and 0xC are just random attenuation values. They are valid // for both TX and RX. cpld.set_tx_dsa(chan, dsa_table_index, dsa, 0xB); BOOST_CHECK_EQUAL((tx_dsa_reg >> tx_shift) & 0x1F, 0xB); tx_dsa_reg = 0x0C0C; BOOST_CHECK_EQUAL(0xB, cpld.get_tx_dsa(chan, dsa_table_index, dsa, false)); BOOST_CHECK_EQUAL(0xC, cpld.get_tx_dsa(chan, dsa_table_index, dsa, true)); cpld.set_rx_dsa(chan, 0, dsa, 0xB); BOOST_CHECK_EQUAL((rx_dsa_reg >> rx_shift) & 0xF, 0xB); rx_dsa_reg = 0xCC; BOOST_CHECK_EQUAL(0xB, cpld.get_rx_dsa(chan, dsa_table_index, dsa, false)); BOOST_CHECK_EQUAL(0xC, cpld.get_rx_dsa(chan, dsa_table_index, dsa, true)); } } } BOOST_FIXTURE_TEST_CASE(zbx_set_from_table_test, zbx_cpld_fixture) { constexpr uint32_t tx_sel_addr = 0x4000; constexpr size_t chan = 0; constexpr uint8_t idx = 2; auto& tx_table_select = mock_reg_iface.memory[tx_sel_addr + idx * 4]; cpld.set_tx_gain_switches(chan, idx, 23); BOOST_REQUIRE_EQUAL(tx_table_select, 23); }