Example #1
0
 def _full_init(self, slot_idx, master_clock_rate, ref_clock_freq, args):
     """
     Run the full initialization sequence. This will bring everything up
     from scratch: The LMK, JESD cores, the AD9371, calibrations, and
     anything else that is clocking-related.
     Depending on the settings, this can take a fair amount of time.
     """
     # Init some more periphs:
     # The following peripherals are only used during init, so we don't
     # want to hang on to them for the full lifetime of the Magnesium
     # class. This helps us close file descriptors associated with the
     # UIO objects.
     with open_uio(
         label="dboard-regs-{}".format(slot_idx),
         read_only=False
     ) as dboard_ctrl_regs:
         self.log.trace("Creating jesdcore object...")
         jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, slot_idx)
         # Now get cracking with the actual init sequence:
         self.log.trace("Creating dboard clock control object...")
         db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
         self.log.debug("Reset Dboard Clocking and JESD204B interfaces...")
         db_clk_control.reset_mmcm()
         jesdcore.reset()
         self.log.trace("Initializing LMK...")
         self.mg_class.lmk = self._init_lmk(
             self._spi_ifaces['lmk'],
             ref_clock_freq,
             master_clock_rate,
             self._spi_ifaces['phase_dac'],
             self.INIT_PHASE_DAC_WORD,
             self.PHASE_DAC_SPI_ADDR,
         )
         db_clk_control.enable_mmcm()
         # Synchronize DB Clocks
         self._sync_db_clock(
             dboard_ctrl_regs,
             master_clock_rate,
             ref_clock_freq,
             args)
         self.log.debug(
             "Sample Clocks and Phase DAC Configured Successfully!")
         # Clocks and PPS are now fully active!
         self.mykonos.set_master_clock_rate(master_clock_rate)
         self.init_jesd(jesdcore, master_clock_rate, args)
         jesdcore = None # Help with garbage collection
         # That's all that requires access to the dboard regs!
     return True
Example #2
0
    def set_clk_safe_state(self):
        """
        Disable all components that could react badly to a sudden change in
        clocking. After calling this method, all clocks will be off. Calling
        _reinit() will turn them on again.

        The only downstream receiver of the clock that is not reset here are the
        lowband LOs, which are controlled through the host UHD interface.
        """
        if self._init_args is None:
            # Then we're already in a safe state
            return
        # Reset Mykonos, since it receives a copy of the clock from the LMK.
        self.cpld.reset_mykonos(keep_in_reset=True)
        with open_uio(label="dboard-regs-{}".format(self.slot_idx),
                      read_only=False) as dboard_ctrl_regs:
            # Clear the Sample Clock enables and place the MMCM in reset.
            db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
            db_clk_control.reset_mmcm()
            # Place the JESD204b core in reset, mainly to reset QPLL/CPLLs.
            jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, self.slot_idx)
            jesdcore.reset()
