1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include <uhd/types/time_spec.hpp>
#include <uhdlib/usrp/dboard/zbx/zbx_cpld_ctrl.hpp>
#include <boost/test/unit_test.hpp>
#include <iostream>
using namespace uhd::usrp::zbx;
struct mock_reg_iface_type
{
uint32_t last_addr = 0;
zbx_cpld_ctrl::chan_t last_chan;
std::map<uint32_t, uint32_t> 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_ant_override_rx_test, zbx_cpld_fixture)
{
cpld.set_rx_antenna_switches(
0, uhd::usrp::zbx::ATR_ADDR_RX, uhd::usrp::zbx::ANTENNA_TXRX);
cpld.set_tx_antenna_switches(
0, ATR_ADDR_0X, uhd::usrp::zbx::ANTENNA_TXRX, tx_amp::HIGHBAND);
cpld.set_tx_antenna_switches(
0, ATR_ADDR_RX, uhd::usrp::zbx::ANTENNA_TXRX, tx_amp::HIGHBAND);
cpld.set_tx_antenna_switches(
0, ATR_ADDR_TX, uhd::usrp::zbx::ANTENNA_TXRX, tx_amp::HIGHBAND);
cpld.set_tx_antenna_switches(
0, ATR_ADDR_XX, uhd::usrp::zbx::ANTENNA_TXRX, tx_amp::HIGHBAND);
// Make sure that configuring the TX antenna switches didn't disconnect the RX
// from the TX/RX port.
BOOST_CHECK_EQUAL(
(mock_reg_iface.memory[0x2000 + 4 * uhd::usrp::zbx::ATR_ADDR_RX] >> 20) & 0x3, 0);
}
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);
}
|