mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
Previously, we had lambdas that would capture 'this' and then not use it. Because on modern compilers (e.g., clang 18) this produces a compiler warning, we remove the unnecessary captures.
208 lines
9.6 KiB
C++
208 lines
9.6 KiB
C++
//
|
|
// Copyright 2024 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
#include <uhdlib/usrp/common/max287x.hpp>
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
// Datasheet for max2871:
|
|
// https://www.analog.com/media/en/technical-documentation/data-sheets/MAX2871.pdf
|
|
// Used to confirm the proper register addresses and values
|
|
|
|
// Based on values set in gen_max2871_regs.py and the constructor of max2871
|
|
std::map<uint8_t, uint32_t> expected_init_values = {
|
|
{0, 0x003e8000},
|
|
{1, 0x2000fff9},
|
|
{2, 0x78006e42},
|
|
{3, 0x0100000b},
|
|
{4, 0x6080023c},
|
|
{5, 0x61400005},
|
|
};
|
|
|
|
// Using macro instead of function so that failures print the line associated with the
|
|
// call to UHD_CHECK_REGMAP rather than pointing out the line in UHD_CHECK_REGMAP itself
|
|
#define UHD_CHECK_REGMAP(lo, expected) \
|
|
{ \
|
|
std::map<uint8_t, uint32_t> actual; \
|
|
for (int i = 0; i < 6; i++) { \
|
|
actual.insert(std::pair<uint8_t, uint32_t>(i, lo->get_register(i))); \
|
|
} \
|
|
actual.insert(std::pair<uint8_t, uint32_t>(0, 0)); \
|
|
for (const auto& expected_r : expected) { \
|
|
std::stringstream exp_ss, act_ss; \
|
|
exp_ss << std::hex << expected_r.second; \
|
|
act_ss << std::hex << actual.at(expected_r.first); \
|
|
const std::string exp_str = \
|
|
"R" + std::to_string(expected_r.first) + " == 0x" + exp_ss.str(); \
|
|
const std::string act_str = \
|
|
"R" + std::to_string(expected_r.first) + " == 0x" + act_ss.str(); \
|
|
BOOST_CHECK_EQUAL(exp_str, act_str); \
|
|
} \
|
|
}
|
|
|
|
// Used to set the field of a register to use for an expected value
|
|
void set_field(uint32_t& reg, uint32_t mask, uint32_t shift, uint32_t value)
|
|
{
|
|
reg &= ~(mask << shift);
|
|
reg |= (value & mask) << shift;
|
|
}
|
|
|
|
// Test that the set_register and get_register functions work as expected for all
|
|
// writeable registers (non-address) and also that set_register works when masking
|
|
// for specific registers
|
|
BOOST_AUTO_TEST_CASE(max2871_set_get_register_test)
|
|
{
|
|
auto test_lo =
|
|
max287x_iface::make<max2871>([](const std::vector<uint32_t>&) { /* noop */ });
|
|
|
|
test_lo->set_register(0, 0xFFFFFFF8, 0x12345678);
|
|
test_lo->set_register(1, 0xFFFFFFF8, 0x12345678);
|
|
test_lo->set_register(2, 0xFFFFFFF8, 0x12345678);
|
|
test_lo->set_register(3, 0xFFFFFFF8, 0x12345678);
|
|
test_lo->set_register(4, 0xFFFFFFF8, 0x12345678);
|
|
test_lo->set_register(5, 0xFFFFFFF8, 0x12345678);
|
|
// Registers should differ only by the address bits of each register
|
|
std::map<uint8_t, uint32_t> expected_values = {
|
|
{0, 0x12345678},
|
|
{1, 0x12345679},
|
|
{2, 0x1234567a},
|
|
{3, 0x1234567b},
|
|
{4, 0x1234567c},
|
|
{5, 0x1234567d},
|
|
};
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
|
|
test_lo->set_register(3, 0x0F000000, 0xFFFFFFFF);
|
|
test_lo->set_register(5, 0x000FF000, 0xFFFFFFFF);
|
|
test_lo->set_register(1, 0xF0000000, 0xFFFFFFFF);
|
|
|
|
set_field(expected_values[3], 0xF, 24, 0XF);
|
|
set_field(expected_values[5], 0xFF, 12, 0XFF);
|
|
set_field(expected_values[1], 0xF, 28, 0XF);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
}
|
|
|
|
// Test that registers are at the expected values after initialization
|
|
BOOST_AUTO_TEST_CASE(max2871_init_test)
|
|
{
|
|
auto test_lo =
|
|
max287x_iface::make<max2871>([](const std::vector<uint32_t>&) { /* noop */ });
|
|
|
|
UHD_CHECK_REGMAP(test_lo, expected_init_values);
|
|
}
|
|
|
|
// Test that functions that set output power function correctly
|
|
BOOST_AUTO_TEST_CASE(max2871_set_output_power_test)
|
|
{
|
|
auto test_lo =
|
|
max287x_iface::make<max2871>([](const std::vector<uint32_t>&) { /* noop */ });
|
|
|
|
// set_output_power should only affect RFOUTA Output Power (bits 4:3 of reg 4)
|
|
test_lo->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
|
|
auto expected_values = expected_init_values;
|
|
set_field(expected_values[4], 0x3, 3, max287x_iface::OUTPUT_POWER_5DBM);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
|
|
test_lo->set_output_power(max287x_iface::OUTPUT_POWER_M1DBM);
|
|
set_field(expected_values[4], 0x3, 3, max287x_iface::OUTPUT_POWER_M1DBM);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
|
|
// set_aux_output_power should only affect RFOUTB Output Power (bits 7:6 of reg 4)
|
|
test_lo->set_aux_output_power(max287x_iface::AUX_OUTPUT_POWER_5DBM);
|
|
set_field(expected_values[4], 0x3, 6, max287x_iface::AUX_OUTPUT_POWER_5DBM);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
|
|
test_lo->set_aux_output_power(max287x_iface::AUX_OUTPUT_POWER_M1DBM);
|
|
set_field(expected_values[4], 0x3, 6, max287x_iface::AUX_OUTPUT_POWER_M1DBM);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
|
|
// set_aux_output_power_enable should only affect RFOUTB Output Mode (bit 8 of reg 4)
|
|
test_lo->set_aux_output_power_enable(true);
|
|
set_field(expected_values[4], 0x1, 8, 1);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
test_lo->set_aux_output_power_enable(false);
|
|
set_field(expected_values[4], 0x1, 8, 0);
|
|
UHD_CHECK_REGMAP(test_lo, expected_values);
|
|
}
|
|
|
|
// Test that registers that need to be set for phase synchronization are set correctly
|
|
// Also, test that phase synchronization only advertises as possible when in frac_n mode
|
|
BOOST_AUTO_TEST_CASE(max2871_phase_sync_test)
|
|
{
|
|
auto test_lo =
|
|
max287x_iface::make<max2871>([](const std::vector<uint32_t>&) { /* noop */ });
|
|
|
|
test_lo->set_frequency(2e9, 50e6, 50e6, true);
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::SHUTDOWN_VAS_ENABLED,
|
|
static_cast<max2871_regs_t::shutdown_vas_t>(
|
|
(test_lo->get_register(3) >> 25) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2,
|
|
static_cast<max2871_regs_t::low_noise_and_spur_t>(
|
|
(test_lo->get_register(2) >> 29) & 0x3));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::F01_AUTO,
|
|
static_cast<max2871_regs_t::f01_t>((test_lo->get_register(5) >> 24) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::AUX_OUTPUT_SELECT_FUNDAMENTAL,
|
|
static_cast<max2871_regs_t::aux_output_select_t>(
|
|
(test_lo->get_register(4) >> 9) & 0x1));
|
|
|
|
// For phase synchronization, ensure the following registers are set to the following
|
|
test_lo->config_for_sync(true);
|
|
test_lo->set_frequency(2e9, 50e6, 50e6, true);
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::SHUTDOWN_VAS_DISABLED,
|
|
static_cast<max2871_regs_t::shutdown_vas_t>(
|
|
(test_lo->get_register(3) >> 25) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE,
|
|
static_cast<max2871_regs_t::low_noise_and_spur_t>(
|
|
(test_lo->get_register(2) >> 29) & 0x3));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::F01_FRAC_N,
|
|
static_cast<max2871_regs_t::f01_t>((test_lo->get_register(5) >> 24) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED,
|
|
static_cast<max2871_regs_t::aux_output_select_t>(
|
|
(test_lo->get_register(4) >> 9) & 0x1));
|
|
|
|
test_lo->commit();
|
|
// Have configured for sync, but cannot sync in int_n mode
|
|
BOOST_CHECK(!test_lo->can_sync());
|
|
test_lo->set_frequency(2e9, 50e6, 50e6, false);
|
|
test_lo->commit();
|
|
// Now should be possible to sync in frac_n mode
|
|
BOOST_CHECK(test_lo->can_sync());
|
|
}
|
|
|
|
// Test that the registers that need to be set for int_n vs frac_n mode within
|
|
// the set_frequency function are set correctly
|
|
BOOST_AUTO_TEST_CASE(max2871_set_frequency_int_n_test)
|
|
{
|
|
auto test_lo =
|
|
max287x_iface::make<max2871>([](const std::vector<uint32_t>&) { /* noop */ });
|
|
|
|
// set_frequency with int_n = true, ensure that all registers that need to be set for
|
|
// int_n are set correctly (other arguments set arbitrarily for this test)
|
|
test_lo->set_frequency(2e9, 50e6, 50e6, true);
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::FEEDBACK_SELECT_DIVIDED,
|
|
static_cast<max2871_regs_t::feedback_select_t>(
|
|
(test_lo->get_register(4) >> 23) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::CPL_DISABLED,
|
|
static_cast<max2871_regs_t::cpl_t>((test_lo->get_register(1) >> 29) & 0x3));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::LDF_INT_N,
|
|
static_cast<max2871_regs_t::ldf_t>((test_lo->get_register(2) >> 8) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::INT_N_MODE_INT_N,
|
|
static_cast<max2871_regs_t::int_n_mode_t>(
|
|
(test_lo->get_register(0) >> 31) & 0x1));
|
|
|
|
// set_frequency with int_n = false, ensure that all registers that need to be set for
|
|
// frac_n are set correctly (other arguments set arbitrarily for this test)
|
|
test_lo->set_frequency(2e9, 50e6, 50e6, false);
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::FEEDBACK_SELECT_DIVIDED,
|
|
static_cast<max2871_regs_t::feedback_select_t>(
|
|
(test_lo->get_register(4) >> 23) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::CPL_ENABLED,
|
|
static_cast<max2871_regs_t::cpl_t>((test_lo->get_register(1) >> 29) & 0x3));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::LDF_FRAC_N,
|
|
static_cast<max2871_regs_t::ldf_t>((test_lo->get_register(2) >> 8) & 0x1));
|
|
BOOST_CHECK_EQUAL(max2871_regs_t::INT_N_MODE_FRAC_N,
|
|
static_cast<max2871_regs_t::int_n_mode_t>(
|
|
(test_lo->get_register(0) >> 31) & 0x1));
|
|
}
|