| 23 |
23 |
#include <uhd/utils/static.hpp>
|
| 24 |
24 |
#include <uhd/utils/assert.hpp>
|
| 25 |
25 |
#include <uhd/utils/algorithm.hpp>
|
|
26 |
#include <uhd/utils/warning.hpp>
|
| 26 |
27 |
#include <uhd/types/ranges.hpp>
|
| 27 |
28 |
#include <uhd/types/dict.hpp>
|
| 28 |
29 |
#include <uhd/usrp/subdev_props.hpp>
|
| ... | ... | |
| 41 |
42 |
/***********************************************************************
|
| 42 |
43 |
* The DBSRX constants
|
| 43 |
44 |
**********************************************************************/
|
| 44 |
|
static const bool dbsrx_debug = true;
|
|
45 |
static const bool dbsrx_debug = false;
|
| 45 |
46 |
|
| 46 |
47 |
static const freq_range_t dbsrx_freq_range(0.8e9, 2.4e9);
|
| 47 |
48 |
|
| ... | ... | |
| 113 |
114 |
for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t)){
|
| 114 |
115 |
int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) ? sizeof(boost::uint32_t) : stop_reg - start_addr + 1;
|
| 115 |
116 |
|
| 116 |
|
//create address to start reading register data
|
| 117 |
|
byte_vector_t address_vector(1);
|
| 118 |
|
address_vector[0] = start_addr;
|
| 119 |
|
|
| 120 |
|
/*
|
| 121 |
|
//send the address
|
| 122 |
|
this->get_iface()->write_i2c(
|
| 123 |
|
_max2118_addr, address_vector
|
| 124 |
|
);
|
| 125 |
|
*/
|
| 126 |
|
|
| 127 |
117 |
//create buffer for register data
|
| 128 |
118 |
byte_vector_t regs_vector(num_bytes);
|
| 129 |
119 |
|
| ... | ... | |
| 135 |
125 |
for(boost::uint8_t i=0; i < num_bytes; i++){
|
| 136 |
126 |
if (i + start_addr >= status_addr){
|
| 137 |
127 |
_max2118_read_regs.set_reg(i + start_addr, regs_vector[i]);
|
| 138 |
|
if(dbsrx_debug) std::cerr << boost::format(
|
| 139 |
|
"DBSRX: set reg 0x%02x, value 0x%04x"
|
| 140 |
|
) % int(i + start_addr) % int(_max2118_read_regs.get_reg(i + start_addr)) << std::endl;
|
| 141 |
128 |
}
|
| 142 |
129 |
if(dbsrx_debug) std::cerr << boost::format(
|
| 143 |
130 |
"DBSRX: read reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
|
| ... | ... | |
| 151 |
138 |
* \return true for locked
|
| 152 |
139 |
*/
|
| 153 |
140 |
bool get_locked(void){
|
| 154 |
|
read_reg(0x0, 0x1);
|
|
141 |
read_reg(0x0, 0x0);
|
| 155 |
142 |
|
| 156 |
143 |
//mask and return lock detect
|
| 157 |
144 |
bool locked = 5 >= _max2118_read_regs.adc && _max2118_read_regs.adc >= 2;
|
| ... | ... | |
| 174 |
161 |
return dboard_base::sptr(new dbsrx(args, 0x67));
|
| 175 |
162 |
}
|
| 176 |
163 |
|
| 177 |
|
//FIXME different dbid for USRP1 also
|
|
164 |
//dbid for USRP2 version
|
| 178 |
165 |
UHD_STATIC_BLOCK(reg_dbsrx_dboard){
|
| 179 |
166 |
//register the factory function for the rx dbid
|
| 180 |
167 |
dboard_manager::register_dboard(0x000D, &make_dbsrx, "DBSRX");
|
| 181 |
168 |
}
|
| 182 |
169 |
|
|
170 |
//dbid for USRP1 version
|
|
171 |
UHD_STATIC_BLOCK(reg_dbsrx_on_usrp1_dboard){
|
|
172 |
//register the factory function for the rx dbid
|
|
173 |
dboard_manager::register_dboard(0x0002, &make_dbsrx, "DBSRX");
|
|
174 |
}
|
|
175 |
|
| 183 |
176 |
/***********************************************************************
|
| 184 |
177 |
* Structors
|
| 185 |
178 |
**********************************************************************/
|
| 186 |
179 |
dbsrx::dbsrx(ctor_args_t args, boost::uint8_t max2118_addr) : rx_dboard_base(args){
|
|
180 |
//warn user about incorrect DBID on USRP1, requires R193 populated
|
|
181 |
if (this->get_iface()->get_mboard_name() == "usrp1" and this->get_rx_id() == 0x000D)
|
|
182 |
uhd::print_warning(
|
|
183 |
str(boost::format(
|
|
184 |
"DBSRX: incorrect dbid\n"
|
|
185 |
"%s expects dbid 0x0002 and R193\n"
|
|
186 |
"found dbid == %d\n"
|
|
187 |
"Please see the daughterboard app notes"
|
|
188 |
) % (this->get_iface()->get_mboard_name()) % (this->get_rx_id().to_pp_string()))
|
|
189 |
);
|
|
190 |
|
|
191 |
//warn user about incorrect DBID on non-USRP1, requires R194 populated
|
|
192 |
if (this->get_iface()->get_mboard_name() != "usrp1" and this->get_rx_id() == 0x0002)
|
|
193 |
uhd::print_warning(
|
|
194 |
str(boost::format(
|
|
195 |
"DBSRX: incorrect dbid\n"
|
|
196 |
"%s expects dbid 0x000D and R194\n"
|
|
197 |
"found dbid == %d\n"
|
|
198 |
"Please see the daughterboard app notes"
|
|
199 |
) % (this->get_iface()->get_mboard_name()) % (this->get_rx_id().to_pp_string()))
|
|
200 |
);
|
|
201 |
|
| 187 |
202 |
//enable only the clocks we need
|
| 188 |
203 |
this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
|
| 189 |
204 |
|
| ... | ... | |
| 197 |
212 |
//send initial register settings
|
| 198 |
213 |
this->send_reg(0x0, 0x5);
|
| 199 |
214 |
|
| 200 |
|
//set defaults for LO, gains
|
|
215 |
//set defaults for LO, gains, and filter bandwidth
|
|
216 |
_bandwidth = 33e6;
|
| 201 |
217 |
set_lo_freq(dbsrx_freq_range.min);
|
|
218 |
|
| 202 |
219 |
BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){
|
| 203 |
220 |
set_gain(dbsrx_gain_ranges[name].min, name);
|
| 204 |
221 |
}
|
| 205 |
222 |
|
| 206 |
|
set_bandwidth(22.27e6); // default bandwidth from datasheet
|
| 207 |
|
|
| 208 |
|
get_locked();
|
|
223 |
set_bandwidth(33e6); // default bandwidth from datasheet
|
| 209 |
224 |
}
|
| 210 |
225 |
|
| 211 |
226 |
dbsrx::~dbsrx(void){
|
| ... | ... | |
| 219 |
234 |
target_freq = std::clip(target_freq, dbsrx_freq_range.min, dbsrx_freq_range.max);
|
| 220 |
235 |
|
| 221 |
236 |
double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0;
|
| 222 |
|
int R=0, N=0, r=0;
|
|
237 |
int R=0, N=0, r=0, m=0;
|
|
238 |
bool update_filter_settings = false;
|
| 223 |
239 |
|
| 224 |
240 |
//choose refclock
|
| 225 |
241 |
std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX);
|
| 226 |
242 |
BOOST_FOREACH(ref_clock, std::reversed(std::sorted(clock_rates))){
|
| 227 |
|
if (ref_clock != 4e6) continue;
|
|
243 |
if (ref_clock > 27.0e6) continue;
|
|
244 |
|
|
245 |
//choose m_divider such that filter tuning constraint is met
|
|
246 |
m = 31;
|
|
247 |
while ((ref_clock/m < 1e6 or ref_clock/m > 2.5e6) and m > 0){ m--; }
|
|
248 |
|
|
249 |
if(dbsrx_debug) std::cerr << boost::format(
|
|
250 |
"DBSRX: trying ref_clock %f and m_divider %d"
|
|
251 |
) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl;
|
|
252 |
|
|
253 |
if (m >= 32) continue;
|
| 228 |
254 |
|
| 229 |
255 |
//choose R
|
| 230 |
256 |
for(r = 0; r <= 6; r += 1) {
|
| ... | ... | |
| 249 |
275 |
}
|
| 250 |
276 |
|
| 251 |
277 |
//Assert because we failed to find a suitable combination of ref_clock, R and N
|
|
278 |
UHD_ASSERT_THROW(ref_clock/(1 << m) < 1e6 or ref_clock/(1 << m) > 2.5e6);
|
| 252 |
279 |
UHD_ASSERT_THROW((pfd_freq < dbsrx_pfd_freq_range.min) or (pfd_freq > dbsrx_pfd_freq_range.max));
|
| 253 |
280 |
UHD_ASSERT_THROW((N < 256) or (N > 32768));
|
| 254 |
281 |
done_loop:
|
| 255 |
282 |
|
|
283 |
if(dbsrx_debug) std::cerr << boost::format(
|
|
284 |
"DBSRX: choose ref_clock %f and m_divider %d"
|
|
285 |
) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl;
|
|
286 |
|
|
287 |
//if ref_clock or m divider changed, we need to update the filter settings
|
|
288 |
if (ref_clock != this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX) or m != _max2118_write_regs.m_divider) update_filter_settings = true;
|
|
289 |
|
| 256 |
290 |
//compute resulting output frequency
|
| 257 |
291 |
actual_freq = pfd_freq * N;
|
| 258 |
292 |
|
| 259 |
293 |
//apply ref_clock, R, and N settings
|
| 260 |
294 |
this->get_iface()->set_clock_rate(dboard_iface::UNIT_RX, ref_clock);
|
| 261 |
295 |
ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
|
|
296 |
_max2118_write_regs.m_divider = m;
|
| 262 |
297 |
_max2118_write_regs.r_divider = (max2118_write_regs_t::r_divider_t) r;
|
| 263 |
298 |
_max2118_write_regs.set_n_divider(N);
|
| 264 |
299 |
_max2118_write_regs.ade_vco_ade_read = max2118_write_regs_t::ADE_VCO_ADE_READ_ENABLED;
|
| 265 |
|
send_reg(0x4,0x4);
|
| 266 |
|
send_reg(0x0,0x2);
|
| 267 |
300 |
|
| 268 |
301 |
//compute prescaler variables
|
| 269 |
302 |
int scaler = actual_freq > 1125e6 ? 2 : 4;
|
| 270 |
303 |
_max2118_write_regs.div2 = scaler == 4 ? max2118_write_regs_t::DIV2_DIV4 : max2118_write_regs_t::DIV2_DIV2;
|
| 271 |
304 |
|
|
305 |
if(dbsrx_debug) std::cerr << boost::format(
|
|
306 |
"DBSRX: scaler %d, actual_freq %f MHz, register bit: %d"
|
|
307 |
) % scaler % (actual_freq/1e6) % int(_max2118_write_regs.div2) << std::endl;
|
|
308 |
|
| 272 |
309 |
//compute vco frequency and select vco
|
| 273 |
310 |
double vco_freq = actual_freq * scaler;
|
| 274 |
|
int vco;
|
| 275 |
311 |
if (vco_freq < 2433e6)
|
| 276 |
|
vco = 0;
|
|
312 |
_max2118_write_regs.osc_band = 0;
|
| 277 |
313 |
else if (vco_freq < 2711e6)
|
| 278 |
|
vco=1;
|
|
314 |
_max2118_write_regs.osc_band = 1;
|
| 279 |
315 |
else if (vco_freq < 3025e6)
|
| 280 |
|
vco=2;
|
|
316 |
_max2118_write_regs.osc_band = 2;
|
| 281 |
317 |
else if (vco_freq < 3341e6)
|
| 282 |
|
vco=3;
|
|
318 |
_max2118_write_regs.osc_band = 3;
|
| 283 |
319 |
else if (vco_freq < 3727e6)
|
| 284 |
|
vco=4;
|
|
320 |
_max2118_write_regs.osc_band = 4;
|
| 285 |
321 |
else if (vco_freq < 4143e6)
|
| 286 |
|
vco=5;
|
|
322 |
_max2118_write_regs.osc_band = 5;
|
| 287 |
323 |
else if (vco_freq < 4493e6)
|
| 288 |
|
vco=6;
|
|
324 |
_max2118_write_regs.osc_band = 6;
|
| 289 |
325 |
else
|
| 290 |
|
vco=7;
|
|
326 |
_max2118_write_regs.osc_band = 7;
|
| 291 |
327 |
|
| 292 |
|
//apply vco selection
|
| 293 |
|
_max2118_write_regs.osc_band = vco;
|
| 294 |
|
send_reg(0x2, 0x2);
|
|
328 |
//send settings over i2c
|
|
329 |
send_reg(0x0, 0x4);
|
| 295 |
330 |
|
| 296 |
331 |
//check vtune for lock condition
|
| 297 |
|
read_reg(0x0, 0x1);
|
|
332 |
read_reg(0x0, 0x0);
|
|
333 |
|
|
334 |
if(dbsrx_debug) std::cerr << boost::format(
|
|
335 |
"DBSRX: initial guess for vco %d, vtune adc %d"
|
|
336 |
) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl;
|
| 298 |
337 |
|
| 299 |
338 |
//if we are out of lock for chosen vco, change vco
|
| 300 |
339 |
while ((_max2118_read_regs.adc == 0) or (_max2118_read_regs.adc == 7)){
|
|
340 |
|
| 301 |
341 |
//vtune is too low, try lower frequency vco
|
| 302 |
342 |
if (_max2118_read_regs.adc == 0){
|
| 303 |
|
UHD_ASSERT_THROW(_max2118_write_regs.osc_band <= 0);
|
|
343 |
if (_max2118_write_regs.osc_band == 0){
|
|
344 |
uhd::print_warning(
|
|
345 |
str(boost::format(
|
|
346 |
"DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"
|
|
347 |
) % int(_max2118_write_regs.osc_band))
|
|
348 |
);
|
|
349 |
UHD_ASSERT_THROW(_max2118_read_regs.adc == 0);
|
|
350 |
}
|
|
351 |
if (_max2118_write_regs.osc_band <= 0) break;
|
| 304 |
352 |
_max2118_write_regs.osc_band -= 1;
|
| 305 |
353 |
}
|
| 306 |
354 |
|
| 307 |
355 |
//vtune is too high, try higher frequency vco
|
| 308 |
356 |
if (_max2118_read_regs.adc == 7){
|
| 309 |
|
UHD_ASSERT_THROW(_max2118_write_regs.osc_band >= 7);
|
|
357 |
if (_max2118_write_regs.osc_band == 7){
|
|
358 |
uhd::print_warning(
|
|
359 |
str(boost::format(
|
|
360 |
"DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"
|
|
361 |
) % int(_max2118_write_regs.osc_band))
|
|
362 |
);
|
|
363 |
UHD_ASSERT_THROW(_max2118_read_regs.adc == 0);
|
|
364 |
}
|
|
365 |
if (_max2118_write_regs.osc_band >= 7) break;
|
| 310 |
366 |
_max2118_write_regs.osc_band += 1;
|
| 311 |
367 |
}
|
| 312 |
368 |
|
|
369 |
if(dbsrx_debug) std::cerr << boost::format(
|
|
370 |
"DBSRX: trying vco %d, vtune adc %d"
|
|
371 |
) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl;
|
|
372 |
|
| 313 |
373 |
//update vco selection and check vtune
|
| 314 |
374 |
send_reg(0x2, 0x2);
|
| 315 |
375 |
read_reg(0x0, 0x0);
|
| 316 |
376 |
}
|
| 317 |
377 |
|
|
378 |
if(dbsrx_debug) std::cerr << boost::format(
|
|
379 |
"DBSRX: final vco %d, vtune adc %d"
|
|
380 |
) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl;
|
|
381 |
|
| 318 |
382 |
//select charge pump bias current
|
| 319 |
383 |
if (_max2118_read_regs.adc <= 2) _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_100UA;
|
| 320 |
384 |
else if (_max2118_read_regs.adc >= 5) _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_400UA;
|
| ... | ... | |
| 335 |
399 |
<< boost::format(" Target Freq=%fMHz\n") % (target_freq/1e6)
|
| 336 |
400 |
<< boost::format(" Actual Freq=%fMHz\n") % (_lo_freq/1e6)
|
| 337 |
401 |
<< std::endl;
|
|
402 |
|
|
403 |
if (update_filter_settings) set_bandwidth(_bandwidth);
|
|
404 |
get_locked();
|
| 338 |
405 |
}
|
| 339 |
406 |
|
| 340 |
407 |
/***********************************************************************
|
| ... | ... | |
| 377 |
444 |
gain = std::clip<float>(gain, dbsrx_gain_ranges["GC1"].min, dbsrx_gain_ranges["GC1"].max);
|
| 378 |
445 |
|
| 379 |
446 |
//voltage level constants
|
| 380 |
|
static const float max_volts = float(2.7), min_volts = float(1.2);
|
|
447 |
static const float max_volts = float(1.2), min_volts = float(2.7);
|
| 381 |
448 |
static const float slope = (max_volts-min_volts)/dbsrx_gain_ranges["GC1"].max;
|
| 382 |
449 |
|
| 383 |
450 |
//calculate the voltage for the aux dac
|
| ... | ... | |
| 413 |
480 |
void dbsrx::set_bandwidth(float bandwidth){
|
| 414 |
481 |
//clip the input
|
| 415 |
482 |
bandwidth = std::clip<float>(bandwidth, 4e6, 33e6);
|
| 416 |
|
|
| 417 |
|
//calculate ref_freq
|
| 418 |
|
float ref_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
|
| 419 |
483 |
|
| 420 |
|
//FIXME this contraint needs to be in the set_freq and needs to assert if it can't hit the range
|
| 421 |
|
//calculate acceptable m_divider for filter tuning
|
| 422 |
|
int m = 1;
|
| 423 |
|
while (ref_freq/m < 1e6 or ref_freq/m > 2.5e6){ m++; }
|
| 424 |
|
_max2118_write_regs.m_divider = m;
|
|
484 |
double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
|
|
485 |
|
|
486 |
//NOTE: _max2118_write_regs.m_divider set in set_lo_freq
|
| 425 |
487 |
|
| 426 |
|
_bandwidth = float((ref_freq/1e6/_max2118_write_regs.m_divider)*(4+0.145*_max2118_write_regs.f_dac)*1e6);
|
|
488 |
//compute f_dac setting
|
|
489 |
_max2118_write_regs.f_dac = std::clip<int>(int((((bandwidth*_max2118_write_regs.m_divider)/ref_clock) - 4)/0.145),0,127);
|
| 427 |
490 |
|
| 428 |
|
_max2118_write_regs.f_dac = int(((bandwidth*_max2118_write_regs.m_divider/ref_freq) - 4)/0.145);
|
|
491 |
//determine actual bandwidth
|
|
492 |
_bandwidth = float((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac));
|
| 429 |
493 |
|
| 430 |
494 |
if (dbsrx_debug) std::cerr << boost::format(
|
| 431 |
495 |
"DBSRX Filter Bandwidth: %f MHz, m: %d, f_dac: %d\n"
|
| 432 |
|
) % (_bandwidth/1e6) % m % int(_max2118_write_regs.f_dac) << std::endl;
|
|
496 |
) % (_bandwidth/1e6) % int(_max2118_write_regs.m_divider) % int(_max2118_write_regs.f_dac) << std::endl;
|
| 433 |
497 |
|
| 434 |
|
this->send_reg(0x3, 0x5);
|
|
498 |
this->send_reg(0x3, 0x4);
|
| 435 |
499 |
}
|
| 436 |
500 |
|
| 437 |
501 |
/***********************************************************************
|