__author__ = 'lessju'
import pyfabil.boards.tpm_hw_definitions as tpm_hw_definitions
from pyfabil.plugins.firmwareblock import FirmwareBlock
from pyfabil.base.definitions import *
from pyfabil.base.utils import *
import logging
import time
[docs]
class TpmPll(FirmwareBlock):
""" FirmwareBlock tests class """
@compatibleboards(BoardMake.TpmBoard)
@friendlyname('tpm_pll')
@maxinstances(1)
def __init__(self, board, **kwargs):
""" TpmPll initialiser
:param board: Pointer to board instance
"""
super(TpmPll, self).__init__(board)
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
logging.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
logging.info("FPGA FIRMWARE VERSION: %s" % hex(self._fpga_firmware))
logging.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
]
else:
logging.warning("FPGA FIRMWARE VERSION: %s"%hex(self._fpga_firmware))
logging.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:
logging.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
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 #0x05#0x0A
self.board[('pll', 0x202)] = 0x13 if doubler == 0 else 0x23 #0x33#0x13
self.board[('pll', 0x203)] = doubler #0x10#0x00
self.board[('pll', 0x204)] = (0 << 5) | (0 << 4) | 0x5 # M1
self.board[('pll', 0x205)] = 0x7
self.board[('pll', 0x207)] = 0x1 #0x1#0x2 # R1
self.board[('pll', 0x208)] = 0x7 if doubler == 0 else 0x3 #0x3#0x7 # 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
"""
self.board['board.regfile.ctrl.ad9528_rst'] = 1
self.board['board.regfile.ctrl.ad9528_rst'] = 0
time.sleep(0.2)
self.board['board.regfile.ctrl.ad9528_rst'] = 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]:
logging.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 """
logging.info("TpmPll has been initialised")
return True
[docs]
def status_check(self):
""" Perform status check
:return: Status
"""
logging.info("TpmPll : Checking status")
pll_reg_508 = self.board[('pll', 0x508)]
if pll_reg_508 not in [0xF2, 0xE7]:
logging.error('TpmPLL: PLL not not locked')
return Status.BoardError
if pll_reg_508 == 0xF2:
logging.error('TpmPLL: PLL not not locked to reference clock')
return Status.BoardError
if self._pll_status_enabled:
if self.get_pll_loss_of_lock() != 0:
logging.error('TpmPLL: PLL Loss of Lock Detected')
return Status.BoardError
return Status.OK
[docs]
def clean_up(self):
""" Perform cleanup
:return: Success
"""
logging.info("TpmPll : Cleaning up")
return True