Example #3
0
    def init(self, args):
        """
        Execute necessary init dance to bring up dboard
        """
        def _init_lmk(lmk_spi, ref_clk_freq, master_clk_rate, pdac_spi,
                      init_phase_dac_word, phase_dac_spi_addr):
            """
            Sets the phase DAC to initial value, and then brings up the LMK
            according to the selected ref clock frequency.
            Will throw if something fails.
            """
            self.log.trace(
                "Initializing Phase DAC to d{}.".format(init_phase_dac_word))
            pdac_spi.poke16(phase_dac_spi_addr, init_phase_dac_word)
            return LMK04828Mg(lmk_spi, self.spi_lock, ref_clk_freq,
                              master_clk_rate, self.log)

        def _sync_db_clock():
            " Synchronizes the DB clock to the common reference "
            reg_offset = 0x200
            ref_clk_freq = self.ref_clock_freq
            ext_pps_delay = self.EXT_PPS_DELAY
            if args.get('time_source', self.default_time_source) == 'sfp0':
                reg_offset = 0x400
                ref_clk_freq = 62.5e6
                ext_pps_delay = 1  # only 1 flop between the WR core output and the TDC input
            synchronizer = ClockSynchronizer(
                dboard_ctrl_regs,
                self.lmk,
                self._spi_ifaces['phase_dac'],
                reg_offset,
                self.master_clock_rate,
                ref_clk_freq,
                860E-15,  # fine phase shift. TODO don't hardcode. This should live in the EEPROM
                self.INIT_PHASE_DAC_WORD,
                self.PHASE_DAC_SPI_ADDR,
                ext_pps_delay,
                self.N3XX_INT_PPS_DELAY,
                self.slot_idx)
            # The radio clock traces on the motherboard are 69 ps longer for Daughterboard B
            # than Daughterboard A. We want both of these clocks to align at the converters
            # on each board, so adjust the target value for DB B. This is an N3xx series
            # peculiarity and will not apply to other motherboards.
            trace_delay_offset = {0: 0.0e-0, 1: 69.0e-12}[self.slot_idx]
            offset = synchronizer.run(num_meas=[512, 128],
                                      target_offset=trace_delay_offset)
            offset_error = abs(offset)
            if offset_error > 100e-12:
                self.log.error(
                    "Clock synchronizer measured an offset of {:.1f} ps!".
                    format(offset_error * 1e12))
                raise RuntimeError(
                    "Clock synchronizer measured an offset of {:.1f} ps!".
                    format(offset_error * 1e12))
            else:
                self.log.debug(
                    "Residual synchronization error: {:.1f} ps.".format(
                        offset_error * 1e12))
            synchronizer = None
            self.log.debug("Sample Clock Synchronization Complete!")

        ## Go, go, go!
        # Sanity checks and input validation:
        self.log.debug("init() called with args `{}'".format(",".join(
            ['{}={}'.format(x, args[x]) for x in args])))
        if not self._periphs_initialized:
            error_msg = "Cannot run init(), peripherals are not initialized!"
            self.log.error(error_msg)
            raise RuntimeError(error_msg)
        if 'ref_clk_freq' in args:
            self.ref_clock_freq = float(args['ref_clk_freq'])
            assert self.ref_clock_freq in (10e6, 20e6, 25e6)
        assert self.ref_clock_freq is not None
        master_clock_rate = \
            float(args.get('master_clock_rate',
                           self.default_master_clock_rate))
        assert master_clock_rate in (122.88e6, 125e6, 153.6e6), \
                "Invalid master clock rate: {:.02f} MHz".format(
                    master_clock_rate / 1e6)
        master_clock_rate_changed = master_clock_rate != self.master_clock_rate
        if master_clock_rate_changed:
            self.master_clock_rate = master_clock_rate
            self.log.debug("Updating master clock rate to {:.02f} MHz!".format(
                self.master_clock_rate / 1e6))
        # Init some more periphs:
        # The following peripherals are only used during init, so we don't want
        # to hang on to them for the full lifetime of the Magnesium class. This
        # helps us close file descriptors associated with the UIO objects.
        with open_uio(label="dboard-regs-{}".format(self.slot_idx),
                      read_only=False) as dboard_ctrl_regs:
            self.log.trace("Creating jesdcore object...")
            jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, self.slot_idx)
            # Now get cracking with the actual init sequence:
            self.log.trace("Creating dboard clock control object...")
            db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
            self.log.debug("Reset Dboard Clocking and JESD204B interfaces...")
            db_clk_control.reset_mmcm()
            jesdcore.reset()
            self.log.trace("Initializing LMK...")
            self.lmk = _init_lmk(
                self._spi_ifaces['lmk'],
                self.ref_clock_freq,
                self.master_clock_rate,
                self._spi_ifaces['phase_dac'],
                self.INIT_PHASE_DAC_WORD,
                self.PHASE_DAC_SPI_ADDR,
            )
            db_clk_control.enable_mmcm()
            # Synchronize DB Clocks
            _sync_db_clock()
            self.log.debug(
                "Sample Clocks and Phase DAC Configured Successfully!")
            # Clocks and PPS are now fully active!
            self.mykonos.set_master_clock_rate(self.master_clock_rate)
            self.init_jesd(jesdcore, args)
            jesdcore = None  # Help with garbage collection
            # That's all that requires access to the dboard regs!
        if bool(args.get('rfic_digital_loopback')):
            self.log.warning("RF Functionality Disabled: JESD204b digital loopback " \
                             "enabled inside Mykonos!")
            self.mykonos.enable_jesd_loopback(1)
        else:
            self.mykonos.start_radio()
        return True
