mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
tests: Modularize x4xx_radio_mock to use it in other tests
Move x4xx_radio_mock_reg_iface_t and x400_radio_fixture from radio block test into own file to reuse it more easily in the future.
This commit is contained in:
parent
5fd7feca0a
commit
e1a86a0c63
@ -4,24 +4,15 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
|
||||
#include "../../lib/usrp/x400/x400_radio_control.hpp"
|
||||
#include "../rfnoc_graph_mock_nodes.hpp"
|
||||
#include "x4xx_radio_mock.hpp"
|
||||
#include "x4xx_zbx_mpm_mock.hpp"
|
||||
#include <uhd/rfnoc/actions.hpp>
|
||||
#include <uhd/rfnoc/defaults.hpp>
|
||||
#include <uhd/rfnoc/mock_block.hpp>
|
||||
#include <uhd/utils/log.hpp>
|
||||
#include <uhd/utils/math.hpp>
|
||||
#include <uhdlib/rfnoc/graph.hpp>
|
||||
#include <uhdlib/rfnoc/node_accessor.hpp>
|
||||
#include <uhdlib/usrp/dboard/zbx/zbx_constants.hpp>
|
||||
#include <uhdlib/usrp/dboard/zbx/zbx_dboard.hpp>
|
||||
#include <uhdlib/utils/narrow.hpp>
|
||||
#include <math.h>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
@ -31,247 +22,6 @@ using namespace std::chrono_literals;
|
||||
using namespace uhd::usrp::zbx;
|
||||
using namespace uhd::experts;
|
||||
|
||||
// Redeclare this here, since it's only defined outside of UHD_API
|
||||
noc_block_base::make_args_t::~make_args_t() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
/* This class extends mock_reg_iface_t by adding a constructor that initializes
|
||||
* some of the read memory to contain the memory size for the radio block.
|
||||
*/
|
||||
class x4xx_radio_mock_reg_iface_t : public mock_reg_iface_t
|
||||
{
|
||||
// Start address of CPLD register space
|
||||
static constexpr uint32_t cpld_offset = radio_control_impl::regmap::PERIPH_BASE;
|
||||
// Start address of RFDC control register space
|
||||
static constexpr uint32_t rfdc_offset =
|
||||
radio_control_impl::regmap::PERIPH_BASE + 0x8000;
|
||||
|
||||
public:
|
||||
x4xx_radio_mock_reg_iface_t(size_t num_channels)
|
||||
{
|
||||
for (size_t chan = 0; chan < num_channels; chan++) {
|
||||
const uint32_t reg_compat =
|
||||
radio_control_impl::regmap::REG_COMPAT_NUM
|
||||
+ chan * radio_control_impl::regmap::REG_CHAN_OFFSET;
|
||||
read_memory[reg_compat] = (radio_control_impl::MINOR_COMPAT
|
||||
| (radio_control_impl::MAJOR_COMPAT << 16));
|
||||
}
|
||||
read_memory[radio_control_impl::regmap::REG_RADIO_WIDTH] =
|
||||
(32 /* bits per sample */ << 16) | 1 /* sample per clock */;
|
||||
}
|
||||
|
||||
void _poke_cb(uint32_t addr, uint32_t data, uhd::time_spec_t, bool) override
|
||||
{
|
||||
// Are we on the peripheral?
|
||||
if (addr >= radio_control_impl::regmap::PERIPH_BASE) {
|
||||
// handle all the periphs stuff that is not CPLD here
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we on the CPLD?
|
||||
if (addr >= cpld_offset && addr < rfdc_offset) {
|
||||
_poke_cpld_cb(addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we poking the RFDC controls?
|
||||
if (addr >= rfdc_offset) {
|
||||
_poke_rfdc_cb(addr, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _poke_cpld_cb(const uint32_t addr, const uint32_t data)
|
||||
{
|
||||
switch (addr - cpld_offset) {
|
||||
/// CURRENT_CONFIG_REG
|
||||
case 0x1000:
|
||||
// FIXME: We write to all regs during init
|
||||
// BOOST_REQUIRE(false); // Not a write-register
|
||||
break;
|
||||
/// SW_CONFIG
|
||||
case 0x1008: {
|
||||
// This register is RW so update read_memory
|
||||
read_memory[addr] = data;
|
||||
// If we're in SW-defined mode, also update CURRENT_CONFIG_REG
|
||||
uint32_t& rf_opt = read_memory[cpld_offset + 0x1004];
|
||||
uint32_t& ccr = read_memory[cpld_offset + 0x1000];
|
||||
// Check if RF0_OPTION is SW_DEFINED
|
||||
if ((rf_opt & 0x00FF) == 0) {
|
||||
ccr = (ccr & 0xFF00) | (data & 0x00FF);
|
||||
}
|
||||
// Check if RF1_OPTION is SW_DEFINED
|
||||
if ((rf_opt & 0xFF00) == 0) {
|
||||
ccr = (ccr & 0x00FF) | (data & 0xFF00);
|
||||
}
|
||||
} break;
|
||||
/// LO SPI transactions
|
||||
case 0x1020:
|
||||
_poke_lo_spi(addr, data);
|
||||
return;
|
||||
/// LO SYNC
|
||||
case 0x1024:
|
||||
// We make these bits sticky, because they might get strobed in
|
||||
// multiple calls. In order to see what was strobed within an
|
||||
// API call, we keep bits as they are.
|
||||
read_memory[addr] |= data;
|
||||
return;
|
||||
// TX0 Table Select
|
||||
case 0x4000:
|
||||
case 0x4004:
|
||||
case 0x4008:
|
||||
case 0x400C:
|
||||
case 0x4010:
|
||||
case 0x4014: {
|
||||
read_memory[addr] = data;
|
||||
const uint32_t src_table_offset = data * 4;
|
||||
const uint32_t dst_table_offset = (addr - cpld_offset) - 0x4000;
|
||||
// Now we fake the transaction that copies ?X?_TABLE_* to
|
||||
// ?X?_DSA*
|
||||
read_memory[cpld_offset + 0x3000 + dst_table_offset] =
|
||||
read_memory[cpld_offset + 0x5000 + src_table_offset];
|
||||
}
|
||||
return;
|
||||
// RX0 Table Select
|
||||
case 0x4800:
|
||||
case 0x4804:
|
||||
case 0x4808:
|
||||
case 0x480C:
|
||||
case 0x4810:
|
||||
case 0x4814: {
|
||||
read_memory[addr] = data;
|
||||
const uint32_t src_table_offset = data * 4;
|
||||
const uint32_t dst_table_offset = (addr - cpld_offset) - 0x4800;
|
||||
// Now we fake the transaction that copies ?X?_TABLE_* to
|
||||
// ?X?_DSA*
|
||||
read_memory[cpld_offset + 0x3800 + dst_table_offset] =
|
||||
read_memory[cpld_offset + 0x5800 + src_table_offset];
|
||||
}
|
||||
return;
|
||||
default: // All other CPLD registers are read-write
|
||||
read_memory[addr] = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _poke_rfdc_cb(const uint32_t addr, const uint32_t data)
|
||||
{
|
||||
read_memory[addr] |= data;
|
||||
}
|
||||
|
||||
void _poke_lo_spi(const uint32_t addr, const uint32_t data)
|
||||
{
|
||||
// UHD_LOG_INFO("TEST", "Detected LO SPI transaction!");
|
||||
const uint16_t spi_data = data & 0xFFFF;
|
||||
const uint8_t spi_addr = (data >> 16) & 0x7F;
|
||||
const bool read = bool(data & (1 << 23));
|
||||
const uint8_t lo_sel = (data >> 24) & 0x7;
|
||||
const bool start_xact = bool(data & (1 << 28));
|
||||
// UHD_LOG_INFO("TEST",
|
||||
// "Transaction record: Read: "
|
||||
// << (read ? "yes" : "no") << " Address: " << int(spi_addr) << std::hex
|
||||
// << " Data: 0x" << spi_data << " LO sel: " << int(lo_sel) << std::dec
|
||||
// << " Start Transaction: " << start_xact);
|
||||
if (!start_xact) {
|
||||
// UHD_LOG_INFO("TEST", "Register probably just initialized. Ignoring.");
|
||||
return;
|
||||
}
|
||||
switch (spi_addr) {
|
||||
case 0:
|
||||
_muxout_to_lock = spi_data & (1 << 2);
|
||||
break;
|
||||
case 125:
|
||||
BOOST_REQUIRE(read);
|
||||
read_memory[addr] = 0x2288;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (read) {
|
||||
read_memory[addr] = (read_memory[addr] & 0xFFFF) | (spi_addr << 16)
|
||||
| (lo_sel << 24) | (1 << 31);
|
||||
}
|
||||
if (_muxout_to_lock) {
|
||||
// UHD_LOG_INFO("TEST", "Muxout set to lock. Returning all ones.");
|
||||
read_memory[addr] = 0xFFFF;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool _muxout_to_lock = false;
|
||||
}; // class x4xx_radio_mock_reg_iface_t
|
||||
|
||||
/*
|
||||
* x400_radio_fixture is a class which is instantiated before each test
|
||||
* case is run. It sets up the block container, mock register interface,
|
||||
* and x400_radio_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.
|
||||
*/
|
||||
constexpr size_t DEFAULT_MTU = 8000;
|
||||
|
||||
//! Helper class to make sure we get the most logging regardless of environment
|
||||
// settings
|
||||
struct uhd_log_enabler
|
||||
{
|
||||
uhd_log_enabler(uhd::log::severity_level level)
|
||||
{
|
||||
std::cout << "Setting log level to " << level << "..." << std::endl;
|
||||
uhd::log::set_log_level(level);
|
||||
uhd::log::set_console_level(level);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
};
|
||||
|
||||
struct x400_radio_fixture
|
||||
{
|
||||
x400_radio_fixture()
|
||||
: ule(uhd::log::warning) // Note: When debugging this test, either set
|
||||
// this to a lower level, or create a
|
||||
// uhd_log_enabler in the test-under-test
|
||||
, num_channels(uhd::usrp::zbx::ZBX_NUM_CHANS)
|
||||
, num_input_ports(num_channels)
|
||||
, num_output_ports(num_channels)
|
||||
, reg_iface(std::make_shared<x4xx_radio_mock_reg_iface_t>(num_channels))
|
||||
, rpcs(std::make_shared<uhd::test::x4xx_mock_rpc_server>(device_info))
|
||||
, mbc(std::make_shared<mpmd_mb_controller>(rpcs, device_info))
|
||||
, block_container(get_mock_block(RADIO_BLOCK,
|
||||
num_channels,
|
||||
num_channels,
|
||||
device_info,
|
||||
DEFAULT_MTU,
|
||||
X400,
|
||||
reg_iface,
|
||||
mbc))
|
||||
, test_radio(block_container.get_block<x400_radio_control_impl>())
|
||||
{
|
||||
node_accessor.init_props(test_radio.get());
|
||||
}
|
||||
|
||||
~x400_radio_fixture() {}
|
||||
|
||||
|
||||
// Must remain the first member so we make sure the log level is high
|
||||
uhd_log_enabler ule;
|
||||
const size_t num_channels;
|
||||
const size_t num_input_ports;
|
||||
const size_t num_output_ports;
|
||||
uhd::device_addr_t device_info = uhd::device_addr_t("master_clock_rate=122.88e6");
|
||||
std::shared_ptr<x4xx_radio_mock_reg_iface_t> reg_iface;
|
||||
std::shared_ptr<uhd::test::x4xx_mock_rpc_server> rpcs;
|
||||
mpmd_mb_controller::sptr mbc;
|
||||
|
||||
mock_block_container block_container;
|
||||
std::shared_ptr<x400_radio_control_impl> test_radio;
|
||||
node_accessor_t node_accessor{};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* RFNoC Graph Test
|
||||
*
|
||||
@ -369,57 +119,67 @@ BOOST_FIXTURE_TEST_CASE(x400_radio_test_prop_prop, x400_radio_fixture)
|
||||
mock_source_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 1, {res_source_info::OUTPUT_EDGE, 0});
|
||||
BOOST_CHECK_EQUAL(mock_source_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 0}), 4);
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 0}),
|
||||
4);
|
||||
|
||||
mock_source_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 4, {res_source_info::OUTPUT_EDGE, 1});
|
||||
BOOST_CHECK_EQUAL(mock_source_term.get_edge_property<size_t>
|
||||
("atomic_item_size", {res_source_info::OUTPUT_EDGE, 1}), 4);
|
||||
BOOST_CHECK_EQUAL(mock_source_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 1}),
|
||||
4);
|
||||
|
||||
mock_source_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 9, {res_source_info::OUTPUT_EDGE, 0});
|
||||
BOOST_CHECK_EQUAL(mock_source_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 0}), 36);
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 0}),
|
||||
36);
|
||||
|
||||
mock_source_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 10, {res_source_info::OUTPUT_EDGE, 1});
|
||||
BOOST_CHECK_EQUAL(mock_source_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 1}), 20);
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 1}),
|
||||
20);
|
||||
|
||||
mock_source_term.set_edge_property<size_t>(
|
||||
"mtu", 99, {res_source_info::OUTPUT_EDGE, 0});
|
||||
mock_source_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 25, {res_source_info::OUTPUT_EDGE, 0});
|
||||
BOOST_CHECK_EQUAL(mock_source_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 0}), 96);
|
||||
"atomic_item_size", {res_source_info::OUTPUT_EDGE, 0}),
|
||||
96);
|
||||
|
||||
//repeat for sink
|
||||
// repeat for sink
|
||||
mock_sink_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 1, {res_source_info::INPUT_EDGE, 0});
|
||||
BOOST_CHECK_EQUAL(mock_sink_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 0}), 4);
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 0}),
|
||||
4);
|
||||
|
||||
mock_sink_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 4, {res_source_info::INPUT_EDGE, 1});
|
||||
BOOST_CHECK_EQUAL(mock_sink_term.get_edge_property<size_t>
|
||||
("atomic_item_size", {res_source_info::INPUT_EDGE, 1}), 4);
|
||||
BOOST_CHECK_EQUAL(mock_sink_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 1}),
|
||||
4);
|
||||
|
||||
mock_sink_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 7, {res_source_info::INPUT_EDGE, 0});
|
||||
BOOST_CHECK_EQUAL(mock_sink_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 0}), 28);
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 0}),
|
||||
28);
|
||||
|
||||
mock_sink_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 22, {res_source_info::INPUT_EDGE, 1});
|
||||
BOOST_CHECK_EQUAL(mock_sink_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 1}), 44);
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 1}),
|
||||
44);
|
||||
|
||||
mock_sink_term.set_edge_property<size_t>(
|
||||
"mtu", 179, {res_source_info::INPUT_EDGE, 0});
|
||||
mock_sink_term.set_edge_property<size_t>(
|
||||
"atomic_item_size", 47, {res_source_info::INPUT_EDGE, 0});
|
||||
BOOST_CHECK_EQUAL(mock_sink_term.get_edge_property<size_t>(
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 0}), 176);
|
||||
"atomic_item_size", {res_source_info::INPUT_EDGE, 0}),
|
||||
176);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(zbx_api_freq_tx_test, x400_radio_fixture)
|
||||
@ -803,8 +563,8 @@ BOOST_FIXTURE_TEST_CASE(zbx_lo_tree_test, x400_radio_fixture)
|
||||
const double req_lo2 = iter_lo->at(1);
|
||||
UHD_LOG_INFO(log,
|
||||
"Testing lo1 freq " << req_lo1 / 1e6 << "MHz, lo2 freq "
|
||||
<< req_lo2 / 1e6 << "MHz at center frequency "
|
||||
<< req_freq / 1e6 << "MHz");
|
||||
<< req_lo2 / 1e6 << "MHz at center frequency "
|
||||
<< req_freq / 1e6 << "MHz");
|
||||
tree->access<double>(fe_path / "freq").set(req_freq);
|
||||
const double ret_lo1 =
|
||||
tree->access<double>(fe_path / "los" / ZBX_LO1 / "freq" / "value")
|
||||
@ -1024,7 +784,8 @@ BOOST_FIXTURE_TEST_CASE(zbx_tx_power_api, x400_radio_fixture)
|
||||
// regarding power
|
||||
const double pow_diff =
|
||||
std::abs(tx_given_power - test_radio->get_tx_power_reference(chan));
|
||||
BOOST_CHECK_MESSAGE(pow_diff < 3.0, "power differential is too large: " << pow_diff);
|
||||
BOOST_CHECK_MESSAGE(
|
||||
pow_diff < 3.0, "power differential is too large: " << pow_diff);
|
||||
|
||||
// Back to gain mode
|
||||
gain_coerced = test_radio->set_tx_gain(tx_given_gain, chan);
|
||||
@ -1058,7 +819,7 @@ BOOST_FIXTURE_TEST_CASE(zbx_rx_power_api, x400_radio_fixture)
|
||||
BOOST_CHECK_MESSAGE(pow_diff < 3.0,
|
||||
"power differential is too large ("
|
||||
<< pow_diff << "): Expected close to: " << rx_given_power
|
||||
<< " Actual: " << actual_power << " Frequency: " << (freq/1e6));
|
||||
<< " Actual: " << actual_power << " Frequency: " << (freq / 1e6));
|
||||
|
||||
gain_coerced = test_radio->set_rx_gain(rx_given_gain, chan);
|
||||
BOOST_REQUIRE_EQUAL(gain_coerced, rx_given_gain);
|
||||
|
||||
263
host/tests/rfnoc_block_tests/x4xx_radio_mock.hpp
Normal file
263
host/tests/rfnoc_block_tests/x4xx_radio_mock.hpp
Normal file
@ -0,0 +1,263 @@
|
||||
//
|
||||
// Copyright 2022 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/usrp/x400/x400_radio_control.hpp"
|
||||
#include "x4xx_zbx_mpm_mock.hpp"
|
||||
#include <uhd/rfnoc/mock_block.hpp>
|
||||
#include <uhd/utils/log.hpp>
|
||||
#include <uhdlib/rfnoc/node_accessor.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace uhd;
|
||||
using namespace uhd::rfnoc;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace uhd::usrp::zbx;
|
||||
using namespace uhd::experts;
|
||||
|
||||
// Redeclare this here, since it's only defined outside of UHD_API
|
||||
noc_block_base::make_args_t::~make_args_t() = default;
|
||||
|
||||
namespace {
|
||||
/* This class extends mock_reg_iface_t by adding a constructor that initializes
|
||||
* some of the read memory to contain the memory size for the radio block.
|
||||
*/
|
||||
class x4xx_radio_mock_reg_iface_t : public mock_reg_iface_t
|
||||
{
|
||||
// Start address of CPLD register space
|
||||
static constexpr uint32_t cpld_offset = radio_control_impl::regmap::PERIPH_BASE;
|
||||
// Start address of RFDC control register space
|
||||
static constexpr uint32_t rfdc_offset =
|
||||
radio_control_impl::regmap::PERIPH_BASE + 0x8000;
|
||||
static constexpr uint32_t spi_offset =
|
||||
radio_control_impl::regmap::PERIPH_BASE
|
||||
+ 0xC000 /*DIO Window*/ + 0x2000 /*DIO Regmap*/;
|
||||
|
||||
public:
|
||||
x4xx_radio_mock_reg_iface_t(size_t num_channels)
|
||||
{
|
||||
for (size_t chan = 0; chan < num_channels; chan++) {
|
||||
const uint32_t reg_compat =
|
||||
radio_control_impl::regmap::REG_COMPAT_NUM
|
||||
+ chan * radio_control_impl::regmap::REG_CHAN_OFFSET;
|
||||
read_memory[reg_compat] = (radio_control_impl::MINOR_COMPAT
|
||||
| (radio_control_impl::MAJOR_COMPAT << 16));
|
||||
}
|
||||
read_memory[radio_control_impl::regmap::REG_RADIO_WIDTH] =
|
||||
(32 /* bits per sample */ << 16) | 1 /* sample per clock */;
|
||||
// Ensure that the SPI Status is always SPI_READY
|
||||
read_memory[spi_offset + 0x18] |= 1 << 24;
|
||||
}
|
||||
|
||||
void _poke_cb(uint32_t addr, uint32_t data, uhd::time_spec_t, bool) override
|
||||
{
|
||||
// Are we on the peripheral?
|
||||
if (addr >= radio_control_impl::regmap::PERIPH_BASE) {
|
||||
// handle all the periphs stuff that is not CPLD here
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we on the CPLD?
|
||||
if (addr >= cpld_offset && addr < rfdc_offset) {
|
||||
_poke_cpld_cb(addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we poking the RFDC controls?
|
||||
if (addr >= rfdc_offset) {
|
||||
_poke_rfdc_cb(addr, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _poke_cpld_cb(const uint32_t addr, const uint32_t data)
|
||||
{
|
||||
switch (addr - cpld_offset) {
|
||||
/// CURRENT_CONFIG_REG
|
||||
case 0x1000:
|
||||
// FIXME: We write to all regs during init
|
||||
// BOOST_REQUIRE(false); // Not a write-register
|
||||
break;
|
||||
/// SW_CONFIG
|
||||
case 0x1008: {
|
||||
// This register is RW so update read_memory
|
||||
read_memory[addr] = data;
|
||||
// If we're in SW-defined mode, also update CURRENT_CONFIG_REG
|
||||
uint32_t& rf_opt = read_memory[cpld_offset + 0x1004];
|
||||
uint32_t& ccr = read_memory[cpld_offset + 0x1000];
|
||||
// Check if RF0_OPTION is SW_DEFINED
|
||||
if ((rf_opt & 0x00FF) == 0) {
|
||||
ccr = (ccr & 0xFF00) | (data & 0x00FF);
|
||||
}
|
||||
// Check if RF1_OPTION is SW_DEFINED
|
||||
if ((rf_opt & 0xFF00) == 0) {
|
||||
ccr = (ccr & 0x00FF) | (data & 0xFF00);
|
||||
}
|
||||
} break;
|
||||
/// LO SPI transactions
|
||||
case 0x1020:
|
||||
_poke_lo_spi(addr, data);
|
||||
return;
|
||||
/// LO SYNC
|
||||
case 0x1024:
|
||||
// We make these bits sticky, because they might get strobed in
|
||||
// multiple calls. In order to see what was strobed within an
|
||||
// API call, we keep bits as they are.
|
||||
read_memory[addr] |= data;
|
||||
return;
|
||||
// TX0 Table Select
|
||||
case 0x4000:
|
||||
case 0x4004:
|
||||
case 0x4008:
|
||||
case 0x400C:
|
||||
case 0x4010:
|
||||
case 0x4014: {
|
||||
read_memory[addr] = data;
|
||||
const uint32_t src_table_offset = data * 4;
|
||||
const uint32_t dst_table_offset = (addr - cpld_offset) - 0x4000;
|
||||
// Now we fake the transaction that copies ?X?_TABLE_* to
|
||||
// ?X?_DSA*
|
||||
read_memory[cpld_offset + 0x3000 + dst_table_offset] =
|
||||
read_memory[cpld_offset + 0x5000 + src_table_offset];
|
||||
}
|
||||
return;
|
||||
// RX0 Table Select
|
||||
case 0x4800:
|
||||
case 0x4804:
|
||||
case 0x4808:
|
||||
case 0x480C:
|
||||
case 0x4810:
|
||||
case 0x4814: {
|
||||
read_memory[addr] = data;
|
||||
const uint32_t src_table_offset = data * 4;
|
||||
const uint32_t dst_table_offset = (addr - cpld_offset) - 0x4800;
|
||||
// Now we fake the transaction that copies ?X?_TABLE_* to
|
||||
// ?X?_DSA*
|
||||
read_memory[cpld_offset + 0x3800 + dst_table_offset] =
|
||||
read_memory[cpld_offset + 0x5800 + src_table_offset];
|
||||
}
|
||||
return;
|
||||
default: // All other CPLD registers are read-write
|
||||
read_memory[addr] = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _poke_rfdc_cb(const uint32_t addr, const uint32_t data)
|
||||
{
|
||||
read_memory[addr] |= data;
|
||||
}
|
||||
|
||||
void _poke_lo_spi(const uint32_t addr, const uint32_t data)
|
||||
{
|
||||
// UHD_LOG_INFO("TEST", "Detected LO SPI transaction!");
|
||||
const uint16_t spi_data = data & 0xFFFF;
|
||||
const uint8_t spi_addr = (data >> 16) & 0x7F;
|
||||
const bool read = bool(data & (1 << 23));
|
||||
const uint8_t lo_sel = (data >> 24) & 0x7;
|
||||
const bool start_xact = bool(data & (1 << 28));
|
||||
// UHD_LOG_INFO("TEST",
|
||||
// "Transaction record: Read: "
|
||||
// << (read ? "yes" : "no") << " Address: " << int(spi_addr) << std::hex
|
||||
// << " Data: 0x" << spi_data << " LO sel: " << int(lo_sel) << std::dec
|
||||
// << " Start Transaction: " << start_xact);
|
||||
if (!start_xact) {
|
||||
// UHD_LOG_INFO("TEST", "Register probably just initialized. Ignoring.");
|
||||
return;
|
||||
}
|
||||
switch (spi_addr) {
|
||||
case 0:
|
||||
_muxout_to_lock = spi_data & (1 << 2);
|
||||
break;
|
||||
case 125:
|
||||
BOOST_REQUIRE(read);
|
||||
read_memory[addr] = 0x2288;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (read) {
|
||||
read_memory[addr] = (read_memory[addr] & 0xFFFF) | (spi_addr << 16)
|
||||
| (lo_sel << 24) | (1 << 31);
|
||||
}
|
||||
if (_muxout_to_lock) {
|
||||
// UHD_LOG_INFO("TEST", "Muxout set to lock. Returning all ones.");
|
||||
read_memory[addr] = 0xFFFF;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool _muxout_to_lock = false;
|
||||
}; // class x4xx_radio_mock_reg_iface_t
|
||||
|
||||
/*
|
||||
* x400_radio_fixture is a class which is instantiated before each test
|
||||
* case is run. It sets up the block container, mock register interface,
|
||||
* and x400_radio_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.
|
||||
*/
|
||||
constexpr size_t DEFAULT_MTU = 8000;
|
||||
|
||||
//! Helper class to make sure we get the most logging regardless of environment
|
||||
// settings
|
||||
struct uhd_log_enabler
|
||||
{
|
||||
uhd_log_enabler(uhd::log::severity_level level)
|
||||
{
|
||||
std::cout << "Setting log level to " << level << "..." << std::endl;
|
||||
uhd::log::set_log_level(level);
|
||||
uhd::log::set_console_level(level);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
};
|
||||
|
||||
struct x400_radio_fixture
|
||||
{
|
||||
x400_radio_fixture()
|
||||
: ule(uhd::log::trace) // Note: When debugging this test, either set
|
||||
// this to a lower level, or create a
|
||||
// uhd_log_enabler in the test-under-test
|
||||
, num_channels(uhd::usrp::zbx::ZBX_NUM_CHANS)
|
||||
, num_input_ports(num_channels)
|
||||
, num_output_ports(num_channels)
|
||||
, reg_iface(std::make_shared<x4xx_radio_mock_reg_iface_t>(num_channels))
|
||||
, rpcs(std::make_shared<uhd::test::x4xx_mock_rpc_server>(device_info))
|
||||
, mbc(std::make_shared<mpmd_mb_controller>(rpcs, device_info))
|
||||
, block_container(get_mock_block(RADIO_BLOCK,
|
||||
num_channels,
|
||||
num_channels,
|
||||
device_info,
|
||||
DEFAULT_MTU,
|
||||
X400,
|
||||
reg_iface,
|
||||
mbc))
|
||||
, test_radio(block_container.get_block<x400_radio_control_impl>())
|
||||
{
|
||||
node_accessor.init_props(test_radio.get());
|
||||
}
|
||||
|
||||
~x400_radio_fixture() {}
|
||||
|
||||
|
||||
// Must remain the first member so we make sure the log level is high
|
||||
uhd_log_enabler ule;
|
||||
const size_t num_channels;
|
||||
const size_t num_input_ports;
|
||||
const size_t num_output_ports;
|
||||
uhd::device_addr_t device_info = uhd::device_addr_t("master_clock_rate=122.88e6");
|
||||
std::shared_ptr<x4xx_radio_mock_reg_iface_t> reg_iface;
|
||||
std::shared_ptr<uhd::test::x4xx_mock_rpc_server> rpcs;
|
||||
mpmd_mb_controller::sptr mbc;
|
||||
|
||||
mock_block_container block_container;
|
||||
std::shared_ptr<x400_radio_control_impl> test_radio;
|
||||
node_accessor_t node_accessor{};
|
||||
};
|
||||
} // namespace
|
||||
Loading…
Reference in New Issue
Block a user