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