1 //
2 // Copyright 2019-2020 Ettus Research, a National Instruments Brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6
7 #include "x300_mb_controller.hpp"
8 #include "x300_fw_common.h"
9 #include "x300_regs.hpp"
10 #include <uhd/exception.hpp>
11 #include <uhdlib/utils/narrow.hpp>
12 #include <chrono>
13 #include <thread>
14
15 uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
16
17 using namespace uhd;
18 using namespace uhd::rfnoc;
19 using namespace uhd::usrp::x300;
20 using namespace std::chrono_literals;
21
22 namespace {
23 constexpr uint32_t DONT_LOOK_FOR_GPSDO = 0x1234abcdul;
24
25 constexpr uint32_t ADC_SELF_TEST_DURATION = 100; // ms
26
27 // When these regs are fixed, there is another fixme below to actually init the
28 // timekeepers
29 constexpr uint32_t TK_NUM_TIMEKEEPERS = 12; // Read-only
30 constexpr uint32_t TK_REG_BASE = 100;
31 constexpr uint32_t TK_REG_OFFSET = 48;
32 constexpr uint32_t TK_REG_TICKS_NOW_LO = 0x00; // Read-only
33 constexpr uint32_t TK_REG_TICKS_NOW_HI = 0x04; // Read-only
34 constexpr uint32_t TK_REG_TICKS_EVENT_LO = 0x08; // Write-only
35 constexpr uint32_t TK_REG_TICKS_EVENT_HI = 0x0C; // Write-only
36 constexpr uint32_t TK_REG_TICKS_CTRL = 0x10; // Write-only
37 constexpr uint32_t TK_REG_TICKS_PPS_LO = 0x14; // Read-only
38 constexpr uint32_t TK_REG_TICKS_PPS_HI = 0x18; // Read-only
39 constexpr uint32_t TK_REG_TICKS_PERIOD_LO = 0x1C; // Read-Write
40 constexpr uint32_t TK_REG_TICKS_PERIOD_HI = 0x20; // Read-Write
41
42 constexpr char LOG_ID[] = "X300::MB_CTRL";
43
44 constexpr char GPIO_SRC_BANK[] = "FP0";
45 constexpr char GPIO_SRC_RFA[] = "RFA";
46 constexpr char GPIO_SRC_RFB[] = "RFB";
47 constexpr size_t GPIO_SRC_NUM_PINS = 12;
48
49 } // namespace
50
51
52 /******************************************************************************
53 * Structors
54 *****************************************************************************/
x300_mb_controller(const size_t hw_rev,const std::string product_name,uhd::i2c_iface::sptr zpu_i2c,uhd::wb_iface::sptr zpu_ctrl,x300_clock_ctrl::sptr clock_ctrl,uhd::usrp::mboard_eeprom_t mb_eeprom,x300_device_args_t args)55 x300_mb_controller::x300_mb_controller(const size_t hw_rev,
56 const std::string product_name,
57 uhd::i2c_iface::sptr zpu_i2c,
58 uhd::wb_iface::sptr zpu_ctrl,
59 x300_clock_ctrl::sptr clock_ctrl,
60 uhd::usrp::mboard_eeprom_t mb_eeprom,
61 x300_device_args_t args)
62 : _hw_rev(hw_rev)
63 , _product_name(product_name)
64 , _zpu_i2c(zpu_i2c)
65 , _zpu_ctrl(zpu_ctrl)
66 , _clock_ctrl(clock_ctrl)
67 , _mb_eeprom(mb_eeprom)
68 , _args(args)
69 {
70 _fw_regmap = std::make_shared<fw_regmap_t>();
71 _fw_regmap->initialize(*_zpu_ctrl.get(), true);
72 _fw_regmap->ref_freq_reg.write(
73 fw_regmap_t::ref_freq_reg_t::REF_FREQ, uint32_t(args.get_system_ref_rate()));
74
75 // Initialize clock source to generate a valid radio clock. This may change
76 // after configuration is done.
77 // This will configure the LMK and wait for lock
78 x300_mb_controller::set_clock_source(args.get_clock_source());
79 x300_mb_controller::set_time_source(args.get_time_source());
80
81 const size_t num_tks = _zpu_ctrl->peek32(SR_ADDR(SET0_BASE, TK_NUM_TIMEKEEPERS));
82 for (size_t i = 0; i < num_tks; i++) {
83 register_timekeeper(i,
84 std::make_shared<x300_timekeeper>(
85 i, _zpu_ctrl, clock_ctrl->get_master_clock_rate()));
86 }
87
88 init_gps();
89 _radio_refs.reserve(2);
90 }
91
~x300_mb_controller()92 x300_mb_controller::~x300_mb_controller() {}
93
94 /******************************************************************************
95 * Timekeeper APIs
96 *****************************************************************************/
get_ticks_now()97 uint64_t x300_mb_controller::x300_timekeeper::get_ticks_now()
98 {
99 uint32_t ticks_lo = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_NOW_LO));
100 uint32_t ticks_hi = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_NOW_HI));
101 return uint64_t(ticks_lo) | (uint64_t(ticks_hi) << 32);
102 }
103
get_ticks_last_pps()104 uint64_t x300_mb_controller::x300_timekeeper::get_ticks_last_pps()
105 {
106 uint32_t ticks_lo = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_PPS_LO));
107 uint32_t ticks_hi = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_PPS_HI));
108 return uint64_t(ticks_lo) | (uint64_t(ticks_hi) << 32);
109 }
110
set_ticks_now(const uint64_t ticks)111 void x300_mb_controller::x300_timekeeper::set_ticks_now(const uint64_t ticks)
112 {
113 _zpu_ctrl->poke32(
114 get_tk_addr(TK_REG_TICKS_EVENT_LO), narrow_cast<uint32_t>(ticks & 0xFFFFFFFF));
115 _zpu_ctrl->poke32(
116 get_tk_addr(TK_REG_TICKS_EVENT_HI), narrow_cast<uint32_t>(ticks >> 32));
117 _zpu_ctrl->poke32(get_tk_addr(TK_REG_TICKS_CTRL), narrow_cast<uint32_t>(0x1));
118 }
119
set_ticks_next_pps(const uint64_t ticks)120 void x300_mb_controller::x300_timekeeper::set_ticks_next_pps(const uint64_t ticks)
121 {
122 _zpu_ctrl->poke32(
123 get_tk_addr(TK_REG_TICKS_EVENT_LO), narrow_cast<uint32_t>(ticks & 0xFFFFFFFF));
124 _zpu_ctrl->poke32(
125 get_tk_addr(TK_REG_TICKS_EVENT_HI), narrow_cast<uint32_t>(ticks >> 32));
126 _zpu_ctrl->poke32(get_tk_addr(TK_REG_TICKS_CTRL), narrow_cast<uint32_t>(0x2));
127 }
128
set_period(const uint64_t period_ns)129 void x300_mb_controller::x300_timekeeper::set_period(const uint64_t period_ns)
130 {
131 _zpu_ctrl->poke32(get_tk_addr(TK_REG_TICKS_PERIOD_LO),
132 narrow_cast<uint32_t>(period_ns & 0xFFFFFFFF));
133 _zpu_ctrl->poke32(
134 get_tk_addr(TK_REG_TICKS_PERIOD_HI), narrow_cast<uint32_t>(period_ns >> 32));
135 }
136
get_tk_addr(const uint32_t tk_addr)137 uint32_t x300_mb_controller::x300_timekeeper::get_tk_addr(const uint32_t tk_addr)
138 {
139 return SR_ADDR(SET0_BASE, TK_REG_BASE + TK_REG_OFFSET * _tk_idx + tk_addr);
140 }
141
142 /******************************************************************************
143 * Motherboard Control API (see mb_controller.hpp)
144 *****************************************************************************/
init()145 void x300_mb_controller::init()
146 {
147 if (_radio_refs.empty()) {
148 UHD_LOG_WARNING(LOG_ID, "No radio registered! Skipping ADC checks.");
149 return;
150 }
151 // Check ADCs
152 if (_args.get_ext_adc_self_test()) {
153 extended_adc_test(_args.get_ext_adc_self_test_duration() / _radio_refs.size());
154 } else if (_args.get_self_cal_adc_delay()) {
155 constexpr bool apply_delay = true;
156 self_cal_adc_xfer_delay(apply_delay);
157 } else {
158 for (auto& radio : _radio_refs) {
159 radio->self_test_adc(ADC_SELF_TEST_DURATION);
160 }
161 }
162 }
163
get_mboard_name() const164 std::string x300_mb_controller::get_mboard_name() const
165 {
166 return _product_name;
167 }
168
set_time_source(const std::string & source)169 void x300_mb_controller::set_time_source(const std::string& source)
170 {
171 if (source == "internal") {
172 _fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
173 fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);
174 } else if (source == "external") {
175 _fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
176 fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);
177 } else if (source == "gpsdo") {
178 _fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
179 fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);
180 } else {
181 throw uhd::key_error("update_time_source: unknown source: " + source);
182 }
183
184 _current_time_src = source;
185
186 /* TODO - Implement intelligent PPS detection
187 //check for valid pps
188 if (!is_pps_present(mb)) {
189 throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please
190 check the PPS source and try again.") % source).str());
191 }
192 */
193 }
194
get_time_source() const195 std::string x300_mb_controller::get_time_source() const
196 {
197 return _current_time_src;
198 }
199
get_time_sources() const200 std::vector<std::string> x300_mb_controller::get_time_sources() const
201 {
202 return {"internal", "external", "gpsdo"};
203 }
204
set_clock_source(const std::string & source)205 void x300_mb_controller::set_clock_source(const std::string& source)
206 {
207 UHD_LOG_TRACE("X300::MB_CTRL", "Setting clock source to " << source);
208 // Optimize for the case when the current source is internal and we are trying
209 // to set it to internal. This is the only case where we are guaranteed that
210 // the clock has not gone away so we can skip setting the MUX and reseting the LMK.
211 const bool reconfigure_clks = (_current_refclk_src != "internal")
212 or (source != "internal");
213 if (reconfigure_clks) {
214 // Update the clock MUX on the motherboard to select the requested source
215 if (source == "internal") {
216 _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
217 fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);
218 _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1);
219 } else if (source == "external") {
220 _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
221 fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);
222 _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);
223 } else if (source == "gpsdo") {
224 _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
225 fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);
226 _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);
227 } else {
228 throw uhd::key_error("set_clock_source: unknown source: " + source);
229 }
230 _fw_regmap->clock_ctrl_reg.flush();
231
232 // Reset the LMK to make sure it re-locks to the new reference
233 _clock_ctrl->reset_clocks();
234 }
235
236 // Wait for the LMK to lock (always, as a sanity check that the clock is useable)
237 //* Currently the LMK can take as long as 30 seconds to lock to a reference but we
238 // don't
239 //* want to wait that long during initialization.
240 // TODO: Need to verify timeout and settings to make sure lock can be achieved in
241 // < 1.0 seconds
242 double timeout = _initialization_done ? 30.0 : 1.0;
243
244 // The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may
245 // lead to locking issues. So, disable the ref-locked check for older (unsupported)
246 // boards.
247 if (_hw_rev > 4) {
248 if (not wait_for_clk_locked(fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) {
249 // failed to lock on reference
250 if (_initialization_done) {
251 throw uhd::runtime_error(
252 (boost::format("Reference Clock PLL failed to lock to %s source.")
253 % source)
254 .str());
255 } else {
256 // TODO: Re-enable this warning when we figure out a reliable lock time
257 // UHD_LOGGER_WARNING("X300::MB_CTRL") << "Reference clock failed to lock
258 // to " + source + " during device initialization. " <<
259 // "Check for the lock before operation or ignore this warning if using
260 // another clock source." ;
261 }
262 }
263 }
264
265 if (reconfigure_clks) {
266 // Reset the radio clock PLL in the FPGA
267 _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL);
268 _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
269
270 // Wait for radio clock PLL to lock
271 if (not wait_for_clk_locked(
272 fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) {
273 throw uhd::runtime_error(
274 (boost::format("Reference Clock PLL in FPGA failed to lock to %s source.")
275 % source)
276 .str());
277 }
278
279 // Reset the IDELAYCTRL used to calibrate the data interface delays
280 _zpu_ctrl->poke32(
281 SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL);
282 _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
283
284 // Wait for the ADC IDELAYCTRL to be ready
285 if (not wait_for_clk_locked(
286 fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) {
287 throw uhd::runtime_error(
288 (boost::format(
289 "ADC Calibration Clock in FPGA failed to lock to %s source.")
290 % source)
291 .str());
292 }
293
294 // Reset ADCs and DACs
295 reset_codecs();
296 }
297
298 // Update cache value
299 _current_refclk_src = source;
300 }
301
get_clock_source() const302 std::string x300_mb_controller::get_clock_source() const
303 {
304 return _current_refclk_src;
305 }
306
get_clock_sources() const307 std::vector<std::string> x300_mb_controller::get_clock_sources() const
308 {
309 return {"internal", "external", "gpsdo"};
310 }
311
set_sync_source(const std::string & clock_source,const std::string & time_source)312 void x300_mb_controller::set_sync_source(
313 const std::string& clock_source, const std::string& time_source)
314 {
315 device_addr_t sync_args;
316 sync_args["clock_source"] = clock_source;
317 sync_args["time_source"] = time_source;
318 set_sync_source(sync_args);
319 }
320
set_sync_source(const device_addr_t & sync_source)321 void x300_mb_controller::set_sync_source(const device_addr_t& sync_source)
322 {
323 if (sync_source.has_key("clock_source")) {
324 set_clock_source(sync_source["clock_source"]);
325 }
326 if (sync_source.has_key("time_source")) {
327 set_time_source(sync_source["time_source"]);
328 }
329 }
330
get_sync_source() const331 device_addr_t x300_mb_controller::get_sync_source() const
332 {
333 const std::string clock_source = get_clock_source();
334 const std::string time_source = get_time_source();
335 device_addr_t sync_source;
336 sync_source["clock_source"] = clock_source;
337 sync_source["time_source"] = time_source;
338 return sync_source;
339 }
340
get_sync_sources()341 std::vector<device_addr_t> x300_mb_controller::get_sync_sources()
342 {
343 const std::vector<std::pair<std::string, std::string>> clock_time_src_pairs = {
344 // Clock source, Time source
345 {"internal", "internal"},
346 {"external", "internal"},
347 {"external", "external"},
348 {"gpsdo", "gpsdo"},
349 {"gpsdo", "internal"}};
350
351 // Now convert to vector of device_addr_t
352 std::vector<device_addr_t> sync_sources;
353 for (const auto& ct_pair : clock_time_src_pairs) {
354 device_addr_t sync_source;
355 sync_source["clock_source"] = ct_pair.first;
356 sync_source["time_source"] = ct_pair.second;
357 sync_sources.push_back(sync_source);
358 }
359 return sync_sources;
360 }
361
set_clock_source_out(const bool enb)362 void x300_mb_controller::set_clock_source_out(const bool enb)
363 {
364 _clock_ctrl->set_ref_out(enb);
365 }
366
set_time_source_out(const bool enb)367 void x300_mb_controller::set_time_source_out(const bool enb)
368 {
369 _fw_regmap->clock_ctrl_reg.write(
370 fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb ? 1 : 0);
371 }
372
get_sensor(const std::string & name)373 sensor_value_t x300_mb_controller::get_sensor(const std::string& name)
374 {
375 if (name == "ref_locked") {
376 return sensor_value_t("Ref", get_ref_locked(), "locked", "unlocked");
377 }
378 // There are only GPS sensors and ref_locked, so we can take a shortcut here
379 // and directly ask the GPS for its sensor value:
380 if (_sensors.count(name)) {
381 return _gps->get_sensor(name);
382 }
383 throw uhd::key_error(std::string("Invalid sensor name: ") + name);
384 }
385
get_sensor_names()386 std::vector<std::string> x300_mb_controller::get_sensor_names()
387 {
388 return std::vector<std::string>(_sensors.cbegin(), _sensors.cend());
389 }
390
get_eeprom()391 uhd::usrp::mboard_eeprom_t x300_mb_controller::get_eeprom()
392 {
393 return _mb_eeprom;
394 }
395
synchronize(std::vector<mb_controller::sptr> & mb_controllers,const uhd::time_spec_t & time_spec,const bool quiet)396 bool x300_mb_controller::synchronize(std::vector<mb_controller::sptr>& mb_controllers,
397 const uhd::time_spec_t& time_spec,
398 const bool quiet)
399 {
400 if (!mb_controller::synchronize(mb_controllers, time_spec, quiet)) {
401 return false;
402 }
403
404 std::vector<std::shared_ptr<x300_mb_controller>> mb_controller_copy;
405 mb_controller_copy.reserve(mb_controllers.size());
406 for (auto mb_controller : mb_controllers) {
407 if (std::dynamic_pointer_cast<x300_mb_controller>(mb_controller)) {
408 mb_controller_copy.push_back(
409 std::dynamic_pointer_cast<x300_mb_controller>(mb_controller));
410 }
411 }
412 // Now, mb_controller_copy contains only references of mb_controllers that
413 // are actually x300_mb_controllers
414 mb_controllers.clear();
415 for (auto mb_controller : mb_controller_copy) {
416 mb_controllers.push_back(mb_controller);
417 }
418
419 // Now we have the housekeeping out of the way, we can actually start
420 // synchronizing. The X300 needs to sync its DACs. First, we get a reference
421 // to all the radios (and thus to the DACs).
422 std::vector<uhd::usrp::x300::x300_radio_mbc_iface*> radios;
423 radios.reserve(2 * mb_controller_copy.size());
424 for (auto& mbc : mb_controller_copy) {
425 for (auto radio_ref : mbc->_radio_refs) {
426 radios.push_back(radio_ref);
427 }
428 }
429
430 UHD_LOG_TRACE(LOG_ID, "Running DAC sync on " << radios.size() << " radios.");
431
432 // **PRECONDITION**
433 // This function assumes that all the VITA times for "radios" are
434 // synchronized to a common reference, which we did earlier.
435
436 // Get a rough estimate of the cumulative command latency
437 auto t_start = std::chrono::steady_clock::now();
438 for (auto radio : radios) {
439 radio->get_adc_rx_word(); // Discard value. We are just timing the call
440 }
441 auto t_elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
442 std::chrono::steady_clock::now() - t_start);
443 // Add 100% of headroom + uncertainty to the command time
444 uint64_t t_sync_us = (t_elapsed.count() * 2) + 16000 /* Scheduler latency */;
445
446 const double radio_clk_rate = _clock_ctrl->get_master_clock_rate();
447 std::string err_str;
448 // Try to sync 3 times before giving up
449 constexpr size_t MAX_ATTEMPTS = 3;
450 for (size_t attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
451 try {
452 // Reinitialize and resync all DACs
453 for (auto radio : radios) {
454 radio->sync_dac();
455 }
456
457 // Make sure FRAMEP/N is 0
458 for (auto radio : radios) {
459 radio->set_dac_sync(false);
460 }
461
462 // Pick radios[0] as the time reference.
463 uhd::time_spec_t sync_time =
464 mb_controller_copy.front()->get_timekeeper(0)->get_time_now()
465 + uhd::time_spec_t(((double)t_sync_us) / 1e6);
466
467 // Send the sync command
468 for (auto radio : radios) {
469 // Arm FRAMEP/N sync pulse by asserting a rising edge
470 radio->set_dac_sync(true, sync_time);
471 }
472
473 // Reset FRAMEP/N to 0 after 2 clock cycles, and reset command time
474 for (auto radio : radios) {
475 radio->set_dac_sync(false, sync_time + (2.0 / radio_clk_rate));
476 }
477
478 // Wait and check status
479 std::this_thread::sleep_for(std::chrono::microseconds(t_sync_us));
480 for (auto radio : radios) {
481 radio->dac_verify_sync();
482 }
483
484 UHD_LOG_TRACE(LOG_ID, "DAC sync passed on attempt " << attempt);
485 return true;
486 } catch (const uhd::runtime_error& e) {
487 err_str = e.what();
488 RFNOC_LOG_DEBUG("Retrying DAC synchronization: " << err_str);
489 }
490 }
491 throw uhd::runtime_error(err_str);
492 }
493
get_gpio_banks() const494 std::vector<std::string> x300_mb_controller::get_gpio_banks() const
495 {
496 return {GPIO_SRC_BANK};
497 }
498
get_gpio_srcs(const std::string & bank) const499 std::vector<std::string> x300_mb_controller::get_gpio_srcs(const std::string& bank) const
500 {
501 if (bank != GPIO_SRC_BANK) {
502 UHD_LOG_ERROR(LOG_ID,
503 "Invalid GPIO source bank: " << bank << ". Only supported bank is "
504 << GPIO_SRC_BANK);
505 throw uhd::runtime_error(
506 std::string("Invalid GPIO source bank: ") + GPIO_SRC_BANK);
507 }
508 return {GPIO_SRC_RFA, GPIO_SRC_RFB};
509 }
510
get_gpio_src(const std::string & bank)511 std::vector<std::string> x300_mb_controller::get_gpio_src(const std::string& bank)
512 {
513 if (bank != GPIO_SRC_BANK) {
514 UHD_LOG_ERROR(LOG_ID,
515 "Invalid GPIO source bank: " << bank << ". Only supported bank is "
516 << GPIO_SRC_BANK);
517 throw uhd::runtime_error(
518 std::string("Invalid GPIO source bank: ") + GPIO_SRC_BANK);
519 }
520 uint32_t fp_gpio_src = _zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_FP_GPIO_SRC));
521 const auto gpio_srcs = get_gpio_srcs(bank);
522 std::vector<std::string> gpio_src;
523 for (size_t ii = 0; ii < GPIO_SRC_NUM_PINS; ++ii) {
524 const uint32_t this_src = (fp_gpio_src >> (2 * ii)) & 0x3;
525 if (this_src > 1) {
526 UHD_LOG_WARNING(LOG_ID,
527 "get_gpio_src() read back invalid GPIO source index: "
528 << this_src << ". Falling back to " << (this_src & 0x1));
529 }
530 gpio_src.push_back(gpio_srcs[this_src & 0x1]);
531 }
532 return gpio_src;
533 }
534
set_gpio_src(const std::string & bank,const std::vector<std::string> & srcs)535 void x300_mb_controller::set_gpio_src(
536 const std::string& bank, const std::vector<std::string>& srcs)
537 {
538 if (srcs.size() > GPIO_SRC_NUM_PINS) {
539 UHD_LOG_WARNING(LOG_ID, "set_gpio_src(): Provided more sources than pins!");
540 }
541 uint32_t fp_gpio_src = _zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_FP_GPIO_SRC));
542 size_t pins_configured = 0;
543
544 const auto gpio_srcs = get_gpio_srcs(bank);
545 for (auto src : srcs) {
546 const uint32_t pins = [src]() {
547 if (src == GPIO_SRC_RFA) {
548 return 0;
549 }
550 if (src == GPIO_SRC_RFB) {
551 return 1;
552 }
553 UHD_LOG_ERROR(LOG_ID, "Invalid GPIO source provided: " << src);
554 throw uhd::runtime_error("Invalid GPIO source provided!");
555 }();
556 uint32_t pin_mask = ~(uint32_t(0x3) << (2 * pins_configured));
557 fp_gpio_src = (fp_gpio_src & pin_mask) | (pins << 2 * pins_configured);
558 pins_configured++;
559 if (pins_configured > GPIO_SRC_NUM_PINS) {
560 break;
561 }
562 }
563 _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_FP_GPIO_SRC), fp_gpio_src);
564 }
565
566 /******************************************************************************
567 * Private Methods
568 *****************************************************************************/
get_unique_id()569 std::string x300_mb_controller::get_unique_id()
570 {
571 return std::string("X300::MB_CTRL") + ""; // FIXME
572 }
573
init_gps()574 void x300_mb_controller::init_gps()
575 {
576 // otherwise if not disabled, look for the internal GPSDO
577 if (_zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS))
578 != DONT_LOOK_FOR_GPSDO) {
579 UHD_LOG_TRACE("X300::MB_CTRL", "Detecting internal GPSDO....");
580 try {
581 // gps_ctrl will print its own log statements if a GPSDO was found
582 _gps = gps_ctrl::make(x300_make_uart_iface(_zpu_ctrl));
583 } catch (std::exception& e) {
584 UHD_LOGGER_WARNING("X300::MB_CTRL")
585 << "An error occurred making GPSDO control: " << e.what()
586 << " Continuing without GPS.";
587 }
588 if (_gps and _gps->gps_detected()) {
589 auto sensors = _gps->get_sensors();
590 _sensors.insert(sensors.cbegin(), sensors.cend());
591 } else {
592 UHD_LOG_TRACE("X300::MB_CTRL",
593 "No GPS found, setting register to save time on next run.");
594 _zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS),
595 DONT_LOOK_FOR_GPSDO);
596 }
597 } else {
598 UHD_LOG_TRACE("X300::MB_CTRL",
599 "Not detecting internal GPSDO, previous run already failed to find it.");
600 }
601 }
602
reset_codecs()603 void x300_mb_controller::reset_codecs()
604 {
605 for (auto& callback : _reset_cbs) {
606 UHD_LOG_TRACE("X300::MB_CTRL", "Calling DAC/ADC reset callback");
607 callback();
608 }
609 }
610
wait_for_clk_locked(uint32_t which,double timeout)611 bool x300_mb_controller::wait_for_clk_locked(uint32_t which, double timeout)
612 {
613 const auto timeout_time = std::chrono::steady_clock::now()
614 + std::chrono::milliseconds(int64_t(timeout * 1000));
615 do {
616 if (_fw_regmap->clock_status_reg.read(which) == 1) {
617 return true;
618 }
619 std::this_thread::sleep_for(5ms);
620 } while (std::chrono::steady_clock::now() < timeout_time);
621
622 // Check one last time
623 return (_fw_regmap->clock_status_reg.read(which) == 1);
624 }
625
is_pps_present()626 bool x300_mb_controller::is_pps_present()
627 {
628 // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS.
629 // We monitor it for up to 1.5 seconds looking for it to toggle.
630 uint32_t pps_detect =
631 _fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT);
632 const auto timeout_time = std::chrono::steady_clock::now() + 1500ms;
633 while (std::chrono::steady_clock::now() < timeout_time) {
634 std::this_thread::sleep_for(100ms);
635 if (pps_detect
636 != _fw_regmap->clock_status_reg.read(
637 fw_regmap_t::clk_status_reg_t::PPS_DETECT))
638 return true;
639 }
640 return false;
641 }
642
get_ref_locked()643 bool x300_mb_controller::get_ref_locked()
644 {
645 _fw_regmap->clock_status_reg.refresh();
646 return (_fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK)
647 == 1)
648 && (_fw_regmap->clock_status_reg.get(
649 fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK)
650 == 1)
651 && (_fw_regmap->clock_status_reg.get(
652 fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK)
653 == 1);
654 }
655
self_cal_adc_xfer_delay(bool apply_delay)656 void x300_mb_controller::self_cal_adc_xfer_delay(bool apply_delay)
657 {
658 UHD_LOG_INFO("X300", "Running ADC transfer delay self-cal: ");
659
660 // Effective resolution of the self-cal.
661 constexpr size_t NUM_DELAY_STEPS = 100;
662
663 double master_clk_period = (1.0e9 / _clock_ctrl->get_master_clock_rate()); // in ns
664 double delay_start = 0.0;
665 double delay_range = 2 * master_clk_period;
666 double delay_incr = delay_range / NUM_DELAY_STEPS;
667
668 double cached_clk_delay = _clock_ctrl->get_clock_delay(X300_CLOCK_WHICH_ADC0);
669 double fpga_clk_delay = _clock_ctrl->get_clock_delay(X300_CLOCK_WHICH_FPGA);
670
671 // Iterate through several values of delays and measure ADC data integrity
672 std::vector<std::pair<double, bool>> results;
673 for (size_t i = 0; i < NUM_DELAY_STEPS; i++) {
674 // Delay the ADC clock (will set both Ch0 and Ch1 delays)
675 double delay = _clock_ctrl->set_clock_delay(
676 X300_CLOCK_WHICH_ADC0, delay_incr * i + delay_start);
677 wait_for_clk_locked(fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1);
678
679 uint32_t err_code = 0;
680 for (auto& radio : _radio_refs) {
681 // Test each channel (I and Q) individually so as to not accidentally
682 // trigger on the data from the other channel if there is a swap
683
684 // -- Test I Channel --
685 // Put ADC in ramp test mode. Tie the other channel to all ones.
686 radio->set_adc_test_word("ramp", "ones");
687 // Turn on the pattern checker in the FPGA. It will lock when it sees a
688 // zero and count deviations from the expected value
689 radio->set_adc_checker_enabled(false);
690 radio->set_adc_checker_enabled(true);
691 // 50ms @ 200MHz = 10 million samples
692 std::this_thread::sleep_for(std::chrono::milliseconds(50));
693 if (radio->get_adc_checker_locked(true /* I */)) {
694 err_code += radio->get_adc_checker_error_code(true /* I */);
695 } else {
696 err_code += 100; // Increment error code by 100 to indicate no lock
697 }
698
699 // -- Test Q Channel --
700 // Put ADC in ramp test mode. Tie the other channel to all ones.
701 radio->set_adc_test_word("ones", "ramp");
702 // Turn on the pattern checker in the FPGA. It will lock when it sees a
703 // zero and count deviations from the expected value
704 radio->set_adc_checker_enabled(false);
705 radio->set_adc_checker_enabled(true);
706 // 50ms @ 200MHz = 10 million samples
707 std::this_thread::sleep_for(std::chrono::milliseconds(50));
708 if (radio->get_adc_checker_locked(false /* Q */)) {
709 err_code += radio->get_adc_checker_error_code(false /* Q */);
710 } else {
711 err_code += 100; // Increment error code by 100 to indicate no lock
712 }
713 }
714 UHD_LOG_TRACE(
715 LOG_ID, boost::format("XferDelay=%fns, Error=%d") % delay % err_code);
716 results.push_back(std::pair<double, bool>(delay, err_code == 0));
717 }
718
719 // Calculate the valid window
720 // When done win_start_idx will have the first delay value index that caused
721 // no errors, and win_stop_idx will have the last valid delay value index
722 int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1;
723 for (size_t i = 0; i < results.size(); i++) {
724 std::pair<double, bool>& item = results[i];
725 if (item.second) { // If data is stable
726 if (cur_start_idx == -1) { // This is the first window
727 cur_start_idx = i;
728 cur_stop_idx = i;
729 } else { // We are extending the window
730 cur_stop_idx = i;
731 }
732 } else {
733 if (cur_start_idx == -1) { // We haven't yet seen valid data
734 // Do nothing
735 } else if (win_start_idx == -1) { // We passed the first valid window
736 win_start_idx = cur_start_idx;
737 win_stop_idx = cur_stop_idx;
738 } else { // Update cached window if current window is larger
739 double cur_win_len =
740 results[cur_stop_idx].first - results[cur_start_idx].first;
741 double cached_win_len =
742 results[win_stop_idx].first - results[win_start_idx].first;
743 if (cur_win_len > cached_win_len) {
744 win_start_idx = cur_start_idx;
745 win_stop_idx = cur_stop_idx;
746 }
747 }
748 // Reset current window
749 cur_start_idx = -1;
750 cur_stop_idx = -1;
751 }
752 }
753 if (win_start_idx == -1) {
754 throw uhd::runtime_error(
755 "self_cal_adc_xfer_delay: Self calibration failed. Convergence error.");
756 }
757
758 double win_center =
759 (results[win_stop_idx].first + results[win_start_idx].first) / 2.0;
760 const double win_length = results[win_stop_idx].first - results[win_start_idx].first;
761 if (win_length < master_clk_period / 4) {
762 throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. "
763 "Valid window too narrow.");
764 }
765
766 // Cycle slip the relative delay by a clock cycle to prevent sample misalignment
767 // fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need
768 bool cycle_slip = (win_center - fpga_clk_delay >= master_clk_period);
769 if (cycle_slip) {
770 win_center -= master_clk_period;
771 }
772
773 if (apply_delay) {
774 // Apply delay
775 win_center = _clock_ctrl->set_clock_delay(
776 X300_CLOCK_WHICH_ADC0, win_center); // Sets ADC0 and ADC1
777 wait_for_clk_locked(fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1);
778 // Validate
779 for (auto radio_ref : _radio_refs) {
780 radio_ref->self_test_adc(2000);
781 }
782 } else {
783 // Restore delay
784 _clock_ctrl->set_clock_delay(
785 X300_CLOCK_WHICH_ADC0, cached_clk_delay); // Sets ADC0 and ADC1
786 }
787
788 // Teardown
789 for (auto& radio : _radio_refs) {
790 radio->set_adc_test_word("normal", "normal");
791 radio->set_adc_checker_enabled(false);
792 }
793 UHD_LOGGER_INFO(LOG_ID)
794 << (boost::format("ADC transfer delay self-cal done (FPGA->ADC=%.3fns%s, "
795 "Window=%.3fns)")
796 % (win_center - fpga_clk_delay) % (cycle_slip ? " +cyc" : "")
797 % win_length);
798 }
799
extended_adc_test(double duration_s)800 void x300_mb_controller::extended_adc_test(double duration_s)
801 {
802 static const size_t SECS_PER_ITER = 5;
803 RFNOC_LOG_INFO(
804 boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...")
805 % duration_s % SECS_PER_ITER);
806
807 size_t num_iters = static_cast<size_t>(ceil(duration_s / SECS_PER_ITER));
808 size_t num_failures = 0;
809 for (size_t iter = 0; iter < num_iters; iter++) {
810 // Run self-test
811 RFNOC_LOG_INFO(
812 boost::format("Extended ADC Self-Test Iteration %06d... ") % (iter + 1));
813 try {
814 for (auto& radio : _radio_refs) {
815 radio->self_test_adc(SECS_PER_ITER * 1000);
816 }
817 RFNOC_LOG_INFO(boost::format("Extended ADC Self-Test Iteration %06d passed ")
818 % (iter + 1));
819 } catch (std::exception& e) {
820 num_failures++;
821 RFNOC_LOG_ERROR(e.what());
822 }
823 }
824 if (num_failures == 0) {
825 RFNOC_LOG_INFO("Extended ADC Self-Test PASSED");
826 } else {
827 const std::string err_msg =
828 (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)")
829 % num_failures % num_iters)
830 .str();
831 RFNOC_LOG_ERROR(err_msg);
832 throw uhd::runtime_error(err_msg);
833 }
834 }
835