diff options
Diffstat (limited to 'mpm')
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt | 9 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/magnesium.py | 193 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/mg_periphs.py | 202 |
3 files changed, 209 insertions, 195 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt index 99184fcd3..542497898 100644 --- a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2017 Ettus Research, National Instruments Company +# Copyright 2017 Ettus Research, a National Instruments Company # # SPDX-License-Identifier: GPL-3.0 # @@ -11,13 +11,14 @@ SET(USRP_MPM_FILES ${USRP_MPM_FILES}) SET(USRP_MPM_DBMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_SOURCE_DIR}/base.py - ${CMAKE_CURRENT_SOURCE_DIR}/test.py - ${CMAKE_CURRENT_SOURCE_DIR}/magnesium.py ${CMAKE_CURRENT_SOURCE_DIR}/eiscat.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk_eiscat.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk_mg.py - ${CMAKE_CURRENT_SOURCE_DIR}/unknown.py + ${CMAKE_CURRENT_SOURCE_DIR}/magnesium.py ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_update_cpld.py + ${CMAKE_CURRENT_SOURCE_DIR}/mg_periphs.py + ${CMAKE_CURRENT_SOURCE_DIR}/test.py + ${CMAKE_CURRENT_SOURCE_DIR}/unknown.py ) LIST(APPEND USRP_MPM_FILES ${USRP_MPM_DBMGR_FILES}) SET(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE) diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py index 205796844..f933d1f14 100644 --- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py +++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py @@ -17,14 +17,14 @@ from six import iterkeys, iteritems from usrp_mpm import lib # Pulls in everything from C++-land from usrp_mpm.dboard_manager import DboardManagerBase from usrp_mpm.dboard_manager.lmk_mg import LMK04828Mg +from usrp_mpm.dboard_manager.mg_periphs import TCA6408, MgCPLD +from usrp_mpm.dboard_manager.mg_periphs import DboardClockControl from usrp_mpm.cores import nijesdcore from usrp_mpm.mpmlog import get_logger from usrp_mpm.sys_utils.uio import open_uio from usrp_mpm.sys_utils.udev import get_eeprom_paths -from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO from usrp_mpm.cores import ClockSynchronizer from usrp_mpm.bfrfs import BufferFS -from usrp_mpm.mpmutils import poll_with_timeout INIT_CALIBRATION_TABLE = {"TX_BB_FILTER" : 0x0001, "ADC_TUNER" : 0x0002, @@ -108,195 +108,6 @@ def create_spidev_iface_phasedac(dev_node): ############################################################################### # Peripherals ############################################################################### -class TCA6408(object): - """ - Abstraction layer for the port/gpio expander - """ - pins = ( - 'PWR-GOOD-3.6V', #3.6V - 'PWR-EN-3.6V', #3.6V - 'PWR-GOOD-1.5V', #1.5V - 'PWR-EN-1.5V', #1.5V - 'PWR-GOOD-5.5V', #5.5V - 'PWR-EN-5.5V', #5.5V - '6', - 'LED', - ) - - def __init__(self, i2c_dev): - if i2c_dev is None: - raise RuntimeError("Need to specify i2c device to use the TCA6408") - self._gpios = SysFSGPIO('tca6408', 0xBF, 0xAA, 0xAA, i2c_dev) - - 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 DboardClockControl(object): - """ - Control the FPGA MMCM for Radio Clock control. - """ - # Clocking Register address constants - RADIO_CLK_MMCM = 0x0020 - PHASE_SHIFT_CONTROL = 0x0024 - RADIO_CLK_ENABLES = 0x0028 - MGT_REF_CLK_STATUS = 0x0030 - - def __init__(self, regs, log): - self.log = log - self.regs = regs - self.poke32 = self.regs.poke32 - self.peek32 = self.regs.peek32 - - def enable_outputs(self, enable=True): - """ - Enables or disables the MMCM outputs. - """ - if enable: - self.poke32(self.RADIO_CLK_ENABLES, 0x011) - else: - self.poke32(self.RADIO_CLK_ENABLES, 0x000) - - def reset_mmcm(self): - """ - Uninitialize and reset the MMCM - """ - self.log.trace("Disabling all Radio Clocks, then resetting FPGA MMCM...") - self.enable_outputs(False) - self.poke32(self.RADIO_CLK_MMCM, 0x1) - - def enable_mmcm(self): - """ - Unreset MMCM and poll lock indicators - - If MMCM is not locked after unreset, an exception is thrown. - """ - self.log.trace("Enabling FPGA Radio Clock MMCM...") - self.poke32(self.RADIO_CLK_MMCM, 0x2) - if not poll_with_timeout( - lambda: bool(self.peek32(self.RADIO_CLK_MMCM) & 0x10), - 500, - 10, - ): - self.log.error("FPGA Radio Clock MMCM not locked!") - raise RuntimeError("FPGA Radio Clock MMCM not locked!") - self.log.trace("FPGA Radio Clock MMCM locked. Enabling clocks to design...") - self.enable_outputs(True) - - def check_refclk(self): - """ - Not technically a clocking reg, but related. - """ - return bool(self.peek32(self.MGT_REF_CLK_STATUS) & 0x1) - -class MgCPLD(object): - """ - Control class for the CPLD - """ - CPLD_SIGNATURE = 0xCAFE # Expected signature ("magic number") - CPLD_MINOR_REV = 0 - CPLD_MAJOR_REV = 5 - - REG_SIGNATURE = 0x0000 - REG_MINOR_REVISION = 0x0001 - REG_MAJOR_REVISION = 0x0002 - REG_BUILD_CODE_LSB = 0x0003 - REG_BUILD_CODE_MSB = 0x0004 - REG_SCRATCH = 0x0005 - REG_CPLD_CTRL = 0x0010 - REG_LMK_CTRL = 0x0011 - REG_LO_STATUS = 0x0012 - REG_MYK_CTRL = 0x0013 - - def __init__(self, regs, log): - self.log = log.getChild("CPLD") - self.log.debug("Initializing CPLD...") - self.regs = regs - self.poke16 = self.regs.poke16 - self.peek16 = self.regs.peek16 - signature = self.peek16(self.REG_SIGNATURE) - if signature != self.CPLD_SIGNATURE: - self.log.error( - "CPLD Signature Mismatch! " \ - "Expected: 0x{:04X} Got: 0x{:04X}".format( - self.CPLD_SIGNATURE, signature)) - raise RuntimeError("CPLD Signature Check Failed! Incorrect signature readback.") - self.minor_rev = self.peek16(self.REG_MINOR_REVISION) - self.major_rev = self.peek16(self.REG_MAJOR_REVISION) - if self.major_rev != self.CPLD_MAJOR_REV: - self.log.error( - "CPLD Major Revision check mismatch! Expected: %d Got: %d", - self.CPLD_MAJOR_REV, - self.major_rev - ) - raise RuntimeError("CPLD Revision Check Failed! MPM is not compatible with " \ - "the loaded CPLD image.") - date_code = self.peek16(self.REG_BUILD_CODE_LSB) | \ - (self.peek16(self.REG_BUILD_CODE_MSB) << 16) - self.log.debug( - "CPLD Signature: 0x{:04X} " - "Revision: {}.{} " - "Date code: 0x{:08X}" - .format(signature, self.major_rev, self.minor_rev, date_code)) - - def set_scratch(self, val): - " Write to the scratch register " - self.poke16(self.REG_SCRATCH, val & 0xFFFF) - - def get_scratch(self): - " Read from the scratch register " - return self.peek16(self.REG_SCRATCH) - - def reset(self): - " Reset entire CPLD " - self.log.trace("Resetting CPLD...") - self.poke16(self.REG_CPLD_CTRL, 0x1) - self.poke16(self.REG_CPLD_CTRL, 0x0) - - def set_pdac_control(self, enb): - """ - If enb is True, the Phase DAC will exclusively control the VCXO voltage - """ - self.log.trace("Giving Phase %s control over VCXO voltage...", - "exclusive" if bool(enb) else "non-exclusive") - reg_val = (1<<4) if enb else 0 - self.poke16(self.REG_LMK_CTRL, reg_val) - - def get_lo_lock_status(self, which): - """ - Returns True if the 'which' LO is locked. 'which' is either 'tx' or - 'rx'. - """ - mask = (1<<4) if which.lower() == 'tx' else 1 - return bool(self.peek16(self.REG_LO_STATUS & mask)) - - def reset_mykonos(self): - """ - Hard-resets Mykonos - """ - self.log.debug("Resetting AD9371!") - self.poke16(self.REG_MYK_CTRL, 0x1) - time.sleep(0.001) # No spec here, but give it some time to reset. - self.poke16(self.REG_MYK_CTRL, 0x0) - time.sleep(0.001) # No spec here, but give it some time to reset. - - ############################################################################### # Main dboard control class ############################################################################### diff --git a/mpm/python/usrp_mpm/dboard_manager/mg_periphs.py b/mpm/python/usrp_mpm/dboard_manager/mg_periphs.py new file mode 100644 index 000000000..77ce4ce89 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/mg_periphs.py @@ -0,0 +1,202 @@ +# +# Copyright 2017 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0 +# +""" +Magnesium dboard peripherals (CPLD, port expander, dboard regs) +""" + +import time +from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO +from usrp_mpm.mpmutils import poll_with_timeout + +class TCA6408(object): + """ + Abstraction layer for the port/gpio expander + """ + pins = ( + 'PWR-GOOD-3.6V', #3.6V + 'PWR-EN-3.6V', #3.6V + 'PWR-GOOD-1.5V', #1.5V + 'PWR-EN-1.5V', #1.5V + 'PWR-GOOD-5.5V', #5.5V + 'PWR-EN-5.5V', #5.5V + '6', + 'LED', + ) + + def __init__(self, i2c_dev): + if i2c_dev is None: + raise RuntimeError("Need to specify i2c device to use the TCA6408") + self._gpios = SysFSGPIO('tca6408', 0xBF, 0xAA, 0xAA, i2c_dev) + + 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 DboardClockControl(object): + """ + Control the FPGA MMCM for Radio Clock control. + """ + # Clocking Register address constants + RADIO_CLK_MMCM = 0x0020 + PHASE_SHIFT_CONTROL = 0x0024 + RADIO_CLK_ENABLES = 0x0028 + MGT_REF_CLK_STATUS = 0x0030 + + def __init__(self, regs, log): + self.log = log + self.regs = regs + self.poke32 = self.regs.poke32 + self.peek32 = self.regs.peek32 + + def enable_outputs(self, enable=True): + """ + Enables or disables the MMCM outputs. + """ + if enable: + self.poke32(self.RADIO_CLK_ENABLES, 0x011) + else: + self.poke32(self.RADIO_CLK_ENABLES, 0x000) + + def reset_mmcm(self): + """ + Uninitialize and reset the MMCM + """ + self.log.trace("Disabling all Radio Clocks, then resetting MMCM...") + self.enable_outputs(False) + self.poke32(self.RADIO_CLK_MMCM, 0x1) + + def enable_mmcm(self): + """ + Unreset MMCM and poll lock indicators + + If MMCM is not locked after unreset, an exception is thrown. + """ + self.log.trace("Enabling FPGA Radio Clock MMCM...") + self.poke32(self.RADIO_CLK_MMCM, 0x2) + if not poll_with_timeout( + lambda: bool(self.peek32(self.RADIO_CLK_MMCM) & 0x10), + 500, + 10, + ): + self.log.error("FPGA Radio Clock MMCM not locked!") + raise RuntimeError("FPGA Radio Clock MMCM not locked!") + self.log.trace("Radio Clock MMCM locked. Enabling clocks to design...") + self.enable_outputs(True) + + def check_refclk(self): + """ + Not technically a clocking reg, but related. + """ + return bool(self.peek32(self.MGT_REF_CLK_STATUS) & 0x1) + +class MgCPLD(object): + """ + Control class for the CPLD + """ + CPLD_SIGNATURE = 0xCAFE # Expected signature ("magic number") + CPLD_MINOR_REV = 0 + CPLD_MAJOR_REV = 5 + + REG_SIGNATURE = 0x0000 + REG_MINOR_REVISION = 0x0001 + REG_MAJOR_REVISION = 0x0002 + REG_BUILD_CODE_LSB = 0x0003 + REG_BUILD_CODE_MSB = 0x0004 + REG_SCRATCH = 0x0005 + REG_CPLD_CTRL = 0x0010 + REG_LMK_CTRL = 0x0011 + REG_LO_STATUS = 0x0012 + REG_MYK_CTRL = 0x0013 + + def __init__(self, regs, log): + self.log = log.getChild("CPLD") + self.log.debug("Initializing CPLD...") + self.regs = regs + self.poke16 = self.regs.poke16 + self.peek16 = self.regs.peek16 + signature = self.peek16(self.REG_SIGNATURE) + if signature != self.CPLD_SIGNATURE: + self.log.error( + "CPLD Signature Mismatch! " \ + "Expected: 0x{:04X} Got: 0x{:04X}".format( + self.CPLD_SIGNATURE, signature)) + raise RuntimeError("CPLD Signature Check Failed! " + "Incorrect signature readback.") + self.minor_rev = self.peek16(self.REG_MINOR_REVISION) + self.major_rev = self.peek16(self.REG_MAJOR_REVISION) + if self.major_rev != self.CPLD_MAJOR_REV: + self.log.error( + "CPLD Major Revision check mismatch! Expected: %d Got: %d", + self.CPLD_MAJOR_REV, + self.major_rev + ) + raise RuntimeError("CPLD Revision Check Failed! MPM is not " + "compatible with the loaded CPLD image.") + date_code = self.peek16(self.REG_BUILD_CODE_LSB) | \ + (self.peek16(self.REG_BUILD_CODE_MSB) << 16) + self.log.debug( + "CPLD Signature: 0x{:04X} " + "Revision: {}.{} " + "Date code: 0x{:08X}" + .format(signature, self.major_rev, self.minor_rev, date_code)) + + def set_scratch(self, val): + " Write to the scratch register " + self.poke16(self.REG_SCRATCH, val & 0xFFFF) + + def get_scratch(self): + " Read from the scratch register " + return self.peek16(self.REG_SCRATCH) + + def reset(self): + " Reset entire CPLD " + self.log.trace("Resetting CPLD...") + self.poke16(self.REG_CPLD_CTRL, 0x1) + self.poke16(self.REG_CPLD_CTRL, 0x0) + + def set_pdac_control(self, enb): + """ + If enb is True, the Phase DAC will exclusively control the VCXO voltage + """ + self.log.trace("Giving Phase %s control over VCXO voltage...", + "exclusive" if bool(enb) else "non-exclusive") + reg_val = (1<<4) if enb else 0 + self.poke16(self.REG_LMK_CTRL, reg_val) + + def get_lo_lock_status(self, which): + """ + Returns True if the 'which' LO is locked. 'which' is either 'tx' or + 'rx'. + """ + mask = (1<<4) if which.lower() == 'tx' else 1 + return bool(self.peek16(self.REG_LO_STATUS & mask)) + + def reset_mykonos(self): + """ + Hard-resets Mykonos + """ + self.log.debug("Resetting AD9371!") + self.poke16(self.REG_MYK_CTRL, 0x1) + time.sleep(0.001) # No spec here, but give it some time to reset. + self.poke16(self.REG_MYK_CTRL, 0x0) + time.sleep(0.001) # No spec here, but give it some time to reset. + |