diff options
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp | 30 | ||||
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp | 7 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/eiscat.py | 66 |
3 files changed, 72 insertions, 31 deletions
diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp index 8e93ac55b..5b10572f7 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp @@ -74,6 +74,7 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) ); /**** Configure the radio_ctrl itself ***********************************/ + // This also sets the command tick rate: radio_ctrl_impl::set_rate(EISCAT_TICK_RATE); for (size_t chan = 0; chan < _num_ports; chan++) { radio_ctrl_impl::set_rx_frequency(EISCAT_CENTER_FREQ, chan); @@ -521,11 +522,14 @@ void eiscat_radio_ctrl_impl::set_rpc_client( UHD_LOG_INFO( "EISCAT", - "Finalizing dboard initialization using internal PPS" + "Finalizing dboard initialization; initializing JESD cores and ADCs." ); + if (not assert_jesd_cores_initialized()) { + throw uhd::runtime_error("Failed to initialize JESD cores and reset ADCs!"); + } send_sysref(); if (not assert_adcs_deframers()) { - throw uhd::runtime_error("Failed to initialize ADCs and JESD cores!"); + throw uhd::runtime_error("Failed to initialize ADCs and JESD deframers!"); } send_sysref(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); @@ -711,7 +715,20 @@ void eiscat_radio_ctrl_impl::send_sysref() if (_block_args.has_key("use_mpm_sysref")) { _rpcc->notify_with_token("db_0_send_sysref"); } else { + // This value needs to be big enough that we actually hit it between + // reading back the time, and applying the command: + const int CMD_DELAY_MS = 100; + auto sysref_time = get_time_now() + + uhd::time_spec_t(double(CMD_DELAY_MS * 1000)); + uint64_t sysref_time_ticks = sysref_time.to_ticks(EISCAT_TICK_RATE); + // The tick value must be even, or we'd still have the 180 degree phase + // ambiguity! The actual value doesn't matter. + sysref_time_ticks += sysref_time_ticks % 2; + set_command_time(uhd::time_spec_t::from_ticks( + sysref_time_ticks, EISCAT_TICK_RATE + )); this->sr_write("SR_SYSREF", 1); + std::this_thread::sleep_for(std::chrono::milliseconds(CMD_DELAY_MS)); } } @@ -725,6 +742,15 @@ void eiscat_radio_ctrl_impl::enable_counter(bool enable) configure_beams(new_value); } +bool eiscat_radio_ctrl_impl::assert_jesd_cores_initialized() +{ + if (_num_dboards == 1) { + return _rpcc->request_with_token<bool>("db_0_init_jesd_core_reset_adcs"); + } + return _rpcc->request_with_token<bool>("db_0_init_jesd_core_reset_adcs") + and _rpcc->request_with_token<bool>("db_1_init_jesd_core_reset_adcs"); +} + bool eiscat_radio_ctrl_impl::assert_adcs_deframers() { if (_num_dboards == 1) { diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp index 4a99793c9..7b54667da 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp @@ -262,8 +262,15 @@ private: */ void enable_counter(bool enable); + //! Sends a SYSREF pulse. Device arg use_mpm_sysref can be used to send it + // via MPM. Default is to send it via CHDR, in which case calling this + // function *will modify the command time!*, but it will ensure that the + // sysref is sent on an even time void send_sysref(); + //! Run initialization of JESD cores, put ADCs into reset + bool assert_jesd_cores_initialized(); + //! Run initialization of ADCs and deframers; returns success status bool assert_adcs_deframers(); diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py index b9305edc9..ddb2561ff 100644 --- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py +++ b/mpm/python/usrp_mpm/dboard_manager/eiscat.py @@ -427,17 +427,17 @@ class EISCAT(DboardManagerBase): - Turns on the power - Initializes clocking - Synchronizes clocks to reference - - Initializes JESD cores - - Initializes and resets ADCs + - Forwards PPS to the radio block This assumes that an appropriate overlay was loaded. If not, this will fail loudly complaining about missing devices. For operation (streaming), the ADCs and deframers still need to be - initialized. + initialized. See init_jesd_core_reset_adcs(), init_adcs_and_deframers(), + and check_deframer_status(). Note that this function will do nothing if the device was previously - initialized. + initialized, unless force_init was specified in the init args. """ def _init_dboard_regs(): " Create a UIO object to talk to dboard regs " @@ -496,19 +496,6 @@ class EISCAT(DboardManagerBase): offset_error*1e12 )) self.log.info("Clock Synchronization Complete!") - def _check_jesd_cores(db_clk_control, jesd_cores): - " Checks clocks are enabled; init the JESD core; throw on failure. " - if not db_clk_control.check_refclk(): - self.log.error("JESD Cores not getting a MGT RefClk!") - raise RuntimeError("JESD Cores not getting a MGT RefClk") - for jesd_core in jesd_cores: - jesd_core.init() - def _init_and_reset_adcs(spi_ifaces): - " Create ADC control objects; reset ADCs " - adcs = [ADS54J56(spi_iface, self.log) for spi_iface in spi_ifaces] - for adc in adcs: - adc.reset() - return adcs # Go, go, go! if args.get("force_init", False): self.log.info("Forcing re-initialization of dboard.") @@ -527,10 +514,10 @@ class EISCAT(DboardManagerBase): self.log.info("Radio-register UIO object successfully generated!") self._spi_ifaces = _init_spi_devices() # Chips don't have power yet! self.log.info("Loaded SPI interfaces!") - # An occasional failure to train the ADC-FPGA JESD204B link was cropping up during - # dev. A simple solution is to turn off the power and control signals to the DB - # before configuration begins (for the first-run case this call is a no-op, but - # for reconfiguration, it turns off the power). + # We toggle power in order to hard-reset the ADCs. This has proven + # necessary to avoid failures of JESD204B link bringups, even though + # it's not lined out in the data sheets. If power was never on, this'll + # just delay initialization a little. self._deinit_power(self.radio_regs) time.sleep(.100) # This time is arbitrarily assigned and seems to work well. self._init_power(self.radio_regs) # Now, we can talk to chips via SPI @@ -558,14 +545,7 @@ class EISCAT(DboardManagerBase): self.log ) _sync_db_clock(self.clock_synchronizer) - _check_jesd_cores( - self.dboard_clk_control, - self.jesd_cores - ) - self.adc0, self.adc1 = _init_and_reset_adcs(( - self._spi_ifaces['adc0'], self._spi_ifaces['adc1'], - )) - self.log.trace("ADC Reset Sequence Complete!") + # Clocks and PPS are now fully active! return True @@ -579,6 +559,34 @@ class EISCAT(DboardManagerBase): self.radio_regs.poke32(self.SYSREF_CONTROL, 0x1) self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0) + def init_jesd_core_reset_adcs(self): + """ + - Initializes JESD cores + - Initializes and resets ADCs + """ + def _check_jesd_cores(db_clk_control, jesd_cores): + " Checks clocks are enabled; init the JESD core; throw on failure. " + if not db_clk_control.check_refclk(): + self.log.error("JESD Cores not getting a MGT RefClk!") + raise RuntimeError("JESD Cores not getting a MGT RefClk") + for jesd_core in jesd_cores: + jesd_core.init() + def _init_and_reset_adcs(spi_ifaces): + " Create ADC control objects; reset ADCs " + adcs = [ADS54J56(spi_iface, self.log) for spi_iface in spi_ifaces] + for adc in adcs: + adc.reset() + return adcs + _check_jesd_cores( + self.dboard_clk_control, + self.jesd_cores + ) + self.adc0, self.adc1 = _init_and_reset_adcs(( + self._spi_ifaces['adc0'], self._spi_ifaces['adc1'], + )) + self.log.trace("ADC Reset Sequence Complete!") + return True + def init_adcs_and_deframers(self): """ Initialize the ADCs and the JESD deframers. Assumption is that they were |