Exemplo n.º 1
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.NIJESDCore(dboard_ctrl_regs, self.slot_idx,
                                             **MagnesiumInitManager.JESD_DEFAULT_ARGS)
            jesdcore.reset()
Exemplo n.º 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.
     """
     if self._init_args is None:
         # Then we're already in a safe state
         return
     # Put the ADC and the DAC in a safe state because they receive a LMK's clock.
     # The DAC37J82 datasheet only recommends disabling its analog output before
     # a clock is provided to the chip.
     self.dac.tx_enable(False)
     self.adc.power_down_channel(True)
     with open_uio(
         label="dboard-regs-{}".format(self.slot_idx),
         read_only=False
     ) as radio_regs:
         # Clear the Sample Clock enables and place the MMCM in reset.
         db_clk_control = DboardClockControl(radio_regs, self.log)
         db_clk_control.reset_mmcm()
         # Place the JESD204b core in reset, mainly to reset QPLL/CPLLs.
         jesdcore = nijesdcore.NIJESDCore(radio_regs, self.slot_idx,
                                          **RhodiumInitManager.JESD_DEFAULT_ARGS)
         jesdcore.reset()
Exemplo n.º 3
0
 def dump_jesd_core(self):
     " Debug method to dump all JESD core regs "
     with open_uio(label="dboard-regs-{}".format(self.slot_idx),
                   read_only=False) as dboard_ctrl_regs:
         for i in range(0x2000, 0x2110, 0x10):
             print(("0x%04X " % i), end=' ')
             for j in range(0, 0x10, 0x4):
                 print(("%08X" % dboard_ctrl_regs.peek32(i + j)), end=' ')
             print("")
Exemplo n.º 4
0
 def dbcore_poke(self, addr, data):
     """
     Debug for accessing the DB Core registers via the RPC shell.
     """
     with open_uio(
         label="dboard-regs-{}".format(self.slot_idx),
         read_only=False
     ) as dboard_ctrl_regs:
         self.log.trace("Writing DB Core Register 0x{:04X} with 0x{:08X}...".format(addr, data))
         dboard_ctrl_regs.poke32(addr, data)
Exemplo n.º 5
0
 def dbcore_poke(self, addr, data):
     """
     Debug for accessing the DB Core registers via the RPC shell.
     """
     with open_uio(label="dboard-regs-{}".format(self.slot_idx),
                   read_only=False) as dboard_ctrl_regs:
         self.log.trace(
             "Writing DB Core Register 0x{:04X} with 0x{:08X}...".format(
                 addr, data))
         dboard_ctrl_regs.poke32(addr, data)
Exemplo n.º 6
0
 def dump_jesd_core(self):
     """
     Debug for reading out all JESD core registers via RPC shell
     """
     with open_uio(label="dboard-regs-{}".format(slot_idx),
                   read_only=False) as radio_regs:
         for i in range(0x2000, 0x2110, 0x10):
             print(("0x%04X " % i), end=' ')
             for j in range(0, 0x10, 0x4):
                 print(("%08X" % radio_regs.peek32(i + j)), end=' ')
             print("")
Exemplo n.º 7
0
 def dbcore_peek(self, addr):
     """
     Debug for accessing the DB Core registers via the RPC shell.
     """
     with open_uio(
         label="dboard-regs-{}".format(self.slot_idx),
         read_only=False
     ) as dboard_ctrl_regs:
         rd_data = dboard_ctrl_regs.peek32(addr)
         self.log.trace("DB Core Register 0x{:04X} response: 0x{:08X}".format(addr, rd_data))
         return rd_data
Exemplo n.º 8
0
 def dump_jesd_core(self):
     " Debug method to dump all JESD core regs "
     with open_uio(
         label="dboard-regs-{}".format(self.slot_idx),
         read_only=False
     ) as dboard_ctrl_regs:
         for i in range(0x2000, 0x2110, 0x10):
             print(("0x%04X " % i), end=' ')
             for j in range(0, 0x10, 0x4):
                 print(("%08X" % dboard_ctrl_regs.peek32(i + j)), end=' ')
             print("")
Exemplo n.º 9
0
 def dbcore_peek(self, addr):
     """
     Debug for accessing the DB Core registers via the RPC shell.
     """
     with open_uio(label="dboard-regs-{}".format(self.slot_idx),
                   read_only=False) as dboard_ctrl_regs:
         rd_data = dboard_ctrl_regs.peek32(addr)
         self.log.trace(
             "DB Core Register 0x{:04X} response: 0x{:08X}".format(
                 addr, rd_data))
         return rd_data
Exemplo n.º 10
0
 def dump_jesd_core(self):
     """
     Debug for reading out all JESD core registers via RPC shell
     """
     with open_uio(
         label="dboard-regs-{}".format(self.slot_idx),
         read_only=False
     ) as radio_regs:
         for i in range(0x2000, 0x2110, 0x10):
             print(("0x%04X " % i), end=' ')
             for j in range(0, 0x10, 0x4):
                 print(("%08X" % radio_regs.peek32(i + j)), end=' ')
             print("")
Exemplo n.º 11
0
def run_aurora_bist(
        device_args,
        product_id,
        master,
        slave=None,
        requested_rate=1300*8e6,
        aurora_image_type='AA'):
    """
    Spawn a BER test
    """
    from usrp_mpm import aurora_control
    from usrp_mpm.sys_utils.uio import open_uio

    class DummyContext(object):
        """Dummy class for context managers"""
        def __enter__(self):
            return

        def __exit__(self, exc_type, exc_value, traceback):
            return exc_type is None

    # Go, go, go!
    try:
        assert_aurora_image(master, slave, device_args, product_id, aurora_image_type)
        with open_uio(label=master, read_only=False) as master_au_uio:
            master_au_ctrl = aurora_control.AuroraControl(master_au_uio)
            with open_uio(label=slave, read_only=False)\
                    if slave is not None else DummyContext() as slave_au_uio:
                slave_au_ctrl = aurora_control.AuroraControl(slave_au_uio)\
                    if slave is not None else None
                return master_au_ctrl.run_ber_loopback_bist(
                    duration=10,
                    requested_rate=requested_rate,
                    slave=slave_au_ctrl,
                )
    except Exception as ex:
        print("Unexpected exception: {}".format(str(ex)))
        exit(1)
Exemplo n.º 12
0
def run_aurora_bist(device_args,
                    product_id,
                    master,
                    slave=None,
                    requested_rate=1300 * 8e6,
                    aurora_image_type='AA'):
    """
    Spawn a BER test
    """
    from usrp_mpm import aurora_control
    from usrp_mpm.sys_utils.uio import open_uio

    class DummyContext(object):
        """Dummy class for context managers"""
        def __enter__(self):
            return

        def __exit__(self, exc_type, exc_value, traceback):
            return exc_type is None

    # Go, go, go!
    try:
        assert_aurora_image(master, slave, device_args, product_id,
                            aurora_image_type)
        with open_uio(label=master, read_only=False) as master_au_uio:
            master_au_ctrl = aurora_control.AuroraControl(master_au_uio)
            with open_uio(label=slave, read_only=False)\
                    if slave is not None else DummyContext() as slave_au_uio:
                slave_au_ctrl = aurora_control.AuroraControl(slave_au_uio)\
                    if slave is not None else None
                return master_au_ctrl.run_ber_loopback_bist(
                    duration=10,
                    requested_rate=requested_rate,
                    slave=slave_au_ctrl,
                )
    except Exception as ex:
        print("Unexpected exception: {}".format(str(ex)))
        exit(1)
Exemplo n.º 13
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.NIJESDCore(dboard_ctrl_regs, slot_idx, **self.JESD_DEFAULT_ARGS)
         # 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!
         if args.get('skip_rfic', None) is None:
             async_exec(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
Exemplo n.º 14
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.NIJESDCore(dboard_ctrl_regs, slot_idx, **self.JESD_DEFAULT_ARGS)
         # 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!
         if args.get('skip_rfic', None) == None:
             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
Exemplo n.º 15
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()
Exemplo n.º 16
0
    def init(self, args):
        """
        Run the full initialization sequence. This will bring everything up
        from scratch: The LMK, JESD cores, the AD9695, the DAC37J82, and
        anything else that is clocking-related.
        Depending on the settings, this can take a fair amount of time.
        """
        # Input validation on RX margin tests (@ FPGA and DAC)
        # By accepting the rx_eyescan/tx_prbs argument being str or bool, one may
        # request an eyescan measurement to be performed from either the USRP's
        # shell (i.e. using --default-args) or from the host's MPM shell.
        perform_rx_eyescan = False
        if 'rx_eyescan' in args:
            perform_rx_eyescan = (args['rx_eyescan'] == 'True') or (args['rx_eyescan'] == True)
        if perform_rx_eyescan:
            self.log.trace("Adding RX eye scan PMA enable to JESD args.")
            self.JESD_DEFAULT_ARGS["enable_rx_eyescan"] = True
        perform_tx_prbs = False
        if 'tx_prbs' in args:
            perform_tx_prbs = (args['tx_prbs'] == 'True') or (args['tx_prbs'] == True)

        # Latency across the JESD204B TX/RX links should remain constant and
        # deterministic across the supported sampling_clock_rate values.
        # After testing the roundtrip latency (i.e. FPGA -> TX -> RX -> FPGA),
        # it was found that a different set of SYSREF delay values are required
        # for sampling_clock_rate = 400 MSPS to achieve latency consistency.
        self.JESD_DEFAULT_ARGS['rx_sysref_delay'] = \
          self.RX_SYSREF_DLY_DIC[self.rh_class.sampling_clock_rate]

        # Bringup Sequence.
        #   1. Prerequisites (include opening mmaps)
        #   2. Initialize LMK and bringup clocks.
        #   3. Synchronize DB Clocks.
        #   4. Initialize FPGA JESD IP.
        #   5. DAC Configuration.
        #   6. ADC Configuration.
        #   7. JESD204B Initialization.
        #   8. CPLD Gain Tables Initialization.

        # 1. Prerequisites
        # Open FPGA IP (Clock control and JESD core).
        self.log.trace("Creating gain table object...")
        self.gain_table_loader = GainTableRh(
            self._spi_ifaces['cpld'],
            self._spi_ifaces['cpld_gain_loader'],
            self.log)

        with open_uio(
            label="dboard-regs-{}".format(self.rh_class.slot_idx),
            read_only=False
        ) as radio_regs:
            self.log.trace("Creating dboard clock control object")
            db_clk_control = DboardClockControl(radio_regs, self.log)
            self.log.trace("Creating jesdcore object")
            jesdcore = nijesdcore.NIJESDCore(radio_regs,
                                             self.rh_class.slot_idx,
                                             **self.JESD_DEFAULT_ARGS)

            # 2. Initialize LMK and bringup clocks.
            # Disable FPGA MMCM's outputs, and assert its reset.
            db_clk_control.reset_mmcm()
            # Always place the JESD204b cores in reset before modifying the clocks,
            # otherwise high power or erroneous conditions could exist in the FPGA!
            jesdcore.reset()
            # Configure and bringup the LMK's clocks.
            self.log.trace("Initializing LMK...")
            self.rh_class.lmk = self._init_lmk(
                self._spi_ifaces['lmk'],
                self.rh_class.ref_clock_freq,
                self.rh_class.sampling_clock_rate,
                self._spi_ifaces['phase_dac'],
                self.INIT_PHASE_DAC_WORD,
                self.PHASE_DAC_SPI_ADDR
            )
            self.log.trace("LMK Initialized!")
            # Deassert FPGA's MMCM reset, poll for lock, and enable outputs.
            db_clk_control.enable_mmcm()

            # 3. Synchronize DB Clocks.
            # The clock synchronzation driver receives the master_clock_rate, which for
            # Rhodium is half the sampling_clock_rate.
            self._sync_db_clock(
                radio_regs,
                self.rh_class.ref_clock_freq,
                self.rh_class.sampling_clock_rate / 2,
                args)

            # 4. DAC Configuration.
            self.dac.config()

            # 5. ADC Configuration.
            self.adc.config()

            # 6-7. JESD204B Initialization.
            self.init_jesd(jesdcore, self.rh_class.sampling_clock_rate)
            # [Optional] Perform RX eyescan.
            if perform_rx_eyescan:
                self.log.info("Performing RX eye scan on ADC to FPGA link...")
                self._rx_eyescan(jesdcore, args)
            # [Optional] Perform TX PRBS test.
            if perform_tx_prbs:
                self.log.info("Performing TX PRBS-31 test on FPGA to DAC link...")
                self._tx_prbs_test(jesdcore, args)
            # Direct the garbage collector by removing our references
            jesdcore = None
            db_clk_control = None

        # 8. CPLD Gain Tables Initialization.
        self.gain_table_loader.init()

        return True
Exemplo n.º 17
0
    def init(self, args):
        """
        Run the full initialization sequence. This will bring everything up
        from scratch: The LMK, JESD cores, the AD9695, the DAC37J82, and
        anything else that is clocking-related.
        Depending on the settings, this can take a fair amount of time.
        """
        # Input validation on RX margin tests (@ FPGA and DAC)
        # By accepting the rx_eyescan/tx_prbs argument being str or bool, one may
        # request an eyescan measurement to be performed from either the USRP's
        # shell (i.e. using --default-args) or from the host's MPM shell.
        perform_rx_eyescan = False
        if 'rx_eyescan' in args:
            perform_rx_eyescan = (args['rx_eyescan']
                                  == 'True') or (args['rx_eyescan'] == True)
        if perform_rx_eyescan:
            self.log.trace("Adding RX eye scan PMA enable to JESD args.")
            self.JESD_DEFAULT_ARGS["enable_rx_eyescan"] = True
        perform_tx_prbs = False
        if 'tx_prbs' in args:
            perform_tx_prbs = (args['tx_prbs'] == 'True') or (args['tx_prbs']
                                                              == True)

        # Latency across the JESD204B TX/RX links should remain constant and
        # deterministic across the supported sampling_clock_rate values.
        # After testing the roundtrip latency (i.e. FPGA -> TX -> RX -> FPGA),
        # it was found that a different set of SYSREF delay values are required
        # for sampling_clock_rate = 400 MSPS to achieve latency consistency.
        self.JESD_DEFAULT_ARGS['rx_sysref_delay'] = \
          self.RX_SYSREF_DLY_DIC[self.rh_class.sampling_clock_rate]

        # Bringup Sequence.
        #   1. Prerequisites (include opening mmaps)
        #   2. Initialize LMK and bringup clocks.
        #   3. Synchronize DB Clocks.
        #   4. Initialize FPGA JESD IP.
        #   5. DAC Configuration.
        #   6. ADC Configuration.
        #   7. JESD204B Initialization.
        #   8. CPLD Gain Tables Initialization.

        # 1. Prerequisites
        # Open FPGA IP (Clock control and JESD core).
        self.log.trace("Creating gain table object...")
        self.gain_table_loader = GainTableRh(
            self._spi_ifaces['cpld'], self._spi_ifaces['cpld_gain_loader'],
            self.log)

        with open_uio(label="dboard-regs-{}".format(self.rh_class.slot_idx),
                      read_only=False) as radio_regs:
            self.log.trace("Creating dboard clock control object")
            db_clk_control = DboardClockControl(radio_regs, self.log)
            self.log.trace("Creating jesdcore object")
            jesdcore = nijesdcore.NIJESDCore(radio_regs,
                                             self.rh_class.slot_idx,
                                             **self.JESD_DEFAULT_ARGS)

            # 2. Initialize LMK and bringup clocks.
            # Disable FPGA MMCM's outputs, and assert its reset.
            db_clk_control.reset_mmcm()
            # Always place the JESD204b cores in reset before modifying the clocks,
            # otherwise high power or erroneous conditions could exist in the FPGA!
            jesdcore.reset()
            # Configure and bringup the LMK's clocks.
            self.log.trace("Initializing LMK...")
            self.rh_class.lmk = self._init_lmk(
                self._spi_ifaces['lmk'], self.rh_class.ref_clock_freq,
                self.rh_class.sampling_clock_rate,
                self._spi_ifaces['phase_dac'], self.INIT_PHASE_DAC_WORD,
                self.PHASE_DAC_SPI_ADDR)
            self.log.trace("LMK Initialized!")
            # Deassert FPGA's MMCM reset, poll for lock, and enable outputs.
            db_clk_control.enable_mmcm()

            # 3. Synchronize DB Clocks.
            # The clock synchronzation driver receives the master_clock_rate, which for
            # Rhodium is half the sampling_clock_rate.
            self._sync_db_clock(radio_regs, self.rh_class.ref_clock_freq,
                                self.rh_class.sampling_clock_rate / 2, args)

            # 4. DAC Configuration.
            self.dac.config()

            # 5. ADC Configuration.
            self.adc.config()

            # 6-7. JESD204B Initialization.
            self.init_jesd(jesdcore, self.rh_class.sampling_clock_rate)
            # [Optional] Perform RX eyescan.
            if perform_rx_eyescan:
                self.log.info("Performing RX eye scan on ADC to FPGA link...")
                self._rx_eyescan(jesdcore, args)
            # [Optional] Perform TX PRBS test.
            if perform_tx_prbs:
                self.log.info(
                    "Performing TX PRBS-31 test on FPGA to DAC link...")
                self._tx_prbs_test(jesdcore, args)
            # Direct the garbage collector by removing our references
            jesdcore = None
            db_clk_control = None

        # 8. CPLD Gain Tables Initialization.
        self.gain_table_loader.init()

        return True
Exemplo n.º 18
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
Exemplo n.º 19
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