Source code for ska_low_sps_tpm_api.plugins.pll

import time

import ska_low_sps_tpm_api.boards.tpm_hw_definitions as tpm_hw_definitions
from ska_low_sps_tpm_api.base.definitions import *
from ska_low_sps_tpm_api.base.utils import *
from ska_low_sps_tpm_api.plugins.firmwareblock import FirmwareBlock

__author__ = "Alessio Magro"


[docs] class TpmPll(FirmwareBlock): """TpmPll plugin"""
[docs] @compatibleboards(BoardMake.TpmBoard) @friendlyname("tpm_pll") @maxinstances(1) def __init__(self, board, logger=None, **kwargs): """ TpmPll initialiser. :param board: Pointer to board instance """ super(TpmPll, self).__init__(board, logger=logger) self._board_type = kwargs.get("board_type", "XTPM") self._pll_out_config = [] fw_date_code = self.board["board.regfile.date_code"] self._fpga_firmware = None _use_jesd_refclk_only = False if self.board.is_programmed(0): self._fpga_firmware = self.board["fpga1.regfile.rev"] if self.board.hw_rev == tpm_hw_definitions.TPM_HW_REV_1_2: if self.board.has_register( "fpga1.regfile.feature_extended.use_jesd_refclk_only" ): _use_jesd_refclk_only = self.board[ "fpga1.regfile.feature_extended.use_jesd_refclk_only" ] self._pps_invert = None self._jesd_refclk_only = None if self.board.hw_rev == tpm_hw_definitions.TPM_HW_REV_1_2: self._pps_invert = 1 if _use_jesd_refclk_only == 1: self._jesd_refclk_only = True self.logger.debug("Not using JESD global clock!") else: self._jesd_refclk_only = False if ( fw_date_code >= tpm_hw_definitions.CPLD_FW_VER_REQUIRED_FOR_I2C_CMD_ACK_TPM_V1_2 ): self._pll_status_enabled = True else: self._pll_status_enabled = False elif self.board.hw_rev >= tpm_hw_definitions.TPM_HW_REV_1_5: self._pps_invert = 0 self._jesd_refclk_only = True if fw_date_code > self.board.CPLD_FW_VER_LOCK_I2C_CHANGE: self._pll_status_enabled = True else: self._pll_status_enabled = False else: raise PluginError("TpmPll: Invalid hw_rev detected") # On TPM 1.6 check if we are using the special firmware supporting 800 MHz clock from PLL self._config_800MHz = False if ( self.board.hw_rev >= tpm_hw_definitions.TPM_HW_REV_1_5 and self._fpga_firmware is not None ): if ( self._fpga_firmware == tpm_hw_definitions.FPGA_FW_800MHZ_EXCEPTION_TPM_V1_5 ): self._config_800MHz = True self.logger.info("FPGA FIRMWARE VERSION: %s" % hex(self._fpga_firmware)) self.logger.info("Using 800 MHz FPGA clock exception!") if self._board_type == "XTPM": if not self._config_800MHz: self._pll_out_config = [ "sysref", # 0 - SYSRF1 "clk", # 1 - CLKB1 "clk", # 2 - CLKB0 "unused", # 3 - PLL_CLK_TST "sysref", # 4 - SYSRF0 "unused", # 5 - NC "vcxo" if not self._jesd_refclk_only else "unused", # 6 - v1.6 PLL_CLK_FPGA0, v2.0 NC "unused", # 7 - 10M_FPGA0 "vcxo", # 8 - PLL_CLK_JESD_FPGA0 "sysref_pll1_retimed", # 9 - SYSREF_FPGA0 "sysref_pll1_retimed", # 10 - SYSREF_FPGA1 "unused", # 11 - 10M_FPGA1 "vcxo" if not self._jesd_refclk_only else "unused", # 12 - v1.6 PLL_CLK_FPGA1, v2.0 NC "vcxo", # 13 - PLL_CLK_JESD_FPGA1 ] # fmt: skip # TODO: Refactor _pll_out_config else: self.logger.warning( "FPGA FIRMWARE VERSION: %s" % hex(self._fpga_firmware) ) self.logger.warning( "PLL configure in 800MHz mode [experimental] [exception for fw %s only]" % hex(tpm_hw_definitions.FPGA_FW_800MHZ_EXCEPTION_TPM_V1_5) ) self._pll_out_config = [ "sysref", # 0 - SYSRF1 "clk", # 1 - CLKB1 "clk", # 2 - CLKB0 "unused", # 3 - PLL_CLK_TST "sysref", # 4 - SYSRF0 "unused", # 5 - NC "unused", # 6 - NC "unused", # 7 - 10M_FPGA0 "clk", # 8 - PLL_CLK_JESD_FPGA0 "sysref_pll1_retimed", # 9 - SYSREF_FPGA0 "sysref_pll1_retimed", # 10 - SYSREF_FPGA1 "unused", # 11 - 10M_FPGA1 "unused", # 12 - NC "clk", # 13 - PLL_CLK_JESD_FPGA1 ] else: raise PluginError("TpmPll: Board type not supported")
#######################################################################################
[docs] def pll_out_set(self, idx): """ Set PLL out. :param idx: :return: """ type = self._pll_out_config[idx] if type == "clk": reg0 = 0x0 reg1 = 0x0 if not self._config_800MHz else 0x80 reg2 = 0x0 elif type == "clk_hstl": reg0 = 0x0 reg1 = 0x80 reg2 = 0x0 elif type == "clk_div_2": reg0 = 0x0 reg1 = 0x0 reg2 = 0x0 if not self._config_800MHz else 0x01 elif type == "clk_div_4": reg0 = 0x0 reg1 = 0x0 if not self._config_800MHz else 0x80 reg2 = 0x3 elif type == "vcxo": reg0 = 0x20 reg1 = 0x0 if not self._config_800MHz else 0x80 reg2 = 0x0 elif type == "vcxo_hstl": reg0 = 0x20 reg1 = 0x80 reg2 = 0x0 elif type == "inverted_vcxo": reg0 = 0xA0 reg1 = 0x0 if not self._config_800MHz else 0x80 reg2 = 0x0 elif type == "sysref": reg0 = 0x40 reg1 = 0x0 if not self._config_800MHz else 0x80 reg2 = 0x0 elif type == "sysref_lvds_boost": reg0 = 0x40 reg1 = 0x40 reg2 = 0x0 elif type == "sysref_pll1_retimed": reg0 = 0x60 reg1 = 0x0 if not self._config_800MHz else 0x80 reg2 = 0x0 elif type == "sysref_lvds": reg0 = 0x40 reg1 = 0x0 reg2 = 0x0 elif type == "sysref_hstl": reg0 = 0x40 reg1 = 0x80 reg2 = 0x0 else: reg0 = 0x0 reg1 = 0x0 reg2 = 0x0 return reg0, reg1, reg2
[docs] def pll_config(self, fsample): """ Configure the PLL. :param fsample: """ doubler = 0x0 # 0x0 0x10 if self._board_type == "XTPM": # PLL1 config self.board[("pll", 0x100)] = 0x1 self.board[("pll", 0x102)] = 0x1 self.board[("pll", 0x104)] = 10 if doubler == 0 else 20 # VCXO100MHz self.board[("pll", 0x106)] = 0x14 # VCXO100MHz ##mod self.board[("pll", 0x107)] = 0x13 # Not disable holdover # Check if it is a TPM 2.0 which requires differential input if self.board.hw_rev >= tpm_hw_definitions.TPM_HW_REV_2_0: self.logger.warn("TpmPLL: PLL VCXO input set to differential") self.board[("pll", 0x108)] = 0x29 # VCXO100MHz with differential input else: self.board[("pll", 0x108)] = 0x28 # VCXO100MHz self.board[ ("pll", 0x109) ] = 0x0 # setting PLL1 feedback source as PLL2 divider output # fmt: skip self.board[("pll", 0x10A)] = 0x2 # 10MHZ: 0x2 # PLL2 config self.board[("pll", 0x200)] = 0xE6 if fsample == 1000e6: self.board[("pll", 0x201)] = 0x0A self.board[("pll", 0x202)] = 0x13 self.board[("pll", 0x203)] = 0x00 self.board[("pll", 0x204)] = 0x4 # M1 self.board[("pll", 0x205)] = 0x7 self.board[("pll", 0x207)] = 0x2 # R1 self.board[("pll", 0x208)] = 0x9 # N2 elif fsample == 800e6: self.board[("pll", 0x201)] = 0x0A if doubler == 0 else 0x05 self.board[("pll", 0x202)] = 0x13 if doubler == 0 else 0x23 self.board[("pll", 0x203)] = doubler self.board[("pll", 0x204)] = (0 << 5) | (0 << 4) | 0x5 # M1 self.board[("pll", 0x205)] = 0x7 self.board[("pll", 0x207)] = 0x1 # R1 self.board[("pll", 0x208)] = 0x7 if doubler == 0 else 0x3 # N2 elif fsample == 700e6: self.board[("pll", 0x201)] = 0xC8 self.board[("pll", 0x202)] = 0x13 self.board[("pll", 0x203)] = 0x00 self.board[("pll", 0x204)] = 0x5 # M1 self.board[("pll", 0x205)] = 0x7 self.board[("pll", 0x207)] = 0x0 # R1 self.board[("pll", 0x208)] = 0x6 # N2 else: raise PluginError("TpmPll: Frequency not supported") else: raise PluginError("TpmPll: Board type not supported") # Setting PLL Outputs for n in range(14): reg0, reg1, reg2 = self.pll_out_set(n) self.board[("pll", 0x300 + 3 * n + 0)] = reg0 self.board[("pll", 0x300 + 3 * n + 1)] = reg1 self.board[("pll", 0x300 + 3 * n + 2)] = reg2 # Setting SYSREF divider = 640 # sysref: divide by 640(*2) self.board[("pll", 0x400)] = divider & 0xFF self.board[("pll", 0x401)] = (divider >> 8) & 0xFF self.board[("pll", 0x402)] = 0x0 self.board[("pll", 0x403)] = 0x96 # Power down unused output self.board[("pll", 0x500)] = 0x10 pd = 0 for c in range(14): if self._pll_out_config[c] == "unused": pd |= 2**c self.board[("pll", 0x501)] = pd & 0xFF self.board[("pll", 0x502)] = (pd & 0xFF00) >> 8 self.board[("pll", 0x503)] = ~pd & 0xFF self.board[("pll", 0x504)] = (~pd & 0xFF00) >> 8 # enable STATUS outputs if self._pll_status_enabled: self.board[("pll", 0x505)] = 0x2 self.board[("pll", 0x506)] = 0x3 self.board[("pll", 0x507)] = 0xC # IO update command self.board[("pll", 0xF)] = 0x1 for i in range(10): if self.board[("pll", 0xF)] == 0: break if i == 9: raise PluginError( "PLL timeout error - IO_UPDATE (0xF): 0x%x" % self.board[("pll", 0xF)] ) time.sleep(0.1) # Enable sysref self.board[("pll", 0x403)] = 0x97 if ( do_until_eq(lambda: self.board[("pll", 0xF)], 0, ms_retry=100, s_timeout=10) is None ): raise PluginError( "PLL timeout error - IO_UPDATE (0xF): 0x%x" % self.board[("pll", 0xF)] ) self.board[("pll", 0xF)] = 0x1 # SYNC command self.board[("pll", 0x32A)] = 0x1 if ( do_until_eq(lambda: self.board[("pll", 0xF)], 0, ms_retry=100, s_timeout=10) is None ): raise PluginError( "PLL timeout error - IO_UPDATE (0xF): 0x%x" % self.board[("pll", 0xF)] ) self.board[("pll", 0xF)] = 0x1 # SYNC command self.board[("pll", 0x32A)] = 0x0 if ( do_until_eq(lambda: self.board[("pll", 0xF)], 0, ms_retry=100, s_timeout=10) is None ): raise PluginError( "PLL timeout error - IO_UPDATE (0xF): 0x%x" % self.board[("pll", 0xF)] ) self.board[("pll", 0xF)] = 0x1 # PLL2 VCO calibration self.board[("pll", 0x203)] = doubler | 0x0 self.board[("pll", 0xF)] = 0x1 if ( do_until_eq(lambda: self.board[("pll", 0xF)], 0, ms_retry=100, s_timeout=10) is None ): raise PluginError( "PLL timeout error - IO_UPDATE (0xF): 0x%x" % self.board[("pll", 0xF)] ) self.board[("pll", 0x203)] = doubler | 0x1 self.board[("pll", 0xF)] = 0x1 if ( do_until_eq(lambda: self.board[("pll", 0xF)], 0, ms_retry=100, s_timeout=10) is None ): raise PluginError( "PLL timeout error - IO_UPDATE (0xF): 0x%x" % self.board[("pll", 0xF)] ) if ( do_until_eq( lambda: self.board[("pll", 0x509)] & 0x1, 0x0, ms_retry=100, s_timeout=10, ) is None ): raise PluginError( "PLL VCO calibration timeout - Status Readback 1 (0x509): 0x%x" % self.board[("pll", 0x509)] ) if ( do_until_eq( lambda: self.board[("pll", 0x508)] in [0xF2, 0xE7], 0x1, ms_retry=100, s_timeout=10, ) is None ): raise PluginError( "PLL not locked - Status Readback 0 (0x508): 0x%x" % self.board[("pll", 0x508)] ) if self._pll_status_enabled: self.reset_pll_loss_of_lock()
[docs] def get_pll_status(self): if not self._pll_status_enabled: return None locks = self.board["board.regfile.pll.status"] return locks
[docs] def get_pll_loss_of_lock(self): if not self._pll_status_enabled: return None return self.board["board.regfile.pll_lol"]
[docs] def reset_pll_loss_of_lock(self): if not self._pll_status_enabled: return # lol = self.board["board.regfile.pll_lol"] self.board["board.regfile.pll_lol"] = 0 # lol & (~lol & 0xff)
[docs] def pll_reset(self): """Perform the PLL reset""" if self.board["board.regfile.enable.adc"] == 0: self.logger.info("ADCs power disabled. Enabling") self.board["board.regfile.enable.adc"] = 1 time.sleep(0.1) else: self.logger.info("ADCs power already enabled.") if self.board["board.regfile.enable.sysr"] == 0: self.logger.info("SYS_REF power disabled. Enabling") self.board["board.regfile.enable.sysr"] = 1 time.sleep(0.1) else: self.logger.info("SYS_REF power already enabled.") self.board["board.regfile.pll.resetn"] = 1 self.board["board.regfile.pll.resetn"] = 0 time.sleep(0.2) self.board["board.regfile.pll.resetn"] = 1 time.sleep(0.2)
[docs] def pll_start(self, fsample): """ Perform the PLL initialization procedure as implemented in ADI demo. :param fsample: PLL output frequency in MHz. Supported frequency are 700, 800, 1000 MHz """ if fsample not in [1000e6, 800e6, 700e6]: self.logger.warn( "TpmPLL: Frequency " + str(fsample / 1e6) + " MHz is not currently supported." ) fsample = 800e6 self.pll_reset() self.board["fpga1.pps_manager.pps_in_invert"] = self._pps_invert self.board["fpga1.pps_manager.sync_tc"] = 0x07090404 self.board["fpga1.pps_manager.sync_prescale"] = 0x0 self.board["fpga1.pps_manager.sync_cnt_enable"] = 0x7 self.board["fpga2.pps_manager.pps_in_invert"] = self._pps_invert self.board["fpga2.pps_manager.sync_tc"] = 0x07090404 self.board["fpga2.pps_manager.sync_prescale"] = 0x0 self.board["fpga2.pps_manager.sync_cnt_enable"] = 0x7 if self._board_type == "XTPM": self.board[("pll", 0xF)] = 0x1 self.pll_config(fsample) self.pll_config(fsample) self.board["fpga1.pps_manager.sync_cnt_enable"] = 0 # disable sync self.board["fpga2.pps_manager.sync_cnt_enable"] = 0 # disable sync if self._pll_status_enabled: if self.get_pll_loss_of_lock() > 0: raise PluginError("PLL Loss of lock detected!") else: raise PluginError("TpmPll: Board type not supported")
##################### Superclass method implementations #################################
[docs] def initialise(self): """Initialise TpmPll""" self.logger.info("TpmPll has been initialised") return True
[docs] def status_check(self): """Perform status check. :return: Status """ self.logger.info("TpmPll : Checking status") if self.board[("pll", 0x508)] not in [0xF2, 0xE7]: self.logger.error("TpmPLL: PLL not initialised") return Status.BoardError if self._pll_status_enabled: if self.get_pll_loss_of_lock() != 0: self.logger.error("TpmPLL: PLL Loss of Lock Detected") return Status.BoardError return Status.OK
[docs] def clean_up(self): """Perform cleanup. :return: Success """ self.logger.info("TpmPll : Cleaning up") return True