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
|
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include "lmx2572_regs.hpp"
#include <uhdlib/usrp/common/lmx2572.hpp>
#include <boost/test/unit_test.hpp>
#include <map>
class lmx2572_mem
{
public:
lmx2572_mem()
{
// Copy silicone defaults into mem
for (uint8_t addr = 0; addr < regs.get_num_regs(); addr++) {
mem[addr] = regs.get_reg(addr);
}
}
void poke16(const uint8_t addr, const uint16_t data)
{
if (regs.get_ro_regs().count(addr)) {
throw uhd::runtime_error("Writing to RO reg!");
}
mem[addr] = data;
}
uint16_t peek16(const uint8_t addr)
{
return mem.at(addr);
}
lmx2572_regs_t regs;
std::map<uint8_t, uint16_t> mem;
};
BOOST_AUTO_TEST_CASE(lmx_init_test)
{
auto mem = lmx2572_mem{};
auto lo = lmx2572_iface::make(
[&](const uint8_t addr, const uint16_t data) { mem.poke16(addr, data); },
[&](const uint8_t addr) -> uint16_t { return mem.peek16(addr); },
[](const uhd::time_spec_t&) {});
lo->reset();
}
void UHD_CHECK_REGMAP(
std::map<uint8_t, uint16_t> expected, std::map<uint8_t, uint16_t> actual)
{
for (const auto& expected_r : expected) {
// Little hack so if this fails, we see all the info:
const std::string exp_str = "R" + std::to_string(expected_r.first)
+ "==" + std::to_string(expected_r.second);
const std::string act_str = "R" + std::to_string(expected_r.first)
+ "==" + std::to_string(actual.at(expected_r.first));
BOOST_CHECK_EQUAL(exp_str, act_str);
}
}
BOOST_AUTO_TEST_CASE(lmx_sync_tune_test)
{
auto mem = lmx2572_mem{};
auto lo = lmx2572_iface::make(
[&](const uint8_t addr, const uint16_t data) { mem.poke16(addr, data); },
[&](const uint8_t addr) -> uint16_t { return mem.peek16(addr); },
[](const uhd::time_spec_t&) {});
lo->reset();
// Mimick ZBX settings:
constexpr bool zbx_spur_dodging = false;
lo->set_sync_mode(true);
lo->set_output_enable(lmx2572_iface::output_t::RF_OUTPUT_A, true);
lo->set_output_enable(lmx2572_iface::output_t::RF_OUTPUT_B, false);
// Test Category 1A + SYNC:
lo->set_frequency(
50 * 64e6, 64e6, zbx_spur_dodging);
lo->commit();
// These values are generated with TICS PRO. We don't check all the values,
// mainly the ones related to sync operation.
UHD_CHECK_REGMAP(
std::map<uint8_t, uint16_t>{
{36, 0x0032}, // Lower bits of N-divider, integer part
{42, 0x0000}, // PLL_NUM upper
{43, 0x0000}, // PLL_NUM lower
},
mem.mem);
// Test max frequency just to test boundary conditions:
lo->set_frequency(
100 * 64e6, 64e6, zbx_spur_dodging);
lo->commit();
// Test Category 1B + SYNC:
// Will set CHDIV to 2.
lo->set_frequency(
40 * 64e6, 64e6, zbx_spur_dodging);
lo->commit();
UHD_CHECK_REGMAP(
std::map<uint8_t, uint16_t>{
{36, 0x0028},
{42, 0x0000},
{43, 0x0000},
},
mem.mem);
// Test Category 2 + SYNC:
// Will set CHDIV to 8.
lo->set_frequency(
10 * 64e6, 64e6, zbx_spur_dodging);
lo->commit();
UHD_CHECK_REGMAP(
std::map<uint8_t, uint16_t>{
{36, 0x0050},
{42, 0x0000},
{43, 0x0000},
},
mem.mem);
// VCO_PHASE_SYNC_EN must be off in this case, b/c we're using the SYNC pin
BOOST_CHECK_EQUAL(mem.mem[0] & (1 << 14), 0);
// Test Category 3 + SYNC:
// Will set CHDIV to 1.
lo->set_frequency(
50.5 * 64e6, 64e6, zbx_spur_dodging);
lo->commit();
UHD_CHECK_REGMAP(
std::map<uint8_t, uint16_t>{
{36, 0x0032},
},
mem.mem);
// VCO_PHASE_SYNC_EN must be on in this case
BOOST_CHECK(mem.mem[0] & (1 << 14));
// Will set CHDIV to 2.
lo->set_frequency(
50.5 * 64e6 / 2, 64e6, zbx_spur_dodging);
lo->commit();
UHD_CHECK_REGMAP(
std::map<uint8_t, uint16_t>{
{11, 0xB028}, // PLL_R == 2. Note this is a ZBX-specific design choice.
{36, 0x0032}, // With PLL_R == 2, you would expect this to be 100, but it's
// only half that!
},
mem.mem);
// VCO_PHASE_SYNC_EN must be on in this case
BOOST_CHECK(mem.mem[0] & (1 << 14));
}
|