def catalina_tune(self, which, freq): """ Async call to catalina tune """ self.log.trace("Tuning {} {}".format(which, freq)) async_exec(lib.ad9361, "tune", self.catalina, which, freq) return self.catalina.get_freq(which)
def init_rf_cal(self, args): " Setup RF CAL " self.log.debug("Setting up RF CAL...") try: self._init_cals_mask = \ self._parse_and_convert_cal_args( INIT_CALIBRATION_TABLE, args.get('init_cals', 'DEFAULT') ) self._tracking_cals_mask = \ self._parse_and_convert_cal_args( TRACKING_CALIBRATION_TABLE, args.get('tracking_cals', 'DEFAULT') ) self._init_cals_timeout = int( args.get('init_cals_timeout', str(self.mykonos.DEFAULT_INIT_CALS_TIMEOUT)), 0) except ValueError as ex: self.log.warning("init() args missing or error using default \ value seeing following exception print out.") self.log.warning("{}".format(ex)) self._init_cals_mask = self._parse_and_convert_cal_args( INIT_CALIBRATION_TABLE, 'DEFAULT') self._tracking_cals_mask = self._parse_and_convert_cal_args( TRACKING_CALIBRATION_TABLE, 'DEFAULT') self._init_cals_timeout = self.mykonos.DEFAULT_INIT_CALS_TIMEOUT self.log.debug("args[init_cals]=0x{:02X}".format(self._init_cals_mask)) self.log.debug("args[tracking_cals]=0x{:02X}".format( self._tracking_cals_mask)) async_exec(self.mykonos, "setup_cal", self._init_cals_mask, self._tracking_cals_mask, self._init_cals_timeout)
def catalina_tune(self, which, freq): """ Async call to catalina tune """ self.log.trace("Tuning {} {}".format(which, freq)) async_exec(lib.ad9361, "tune", self.catalina, which, freq) return self.catalina.get_freq(which)
def set_catalina_clock_rate(self, rate): """ Async call to catalina set_clock_rate """ self.log.trace("Setting Clock rate to {}".format(rate)) async_exec(lib.ad9361, "set_clock_rate", self.catalina, rate) return rate
def set_catalina_clock_rate(self, rate): """ Async call to catalina set_clock_rate """ self.log.trace("Setting Clock rate to {}".format(rate)) async_exec(lib.ad9361, "set_clock_rate", self.catalina, rate) return rate
def init_rf_cal(self, args): """ Setup RF CAL """ def _parse_and_convert_cal_args(table, cal_args): """Parse calibration string and convert it to a number Arguments: table {dictionary} -- a look up table that map a type of calibration to its bit mask.(defined in AD9375-UG992) cal_args {string} -- string arguments from user in form of "CAL1|CAL2|CAL3" or "CAL1 CAL2 CAL3" or "CAL1;CAL2;CAL3" Returns: int -- calibration value bit mask. """ value = 0 try: return int(cal_args, 0) except ValueError: pass for key in re.split(r'[;|\s]\s*', cal_args): value_tmp = table.get(key.upper()) if (value_tmp) != None: value |= value_tmp else: self.log.warning( "Calibration key `%s' is not in calibration table. " "Ignoring this key.", key.upper()) return value ## Go, go, go! self.log.trace("Setting up RF CAL...") try: init_cals_mask = _parse_and_convert_cal_args( INIT_CALIBRATION_TABLE, args.get('init_cals', 'DEFAULT')) tracking_cals_mask = _parse_and_convert_cal_args( TRACKING_CALIBRATION_TABLE, args.get('tracking_cals', 'DEFAULT')) init_cals_timeout = int( args.get('init_cals_timeout', str(self.mykonos.DEFAULT_INIT_CALS_TIMEOUT)), 0) except ValueError as ex: self.log.warning("init() args missing or error using default \ value seeing following exception print out.") self.log.warning("{}".format(ex)) init_cals_mask = _parse_and_convert_cal_args( INIT_CALIBRATION_TABLE, 'DEFAULT') tracking_cals_mask = _parse_and_convert_cal_args( TRACKING_CALIBRATION_TABLE, 'DEFAULT') init_cals_timeout = self.mykonos.DEFAULT_INIT_CALS_TIMEOUT self.log.debug("args[init_cals]=0x{:02X}".format(init_cals_mask)) self.log.debug( "args[tracking_cals]=0x{:02X}".format(tracking_cals_mask)) async_exec(self.mykonos, "setup_cal", init_cals_mask, tracking_cals_mask, init_cals_timeout)
def set_freq(self, which, freq, wait_for_lock): """ Perform asynchronous tuning. This will, under the hood, call set_freq() on the AD937X object, but will spin it out into an asynchronous execution. We do this because under some circumstances, the set_freq() call can take a long time to execute, and we want to release the GIL during that time. Note: This overrides the set_freq() call provided from self.mykonos. """ self.log.trace("Tuning {} {} {}".format(which, freq, wait_for_lock)) async_exec(self.mykonos, "set_freq", which, freq, wait_for_lock) return self.mykonos.get_freq(which)
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
def init_jesd(self, jesdcore, args): """ Bring up the JESD link between Mykonos and the N310. All clocks must be set up and stable before starting this routine. """ jesdcore.check_core() # JESD Lane Rate only depends on the master_clock_rate selection, since all # other link parameters (LMFS,N) remain constant. L = 4 M = 4 F = 2 S = 1 N = 16 new_rate = self.master_clock_rate * M * N * (10.0 / 8) / L / S self.log.trace("Calculated JESD204b lane rate is {} Gbps".format( new_rate / 1e9)) self.set_jesd_rate(jesdcore, new_rate) self.log.trace("Pulsing Mykonos Hard Reset...") self.cpld.reset_mykonos() self.log.trace("Initializing Mykonos...") self.init_lo_source(args) self.mykonos.begin_initialization() # Multi-chip Sync requires two SYSREF pulses at least 17us apart. jesdcore.send_sysref_pulse() time.sleep(0.001) # 17us... ish. jesdcore.send_sysref_pulse() async_exec(self.mykonos, "finish_initialization") # TODO:can we call this after JESD? self.init_rf_cal(args) self.log.trace("Starting JESD204b Link Initialization...") # Generally, enable the source before the sink. Start with the DAC side. self.log.trace("Starting FPGA framer...") jesdcore.init_framer() self.log.trace("Starting Mykonos deframer...") self.mykonos.start_jesd_rx() # Now for the ADC link. Note that the Mykonos framer will not start issuing CGS # characters until SYSREF is received by the framer. Therefore we enable the # framer in Mykonos and the FPGA, send a SYSREF pulse to everyone, and then # start the deframer in the FPGA. self.log.trace("Starting Mykonos framer...") self.mykonos.start_jesd_tx() jesdcore.enable_lmfc(True) jesdcore.send_sysref_pulse() # Allow a bit of time for SYSREF to reach Mykonos and then CGS to appear. In # several experiments this time requirement was only in the 100s of nanoseconds. time.sleep(0.001) self.log.trace("Starting FPGA deframer...") jesdcore.init_deframer() # Allow a bit of time for CGS/ILA to complete. time.sleep(0.100) error_flag = False if not jesdcore.get_framer_status(): self.log.error("JESD204b FPGA Core Framer is not synced!") error_flag = True if not self.check_mykonos_deframer_status(): self.log.error("Mykonos JESD204b Deframer is not synced!") error_flag = True if not jesdcore.get_deframer_status(): self.log.error("JESD204b FPGA Core Deframer is not synced!") error_flag = True if not self.check_mykonos_framer_status(): self.log.error("Mykonos JESD204b Framer is not synced!") error_flag = True if (self.mykonos.get_multichip_sync_status() & 0xB) != 0xB: self.log.error("Mykonos Multi-chip Sync failed!") error_flag = True if error_flag: raise RuntimeError( 'JESD204B Link Initialization Failed. See MPM logs for details.' ) self.log.debug("JESD204B Link Initialization & Training Complete")
def init_jesd(self, jesdcore, master_clock_rate, args): """ Bring up the JESD link between Mykonos and the N310. All clocks must be set up and stable before starting this routine. """ jesdcore.check_core() # JESD Lane Rate only depends on the master_clock_rate selection, since all # other link parameters (LMFS,N) remain constant. L = 4 M = 4 F = 2 S = 1 N = 16 new_rate = master_clock_rate * M * N * (10.0/8) / L / S self.log.trace("Calculated JESD204b lane rate is {} Gbps".format(new_rate/1e9)) self.mg_class.current_jesd_rate = \ self.set_jesd_rate( jesdcore, new_rate, self.mg_class.current_jesd_rate) self.log.trace("Pulsing Mykonos Hard Reset...") self.mg_class.cpld.reset_mykonos() self.log.trace("Initializing Mykonos...") # TODO: If we can set the LO source after initialization, that would # enable us to switch LO sources without doing the entire JESD and # clocking bringup. For now, we'll keep it here, and every time the LO # source needs to be changed, we need to re-initialize (this is because # MYKONOS_initialize() takes in the entire device config, which includes # the LO source), but we can revisit this if we want to either # - speed up init when the only change is the LO source, or # - we want to make the LO source runtime-configurable. self.init_lo_source(args) self.mykonos.begin_initialization() # Multi-chip Sync requires two SYSREF pulses at least 17us apart. jesdcore.send_sysref_pulse() time.sleep(0.001) # 17us... ish. jesdcore.send_sysref_pulse() async_exec(self.mykonos, "finish_initialization") # According to the AD9371 user guide, p.57, the RF cal must come before # the framer/deframer init. We tried otherwise, and failed. So don't # move this anywhere else. self.init_rf_cal(args) self.log.trace("Starting JESD204b Link Initialization...") # Generally, enable the source before the sink. Start with the DAC side. self.log.trace("Starting FPGA framer...") jesdcore.init_framer() self.log.trace("Starting Mykonos deframer...") self.mykonos.start_jesd_rx() # Now for the ADC link. Note that the Mykonos framer will not start issuing CGS # characters until SYSREF is received by the framer. Therefore we enable the # framer in Mykonos and the FPGA, send a SYSREF pulse to everyone, and then # start the deframer in the FPGA. self.log.trace("Starting Mykonos framer...") self.mykonos.start_jesd_tx() jesdcore.enable_lmfc(True) jesdcore.send_sysref_pulse() # Allow a bit of time for SYSREF to reach Mykonos and then CGS to # appear. In several experiments this time requirement was only in the # 100s of nanoseconds. time.sleep(0.001) self.log.trace("Starting FPGA deframer...") jesdcore.init_deframer() # Allow a bit of time for CGS/ILA to complete. time.sleep(0.100) error_flag = False if not jesdcore.get_framer_status(): self.log.error("JESD204b FPGA Core Framer is not synced!") error_flag = True if not self.check_mykonos_deframer_status(): self.log.error("Mykonos JESD204b Deframer is not synced!") error_flag = True if not jesdcore.get_deframer_status(): self.log.error("JESD204b FPGA Core Deframer is not synced!") error_flag = True if not self.check_mykonos_framer_status(): self.log.error("Mykonos JESD204b Framer is not synced!") error_flag = True if (self.mykonos.get_multichip_sync_status() & 0xB) != 0xB: self.log.error("Mykonos Multi-chip Sync failed!") error_flag = True if error_flag: raise RuntimeError('JESD204B Link Initialization Failed. See MPM logs for details.') self.log.debug("JESD204B Link Initialization & Training Complete")
def init_rf_cal(self, args): """ Setup RF CAL """ def _parse_and_convert_cal_args(table, cal_args): """Parse calibration string and convert it to a number Arguments: table {dictionary} -- a look up table that map a type of calibration to its bit mask.(defined in AD9375-UG992) cal_args {string} -- string arguments from user in form of "CAL1|CAL2|CAL3" or "CAL1 CAL2 CAL3" or "CAL1;CAL2;CAL3" Returns: int -- calibration value bit mask. """ value = 0 try: return int(cal_args, 0) except ValueError: pass for key in re.split(r'[;|\s]\s*', cal_args): value_tmp = table.get(key.upper()) if (value_tmp) != None: value |= value_tmp else: self.log.warning( "Calibration key `%s' is not in calibration table. " "Ignoring this key.", key.upper() ) return value ## Go, go, go! self.log.trace("Setting up RF CAL...") try: init_cals_mask = _parse_and_convert_cal_args( INIT_CALIBRATION_TABLE, args.get('init_cals', 'DEFAULT') ) tracking_cals_mask = _parse_and_convert_cal_args( TRACKING_CALIBRATION_TABLE, args.get('tracking_cals', 'DEFAULT') ) init_cals_timeout = int( args.get( 'init_cals_timeout', str(self.mykonos.DEFAULT_INIT_CALS_TIMEOUT) ), 0 ) except ValueError as ex: self.log.warning("init() args missing or error using default \ value seeing following exception print out.") self.log.warning("{}".format(ex)) init_cals_mask = _parse_and_convert_cal_args( INIT_CALIBRATION_TABLE, 'DEFAULT') tracking_cals_mask = _parse_and_convert_cal_args( TRACKING_CALIBRATION_TABLE, 'DEFAULT') init_cals_timeout = self.mykonos.DEFAULT_INIT_CALS_TIMEOUT self.log.debug("args[init_cals]=0x{:02X}".format(init_cals_mask)) self.log.debug("args[tracking_cals]=0x{:02X}".format(tracking_cals_mask)) async_exec( self.mykonos, "setup_cal", init_cals_mask, tracking_cals_mask, init_cals_timeout )
def init_jesd(self, jesdcore, master_clock_rate, args): """ Bring up the JESD link between Mykonos and the N310. All clocks must be set up and stable before starting this routine. """ jesdcore.check_core() # JESD Lane Rate only depends on the master_clock_rate selection, since all # other link parameters (LMFS,N) remain constant. L = 4 M = 4 F = 2 S = 1 N = 16 new_rate = master_clock_rate * M * N * (10.0 / 8) / L / S self.log.trace("Calculated JESD204b lane rate is {} Gbps".format( new_rate / 1e9)) self.mg_class.current_jesd_rate = \ self.set_jesd_rate( jesdcore, new_rate, self.mg_class.current_jesd_rate) self.log.trace("Pulsing Mykonos Hard Reset...") self.mg_class.cpld.reset_mykonos() self.log.trace("Initializing Mykonos...") # TODO: If we can set the LO source after initialization, that would # enable us to switch LO sources without doing the entire JESD and # clocking bringup. For now, we'll keep it here, and every time the LO # source needs to be changed, we need to re-initialize (this is because # MYKONOS_initialize() takes in the entire device config, which includes # the LO source), but we can revisit this if we want to either # - speed up init when the only change is the LO source, or # - we want to make the LO source runtime-configurable. self.init_lo_source(args) self.mykonos.begin_initialization() # Multi-chip Sync requires two SYSREF pulses at least 17us apart. jesdcore.send_sysref_pulse() time.sleep(0.001) # 17us... ish. jesdcore.send_sysref_pulse() async_exec(self.mykonos, "finish_initialization") # According to the AD9371 user guide, p.57, the RF cal must come before # the framer/deframer init. We tried otherwise, and failed. So don't # move this anywhere else. self.init_rf_cal(args) self.log.trace("Starting JESD204b Link Initialization...") # Generally, enable the source before the sink. Start with the DAC side. self.log.trace("Starting FPGA framer...") jesdcore.init_framer() self.log.trace("Starting Mykonos deframer...") self.mykonos.start_jesd_rx() # Now for the ADC link. Note that the Mykonos framer will not start issuing CGS # characters until SYSREF is received by the framer. Therefore we enable the # framer in Mykonos and the FPGA, send a SYSREF pulse to everyone, and then # start the deframer in the FPGA. self.log.trace("Starting Mykonos framer...") self.mykonos.start_jesd_tx() jesdcore.enable_lmfc(True) jesdcore.send_sysref_pulse() # Allow a bit of time for SYSREF to reach Mykonos and then CGS to # appear. In several experiments this time requirement was only in the # 100s of nanoseconds. time.sleep(0.001) self.log.trace("Starting FPGA deframer...") jesdcore.init_deframer() # Allow a bit of time for CGS/ILA to complete. time.sleep(0.100) error_flag = False if not jesdcore.get_framer_status(): self.log.error("JESD204b FPGA Core Framer is not synced!") error_flag = True if not self.check_mykonos_deframer_status(): self.log.error("Mykonos JESD204b Deframer is not synced!") error_flag = True if not jesdcore.get_deframer_status(): self.log.error("JESD204b FPGA Core Deframer is not synced!") error_flag = True if not self.check_mykonos_framer_status(): self.log.error("Mykonos JESD204b Framer is not synced!") error_flag = True if (self.mykonos.get_multichip_sync_status() & 0xB) != 0xB: self.log.error("Mykonos Multi-chip Sync failed!") error_flag = True if error_flag: raise RuntimeError( 'JESD204B Link Initialization Failed. See MPM logs for details.' ) self.log.debug("JESD204B Link Initialization & Training Complete")