diff options
author | Martin Braun <martin.braun@ettus.com> | 2018-05-07 15:27:29 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2018-05-08 11:37:12 -0700 |
commit | 9cf1a8e99a76409b9be727d9efc8189bca54c4eb (patch) | |
tree | 2ecda408f69d71bed6e3731be2b7c59045f40720 /mpm | |
parent | f4b8879064f6090e2233165216e78944070b6a06 (diff) | |
download | uhd-9cf1a8e99a76409b9be727d9efc8189bca54c4eb.tar.gz uhd-9cf1a8e99a76409b9be727d9efc8189bca54c4eb.tar.bz2 uhd-9cf1a8e99a76409b9be727d9efc8189bca54c4eb.zip |
mpm: n3xx: Refactor n3xx_periphs into its own module
Diffstat (limited to 'mpm')
-rwxr-xr-x | mpm/python/n3xx_bist | 8 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx.py | 390 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py | 395 |
4 files changed, 404 insertions, 390 deletions
diff --git a/mpm/python/n3xx_bist b/mpm/python/n3xx_bist index 90764ea06..7873d9f6c 100755 --- a/mpm/python/n3xx_bist +++ b/mpm/python/n3xx_bist @@ -687,10 +687,10 @@ class N3XXBIST(object): 'write_patterns': list(patterns), 'read_patterns': list(patterns), } - from usrp_mpm.periph_manager import n3xx - gpio_tca6424 = n3xx.TCA6424(self.mb_rev) + from usrp_mpm.periph_manager import n3xx, n3xx_periphs + gpio_tca6424 = n3xx_periphs.TCA6424(self.mb_rev) gpio_tca6424.set("FPGA-GPIO-EN") - mb_regs = n3xx.MboardRegsControl(n3xx.n3xx.mboard_regs_label, self.log) + mb_regs = n3xx_periphs.MboardRegsControl(n3xx.n3xx.mboard_regs_label, self.log) mb_regs.set_fp_gpio_master(0xFFF) # Allow some time for the front-panel GPIOs to become usable time.sleep(.5) @@ -698,7 +698,7 @@ class N3XXBIST(object): ddr2 = 0xfc0 def _run_gpio(ddr, patterns): " Run a GPIO test for a given set of patterns " - gpio_ctrl = n3xx.FrontpanelGPIO(ddr) + gpio_ctrl = n3xx_periphs.FrontpanelGPIO(ddr) for pattern in patterns: gpio_set_all(gpio_ctrl, pattern, GPIO_WIDTH, ddr) time.sleep(0.1) diff --git a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt index 8000ebc6d..337b2c944 100644 --- a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt @@ -12,6 +12,7 @@ SET(USRP_MPM_PERIPHMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in ${CMAKE_CURRENT_SOURCE_DIR}/base.py ${CMAKE_CURRENT_SOURCE_DIR}/n3xx.py + ${CMAKE_CURRENT_SOURCE_DIR}/n3xx_periphs.py ${CMAKE_CURRENT_SOURCE_DIR}/test.py ) LIST(APPEND USRP_MPM_FILES ${USRP_MPM_PERIPHMGR_FILES}) diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py index cac21cd56..cce8494de 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py @@ -1,5 +1,5 @@ # -# Copyright 2017 Ettus Research, a National Instruments Company +# Copyright 2017-2018 Ettus Research, a National Instruments Company # # SPDX-License-Identifier: GPL-3.0-or-later # @@ -13,11 +13,9 @@ import copy import shutil import subprocess import json -import time import datetime import threading from six import iteritems, itervalues -from builtins import object from usrp_mpm.cores import WhiteRabbitRegsControl from usrp_mpm.gpsd_iface import GPSDIface from usrp_mpm.periph_manager import PeriphManagerBase @@ -25,10 +23,11 @@ from usrp_mpm.mpmtypes import SID from usrp_mpm.mpmutils import assert_compat_number, str2bool, poll_with_timeout from usrp_mpm.rpc_server import no_rpc from usrp_mpm.sys_utils import dtoverlay -from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank -from usrp_mpm.sys_utils.uio import UIO from usrp_mpm.sys_utils.sysfs_thermal import read_thermal_sensor_value from usrp_mpm.xports import XportMgrUDP, XportMgrLiberio +from usrp_mpm.periph_manager.n3xx_periphs import TCA6424 +from usrp_mpm.periph_manager.n3xx_periphs import BackpanelGPIO +from usrp_mpm.periph_manager.n3xx_periphs import MboardRegsControl N3XX_DEFAULT_EXT_CLOCK_FREQ = 10e6 N3XX_DEFAULT_CLOCK_SOURCE = 'internal' @@ -38,387 +37,6 @@ N3XX_DEFAULT_ENABLE_FPGPIO = True N3XX_DEFAULT_ENABLE_PPS_EXPORT = True N3XX_FPGA_COMPAT = (5, 2) N3XX_MONITOR_THREAD_INTERVAL = 1.0 # seconds -N3XX_SFP_TYPES = {0:"", 1:"1G", 2:"10G", 3:"A", 4:"W"} - -############################################################################### -# Additional peripheral controllers specific to Magnesium -############################################################################### -class TCA6424(object): - """ - Abstraction layer for the port/gpio expander - pins_list is an array of different version of TCA6424 pins map. - First element of this array corresponding to revC, second is revD etc... - """ - pins_list = [ - ( - 'PWREN-CLK-MGT156MHz', - 'NETCLK-CE', #revC name: 'PWREN-CLK-WB-CDCM', - 'NETCLK-RESETn', #revC name: 'WB-CDCM-RESETn', - 'NETCLK-PR0', #revC name: 'WB-CDCM-PR0', - 'NETCLK-PR1', #revC name: 'WB-CDCM-PR1', - 'NETCLK-OD0', #revC name: 'WB-CDCM-OD0', - 'NETCLK-OD1', #revC name: 'WB-CDCM-OD1', - 'NETCLK-OD2', #revC name: 'WB-CDCM-OD2', - 'PWREN-CLK-MAINREF', - 'CLK-MAINSEL-25MHz', #revC name: 'CLK-MAINREF-SEL1', - 'CLK-MAINSEL-EX_B', #revC name: 'CLK-MAINREF-SEL0', - '12', - 'CLK-MAINSEL-GPS', #revC name: '13', - 'FPGA-GPIO-EN', - 'PWREN-CLK-WB-20MHz', - 'PWREN-CLK-WB-25MHz', - 'GPS-PHASELOCK', - 'GPS-nINITSURV', - 'GPS-nRESET', - 'GPS-WARMUP', - 'GPS-SURVEY', - 'GPS-LOCKOK', - 'GPS-ALARM', - 'PWREN-GPS', - ), - ( - 'NETCLK-PR1', - 'NETCLK-PR0', - 'NETCLK-CE', - 'NETCLK-RESETn', - 'NETCLK-OD2', - 'NETCLK-OD1', - 'NETCLK-OD0', - 'PWREN-CLK-MGT156MHz', - 'PWREN-CLK-MAINREF', - 'CLK-MAINSEL-25MHz', - 'CLK-MAINSEL-EX_B', - '12', - 'CLK-MAINSEL-GPS', - 'FPGA-GPIO-EN', - 'PWREN-CLK-WB-20MHz', - 'PWREN-CLK-WB-25MHz', - 'GPS-PHASELOCK', - 'GPS-nINITSURV', - 'GPS-nRESET', - 'GPS-WARMUP', - 'GPS-SURVEY', - 'GPS-LOCKOK', - 'GPS-ALARM', - 'PWREN-GPS', - )] - - def __init__(self, rev): - # Default state: Turn on GPS power, take GPS out of reset or - # init-survey, turn on 156.25 MHz clock - # min Support from revC or rev = 2 - if rev == 2: - self.pins = self.pins_list[0] - else: - self.pins = self.pins_list[1] - - default_val = 0x860101 if rev == 2 else 0x860780 - self._gpios = SysFSGPIO('tca6424', 0xFFF7FF, 0x86F7FF, default_val) - - def set(self, name, value=None): - """ - Assert a pin by name - """ - assert name in self.pins - self._gpios.set(self.pins.index(name), value=value) - - def reset(self, name): - """ - Deassert a pin by name - """ - self.set(name, value=0) - - def get(self, name): - """ - Read back a pin by name - """ - assert name in self.pins - return self._gpios.get(self.pins.index(name)) - - -class FrontpanelGPIO(GPIOBank): - """ - Abstraction layer for the front panel GPIO - """ - EMIO_BASE = 54 - FP_GPIO_OFFSET = 32 # Bit offset within the ps_gpio_* pins - - def __init__(self, ddr): - GPIOBank.__init__( - self, - 'zynq_gpio', - self.FP_GPIO_OFFSET + self.EMIO_BASE, - 0xFFF, # use_mask - ddr - ) - -class BackpanelGPIO(GPIOBank): - """ - Abstraction layer for the back panel GPIO - """ - EMIO_BASE = 54 - BP_GPIO_OFFSET = 45 - LED_LINK = 0 - LED_REF = 1 - LED_GPS = 2 - - def __init__(self): - GPIOBank.__init__( - self, - 'zynq_gpio', - self.BP_GPIO_OFFSET + self.EMIO_BASE, - 0x7, # use_mask - 0x7, # ddr - ) - -class MboardRegsControl(object): - """ - Control the FPGA Motherboard registers - """ - # Motherboard registers - M_COMPAT_NUM = 0x0000 - MB_DATESTAMP = 0x0004 - MB_GIT_HASH = 0x0008 - MB_SCRATCH = 0x000C - MB_NUM_CE = 0x0010 - MB_NUM_IO_CE = 0x0014 - MB_CLOCK_CTRL = 0x0018 - MB_XADC_RB = 0x001C - MB_BUS_CLK_RATE = 0x0020 - MB_BUS_COUNTER = 0x0024 - MB_SFP0_INFO = 0x0028 - MB_SFP1_INFO = 0x002C - MB_GPIO_MASTER = 0x0030 - MB_GPIO_RADIO_SRC = 0x0034 - - # Bitfield locations for the MB_CLOCK_CTRL register. - MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0 # pps_sel is one-hot encoded! - MB_CLOCK_CTRL_PPS_SEL_INT_25 = 1 - MB_CLOCK_CTRL_PPS_SEL_EXT = 2 - MB_CLOCK_CTRL_PPS_SEL_GPSDO = 3 - MB_CLOCK_CTRL_PPS_SEL_SFP0 = 5 - MB_CLOCK_CTRL_PPS_SEL_SFP1 = 6 - MB_CLOCK_CTRL_PPS_OUT_EN = 4 # output enabled = 1 - MB_CLOCK_CTRL_MEAS_CLK_RESET = 12 # set to 1 to reset mmcm, default is 0 - MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13 # locked indication for meas_clk mmcm - - def __init__(self, label, log): - self.log = log - self.regs = UIO( - label=label, - read_only=False - ) - self.poke32 = self.regs.poke32 - self.peek32 = self.regs.peek32 - - def get_compat_number(self): - """get FPGA compat number - - This function reads back FPGA compat number. - The return is a tuple of - 2 numbers: (major compat number, minor compat number ) - """ - with self.regs.open(): - compat_number = self.peek32(self.M_COMPAT_NUM) - minor = compat_number & 0xff - major = (compat_number>>16) & 0xff - return (major, minor) - - def set_fp_gpio_master(self, value): - """set driver for front panel GPIO - Arguments: - value {unsigned} -- value is a single bit bit mask of 12 pins GPIO - """ - with self.regs.open(): - return self.poke32(self.MB_GPIO_MASTER, value) - - def get_fp_gpio_master(self): - """get "who" is driving front panel gpio - The return value is a bit mask of 12 pins GPIO. - 0: means the pin is driven by PL - 1: means the pin is driven by PS - """ - with self.regs.open(): - return self.peek32(self.MB_GPIO_MASTER) & 0xfff - - def set_fp_gpio_radio_src(self, value): - """set driver for front panel GPIO - Arguments: - value {unsigned} -- value is 2-bit bit mask of 12 pins GPIO - 00: means the pin is driven by radio 0 - 01: means the pin is driven by radio 1 - 10: means the pin is driven by radio 2 - 11: means the pin is driven by radio 3 - """ - with self.regs.open(): - return self.poke32(self.MB_GPIO_RADIO_SRC, value) - - def get_fp_gpio_radio_src(self): - """get which radio is driving front panel gpio - The return value is 2-bit bit mask of 12 pins GPIO. - 00: means the pin is driven by radio 0 - 01: means the pin is driven by radio 1 - 10: means the pin is driven by radio 2 - 11: means the pin is driven by radio 3 - """ - with self.regs.open(): - return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff - - def get_build_timestamp(self): - """ - Returns the build date/time for the FPGA image. - The return is datetime string with the ISO 8601 format - (YYYY-MM-DD HH:MM:SS.mmmmmm) - """ - with self.regs.open(): - datestamp_rb = self.peek32(self.MB_DATESTAMP) - if datestamp_rb > 0: - dt_str = datetime.datetime( - year=((datestamp_rb>>17)&0x3F)+2000, - month=(datestamp_rb>>23)&0x0F, - day=(datestamp_rb>>27)&0x1F, - hour=(datestamp_rb>>12)&0x1F, - minute=(datestamp_rb>>6)&0x3F, - second=((datestamp_rb>>0)&0x3F)) - self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) - return str(dt_str) - else: - # Compatibility with FPGAs without datestamp capability - return '' - - def get_git_hash(self): - """ - Returns the GIT hash for the FPGA build. - The return is a tuple of - 2 numbers: (short git hash, bool: is the tree dirty?) - """ - with self.regs.open(): - git_hash_rb = self.peek32(self.MB_GIT_HASH) - git_hash = git_hash_rb & 0x0FFFFFFF - tree_dirty = ((git_hash_rb & 0xF0000000) > 0) - dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' - self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( - git_hash, dirtiness_qualifier)) - return (git_hash, dirtiness_qualifier) - - def set_time_source(self, time_source, ref_clk_freq): - """ - Set time source - """ - pps_sel_val = 0x0 - if time_source == 'internal': - assert ref_clk_freq in (10e6, 25e6) - if ref_clk_freq == 10e6: - self.log.debug("Setting time source to internal " - "(10 MHz reference)...") - pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_10 - elif ref_clk_freq == 25e6: - self.log.debug("Setting time source to internal " - "(25 MHz reference)...") - pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_25 - elif time_source == 'external': - self.log.debug("Setting time source to external...") - pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_EXT - elif time_source == 'gpsdo': - self.log.debug("Setting time source to gpsdo...") - pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO - elif time_source == 'sfp0': - self.log.debug("Setting time source to sfp0...") - pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP0 - elif time_source == 'sfp1': - self.log.debug("Setting time source to sfp1...") - pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP1 - else: - assert False - - with self.regs.open(): - reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFF90 - # prevent glitches by writing a cleared value first, then the final value. - self.poke32(self.MB_CLOCK_CTRL, reg_val) - reg_val = reg_val | (pps_sel_val & 0x6F) - self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) - self.poke32(self.MB_CLOCK_CTRL, reg_val) - - def enable_pps_out(self, enable): - """ - Enables the PPS/Trig output on the back panel - """ - self.log.trace("%s PPS/Trig output!", - "Enabling" if enable else "Disabling") - mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) - with self.regs.open(): - # mask the bit to clear it: - reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask - if enable: - # set the bit if desired: - reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) - self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) - self.poke32(self.MB_CLOCK_CTRL, reg_val) - - def reset_meas_clk_mmcm(self, reset=True): - """ - Reset or unreset the MMCM for the measurement clock in the FPGA TDC. - """ - self.log.trace("%s measurement clock MMCM reset...", - "Asserting" if reset else "Clearing") - mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) - with self.regs.open(): - # mask the bit to clear it - reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask - if reset: - # set the bit if desired - reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) - self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) - self.poke32(self.MB_CLOCK_CTRL, reg_val) - - def get_meas_clock_mmcm_lock(self): - """ - Check the status of the MMCM for the measurement clock in the FPGA TDC. - """ - mask = 0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_LOCKED - with self.regs.open(): - reg_val = self.peek32(self.MB_CLOCK_CTRL) - locked = (reg_val & mask) > 0 - if not locked: - self.log.warning("Measurement clock MMCM reporting unlocked. " - "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val)) - else: - self.log.trace("Measurement clock MMCM locked!") - return locked - - def get_fpga_type(self): - """ - Reads the type of the FPGA image currently loaded - Returns a string with the type (ie HG, XG, AA, etc.) - """ - with self.regs.open(): - sfp0_info_rb = self.peek32(self.MB_SFP0_INFO) - sfp1_info_rb = self.peek32(self.MB_SFP1_INFO) - # Print the registers values as 32-bit hex values - self.log.trace("SFP0 Info: 0x{0:0{1}X}".format(sfp0_info_rb, 8)) - self.log.trace("SFP1 Info: 0x{0:0{1}X}".format(sfp1_info_rb, 8)) - - sfp0_type = N3XX_SFP_TYPES.get((sfp0_info_rb & 0x0000FF00) >> 8, "") - sfp1_type = N3XX_SFP_TYPES.get((sfp1_info_rb & 0x0000FF00) >> 8, "") - self.log.trace("SFP types: ({}, {})".format(sfp0_type, sfp1_type)) - if (sfp0_type == "") or (sfp1_type == ""): - return "" - elif (sfp0_type == "1G") and (sfp1_type == "10G"): - return "HG" - elif (sfp0_type == "10G") and (sfp1_type == "10G"): - return "XG" - elif (sfp0_type == "10G") and (sfp1_type == "A"): - return "XA" - elif (sfp0_type == "A") and (sfp1_type == "A"): - return "AA" - elif (sfp0_type == "W") and (sfp1_type == "10G"): - return "WX" - else: - self.log.warning("Unrecognized SFP type combination: ({}, {})".format( - sfp0_type, sfp1_type - )) - return "" - ############################################################################### # Transport managers diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py new file mode 100644 index 000000000..756f0f788 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py @@ -0,0 +1,395 @@ +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +N3xx peripherals +""" + +import datetime +from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank +from usrp_mpm.sys_utils.uio import UIO + +# Map register values to SFP transport types +N3XX_SFP_TYPES = { + 0: "", # Port not connected + 1: "1G", + 2: "10G", + 3: "A", # Aurora + 4: "W" # White Rabbit +} + +N3XX_FPGA_TYPES_BY_SFP = { + ("", ""): "", + ("1G", "10G"): "HG", + ("10G", "10G"): "XG", + ("10G", "A"): "XA", + ("A", "A"): "AA", + ("W", "10G"): "WX", +} + +class TCA6424(object): + """ + Abstraction layer for the port/gpio expander + pins_list is an array of different version of TCA6424 pins map. + First element of this array corresponding to revC, second is revD etc... + """ + pins_list = [ + ( + 'PWREN-CLK-MGT156MHz', + 'NETCLK-CE', #revC name: 'PWREN-CLK-WB-CDCM', + 'NETCLK-RESETn', #revC name: 'WB-CDCM-RESETn', + 'NETCLK-PR0', #revC name: 'WB-CDCM-PR0', + 'NETCLK-PR1', #revC name: 'WB-CDCM-PR1', + 'NETCLK-OD0', #revC name: 'WB-CDCM-OD0', + 'NETCLK-OD1', #revC name: 'WB-CDCM-OD1', + 'NETCLK-OD2', #revC name: 'WB-CDCM-OD2', + 'PWREN-CLK-MAINREF', + 'CLK-MAINSEL-25MHz', #revC name: 'CLK-MAINREF-SEL1', + 'CLK-MAINSEL-EX_B', #revC name: 'CLK-MAINREF-SEL0', + '12', + 'CLK-MAINSEL-GPS', #revC name: '13', + 'FPGA-GPIO-EN', + 'PWREN-CLK-WB-20MHz', + 'PWREN-CLK-WB-25MHz', + 'GPS-PHASELOCK', + 'GPS-nINITSURV', + 'GPS-nRESET', + 'GPS-WARMUP', + 'GPS-SURVEY', + 'GPS-LOCKOK', + 'GPS-ALARM', + 'PWREN-GPS', + ), + ( + 'NETCLK-PR1', + 'NETCLK-PR0', + 'NETCLK-CE', + 'NETCLK-RESETn', + 'NETCLK-OD2', + 'NETCLK-OD1', + 'NETCLK-OD0', + 'PWREN-CLK-MGT156MHz', + 'PWREN-CLK-MAINREF', + 'CLK-MAINSEL-25MHz', + 'CLK-MAINSEL-EX_B', + '12', + 'CLK-MAINSEL-GPS', + 'FPGA-GPIO-EN', + 'PWREN-CLK-WB-20MHz', + 'PWREN-CLK-WB-25MHz', + 'GPS-PHASELOCK', + 'GPS-nINITSURV', + 'GPS-nRESET', + 'GPS-WARMUP', + 'GPS-SURVEY', + 'GPS-LOCKOK', + 'GPS-ALARM', + 'PWREN-GPS', + )] + + def __init__(self, rev): + # Default state: Turn on GPS power, take GPS out of reset or + # init-survey, turn on 156.25 MHz clock + # min Support from revC or rev = 2 + if rev == 2: + self.pins = self.pins_list[0] + else: + self.pins = self.pins_list[1] + + default_val = 0x860101 if rev == 2 else 0x860780 + self._gpios = SysFSGPIO('tca6424', 0xFFF7FF, 0x86F7FF, default_val) + + def set(self, name, value=None): + """ + Assert a pin by name + """ + assert name in self.pins + self._gpios.set(self.pins.index(name), value=value) + + def reset(self, name): + """ + Deassert a pin by name + """ + self.set(name, value=0) + + def get(self, name): + """ + Read back a pin by name + """ + assert name in self.pins + return self._gpios.get(self.pins.index(name)) + + +class FrontpanelGPIO(GPIOBank): + """ + Abstraction layer for the front panel GPIO + """ + EMIO_BASE = 54 + FP_GPIO_OFFSET = 32 # Bit offset within the ps_gpio_* pins + + def __init__(self, ddr): + GPIOBank.__init__( + self, + 'zynq_gpio', + self.FP_GPIO_OFFSET + self.EMIO_BASE, + 0xFFF, # use_mask + ddr + ) + +class BackpanelGPIO(GPIOBank): + """ + Abstraction layer for the back panel GPIO + """ + EMIO_BASE = 54 + BP_GPIO_OFFSET = 45 + LED_LINK = 0 + LED_REF = 1 + LED_GPS = 2 + + def __init__(self): + GPIOBank.__init__( + self, + 'zynq_gpio', + self.BP_GPIO_OFFSET + self.EMIO_BASE, + 0x7, # use_mask + 0x7, # ddr + ) + +class MboardRegsControl(object): + """ + Control the FPGA Motherboard registers + """ + # Motherboard registers + M_COMPAT_NUM = 0x0000 + MB_DATESTAMP = 0x0004 + MB_GIT_HASH = 0x0008 + MB_SCRATCH = 0x000C + MB_NUM_CE = 0x0010 + MB_NUM_IO_CE = 0x0014 + MB_CLOCK_CTRL = 0x0018 + MB_XADC_RB = 0x001C + MB_BUS_CLK_RATE = 0x0020 + MB_BUS_COUNTER = 0x0024 + MB_SFP0_INFO = 0x0028 + MB_SFP1_INFO = 0x002C + MB_GPIO_MASTER = 0x0030 + MB_GPIO_RADIO_SRC = 0x0034 + + # Bitfield locations for the MB_CLOCK_CTRL register. + MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0 # pps_sel is one-hot encoded! + MB_CLOCK_CTRL_PPS_SEL_INT_25 = 1 + MB_CLOCK_CTRL_PPS_SEL_EXT = 2 + MB_CLOCK_CTRL_PPS_SEL_GPSDO = 3 + MB_CLOCK_CTRL_PPS_SEL_SFP0 = 5 + MB_CLOCK_CTRL_PPS_SEL_SFP1 = 6 + MB_CLOCK_CTRL_PPS_OUT_EN = 4 # output enabled = 1 + MB_CLOCK_CTRL_MEAS_CLK_RESET = 12 # set to 1 to reset mmcm, default is 0 + MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13 # locked indication for meas_clk mmcm + + def __init__(self, label, log): + self.log = log + self.regs = UIO( + label=label, + read_only=False + ) + self.poke32 = self.regs.poke32 + self.peek32 = self.regs.peek32 + + def get_compat_number(self): + """get FPGA compat number + + This function reads back FPGA compat number. + The return is a tuple of + 2 numbers: (major compat number, minor compat number ) + """ + with self.regs.open(): + compat_number = self.peek32(self.M_COMPAT_NUM) + minor = compat_number & 0xff + major = (compat_number>>16) & 0xff + return (major, minor) + + def set_fp_gpio_master(self, value): + """set driver for front panel GPIO + Arguments: + value {unsigned} -- value is a single bit bit mask of 12 pins GPIO + """ + with self.regs.open(): + return self.poke32(self.MB_GPIO_MASTER, value) + + def get_fp_gpio_master(self): + """get "who" is driving front panel gpio + The return value is a bit mask of 12 pins GPIO. + 0: means the pin is driven by PL + 1: means the pin is driven by PS + """ + with self.regs.open(): + return self.peek32(self.MB_GPIO_MASTER) & 0xfff + + def set_fp_gpio_radio_src(self, value): + """set driver for front panel GPIO + Arguments: + value {unsigned} -- value is 2-bit bit mask of 12 pins GPIO + 00: means the pin is driven by radio 0 + 01: means the pin is driven by radio 1 + 10: means the pin is driven by radio 2 + 11: means the pin is driven by radio 3 + """ + with self.regs.open(): + return self.poke32(self.MB_GPIO_RADIO_SRC, value) + + def get_fp_gpio_radio_src(self): + """get which radio is driving front panel gpio + The return value is 2-bit bit mask of 12 pins GPIO. + 00: means the pin is driven by radio 0 + 01: means the pin is driven by radio 1 + 10: means the pin is driven by radio 2 + 11: means the pin is driven by radio 3 + """ + with self.regs.open(): + return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff + + def get_build_timestamp(self): + """ + Returns the build date/time for the FPGA image. + The return is datetime string with the ISO 8601 format + (YYYY-MM-DD HH:MM:SS.mmmmmm) + """ + with self.regs.open(): + datestamp_rb = self.peek32(self.MB_DATESTAMP) + if datestamp_rb > 0: + dt_str = datetime.datetime( + year=((datestamp_rb>>17)&0x3F)+2000, + month=(datestamp_rb>>23)&0x0F, + day=(datestamp_rb>>27)&0x1F, + hour=(datestamp_rb>>12)&0x1F, + minute=(datestamp_rb>>6)&0x3F, + second=((datestamp_rb>>0)&0x3F)) + self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) + return str(dt_str) + else: + # Compatibility with FPGAs without datestamp capability + return '' + + def get_git_hash(self): + """ + Returns the GIT hash for the FPGA build. + The return is a tuple of + 2 numbers: (short git hash, bool: is the tree dirty?) + """ + with self.regs.open(): + git_hash_rb = self.peek32(self.MB_GIT_HASH) + git_hash = git_hash_rb & 0x0FFFFFFF + tree_dirty = ((git_hash_rb & 0xF0000000) > 0) + dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' + self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( + git_hash, dirtiness_qualifier)) + return (git_hash, dirtiness_qualifier) + + def set_time_source(self, time_source, ref_clk_freq): + """ + Set time source + """ + pps_sel_val = 0x0 + if time_source == 'internal': + assert ref_clk_freq in (10e6, 25e6) + if ref_clk_freq == 10e6: + self.log.debug("Setting time source to internal " + "(10 MHz reference)...") + pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_10 + elif ref_clk_freq == 25e6: + self.log.debug("Setting time source to internal " + "(25 MHz reference)...") + pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_25 + elif time_source == 'external': + self.log.debug("Setting time source to external...") + pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_EXT + elif time_source == 'gpsdo': + self.log.debug("Setting time source to gpsdo...") + pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO + elif time_source == 'sfp0': + self.log.debug("Setting time source to sfp0...") + pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP0 + elif time_source == 'sfp1': + self.log.debug("Setting time source to sfp1...") + pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP1 + else: + assert False + + with self.regs.open(): + reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFF90 + # prevent glitches by writing a cleared value first, then the final value. + self.poke32(self.MB_CLOCK_CTRL, reg_val) + reg_val = reg_val | (pps_sel_val & 0x6F) + self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) + self.poke32(self.MB_CLOCK_CTRL, reg_val) + + def enable_pps_out(self, enable): + """ + Enables the PPS/Trig output on the back panel + """ + self.log.trace("%s PPS/Trig output!", + "Enabling" if enable else "Disabling") + mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) + with self.regs.open(): + # mask the bit to clear it: + reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask + if enable: + # set the bit if desired: + reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) + self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) + self.poke32(self.MB_CLOCK_CTRL, reg_val) + + def reset_meas_clk_mmcm(self, reset=True): + """ + Reset or unreset the MMCM for the measurement clock in the FPGA TDC. + """ + self.log.trace("%s measurement clock MMCM reset...", + "Asserting" if reset else "Clearing") + mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) + with self.regs.open(): + # mask the bit to clear it + reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask + if reset: + # set the bit if desired + reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) + self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val)) + self.poke32(self.MB_CLOCK_CTRL, reg_val) + + def get_meas_clock_mmcm_lock(self): + """ + Check the status of the MMCM for the measurement clock in the FPGA TDC. + """ + mask = 0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_LOCKED + with self.regs.open(): + reg_val = self.peek32(self.MB_CLOCK_CTRL) + locked = (reg_val & mask) > 0 + if not locked: + self.log.warning("Measurement clock MMCM reporting unlocked. " + "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val)) + else: + self.log.trace("Measurement clock MMCM locked!") + return locked + + def get_fpga_type(self): + """ + Reads the type of the FPGA image currently loaded + Returns a string with the type (ie HG, XG, AA, etc.) + """ + with self.regs.open(): + sfp0_info_rb = self.peek32(self.MB_SFP0_INFO) + sfp1_info_rb = self.peek32(self.MB_SFP1_INFO) + # Print the registers values as 32-bit hex values + self.log.trace("SFP0 Info: 0x{0:0{1}X}".format(sfp0_info_rb, 8)) + self.log.trace("SFP1 Info: 0x{0:0{1}X}".format(sfp1_info_rb, 8)) + sfp0_type = N3XX_SFP_TYPES.get((sfp0_info_rb & 0x0000FF00) >> 8, "") + sfp1_type = N3XX_SFP_TYPES.get((sfp1_info_rb & 0x0000FF00) >> 8, "") + self.log.trace("SFP types: ({}, {})".format(sfp0_type, sfp1_type)) + try: + return N3XX_FPGA_TYPES_BY_SFP[(sfp0_type, sfp1_type)] + except KeyError: + self.log.warning("Unrecognized SFP type combination: ({}, {})" + .format(sfp0_type, sfp1_type)) + return "" + |