Example #4
0
    def init(self, args):
        """
        Execute necessary init dance to bring up dboard
        """
        def _init_lmk(lmk_spi, ref_clk_freq, master_clk_rate, pdac_spi,
                      init_phase_dac_word):
            """
            Sets the phase DAC to initial value, and then brings up the LMK
            according to the selected ref clock frequency.
            Will throw if something fails.
            """
            self.log.trace(
                "Initializing Phase DAC to d{}.".format(init_phase_dac_word))
            pdac_spi.poke16(0x0, init_phase_dac_word)
            return LMK04828Mg(lmk_spi, self.spi_lock, ref_clk_freq,
                              master_clk_rate, self.log)

        def _get_clock_synchronizer():
            " Return a clock synchronizer object "
            # Future Work: target_value needs to be tweaked to support
            # heterogeneous rate sync.
            target_value = {
                122.88e6: 128e-9,
                125e6: 128e-9,
                153.6e6: 122e-9
            }[self.master_clock_rate]
            return ClockSynchronizer(
                dboard_ctrl_regs,
                self.lmk,
                self._spi_ifaces['phase_dac'],
                0,  # register offset value. future work.
                self.master_clock_rate,
                self.ref_clock_freq,
                860E-15,  # fine phase shift. TODO don't hardcode. This should live in the EEPROM
                self.INIT_PHASE_DAC_WORD,
                [
                    target_value,
                ],  # target_values
                0x0,  # spi_addr TODO: make this a constant and replace in _sync_db_clock as well
                self.slot_idx)

        def _sync_db_clock(synchronizer):
            " Synchronizes the DB clock to the common reference "
            synchronizer.check_core()
            synchronizer.run_sync(measurement_only=False)
            offset_error = synchronizer.run_sync(measurement_only=True)
            if offset_error > 100e-12:
                self.log.error(
                    "Clock synchronizer measured an offset of {:.1f} ps!".
                    format(offset_error * 1e12))
                raise RuntimeError(
                    "Clock synchronizer measured an offset of {:.1f} ps!".
                    format(offset_error * 1e12))
            else:
                self.log.debug("Residual DAC offset error: {:.1f} ps.".format(
                    offset_error * 1e12))
            self.log.info("Sample Clock Synchronization Complete!")

        ## Go, go, go!
        # Sanity checks and input validation:
        self.log.info("init() called with args `{}'".format(",".join(
            ['{}={}'.format(x, args[x]) for x in args])))
        if not self._periphs_initialized:
            error_msg = "Cannot run init(), peripherals are not initialized!"
            self.log.error(error_msg)
            raise RuntimeError(error_msg)
        if 'ref_clk_freq' in args:
            self.ref_clock_freq = float(args['ref_clk_freq'])
            assert self.ref_clock_freq in (10e6, 20e6, 25e6)
        assert self.ref_clock_freq is not None
        master_clock_rate = \
            float(args.get('master_clock_rate',
                           self.default_master_clock_rate))
        assert master_clock_rate in (122.88e6, 125e6, 153.6e6), \
                "Invalid master clock rate: {:.02f} MHz".format(
                    master_clock_rate / 1e6)
        master_clock_rate_changed = master_clock_rate != self.master_clock_rate
        if master_clock_rate_changed:
            self.master_clock_rate = master_clock_rate
            self.log.debug("Updating master clock rate to {:.02f} MHz!".format(
                self.master_clock_rate / 1e6))
        # Init some more periphs:
        # The following peripherals are only used during init, so we don't want
        # to hang on to them for the full lifetime of the Magnesium class. This
        # helps us close file descriptors associated with the UIO objects.
        with open_uio(label="dboard-regs-{}".format(self.slot_idx),
                      read_only=False) as dboard_ctrl_regs:
            self.log.trace("Creating jesdcore object...")
            jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, self.slot_idx)
            # Now get cracking with the actual init sequence:
            self.log.trace("Creating dboard clock control object...")
            db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
            self.log.debug("Reset Dboard Clocking and JESD204B interfaces...")
            db_clk_control.reset_mmcm()
            jesdcore.reset()
            self.log.trace("Initializing LMK...")
            self.lmk = _init_lmk(
                self._spi_ifaces['lmk'],
                self.ref_clock_freq,
                self.master_clock_rate,
                self._spi_ifaces['phase_dac'],
                self.INIT_PHASE_DAC_WORD,
            )
            db_clk_control.enable_mmcm()
            self.log.info(
                "Sample Clocks and Phase DAC Configured Successfully!")
            # Synchronize DB Clocks
            _sync_db_clock(_get_clock_synchronizer())
            # Clocks and PPS are now fully active!
            self.mykonos.set_master_clock_rate(self.master_clock_rate)
            self.init_jesd(jesdcore, args)
            jesdcore = None  # Help with garbage collection
            # That's all that requires access to the dboard regs!
        self.mykonos.start_radio()
        return True