aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
diff options
context:
space:
mode:
authorBrent Stapleton <brent.stapleton@ettus.com>2018-04-17 19:33:23 -0700
committerBrent Stapleton <bstapleton@g.hmc.edu>2018-07-18 15:37:27 -0700
commit300a5e3f6e5e845b4b8d093222e1c51ca4640f79 (patch)
tree62f8bb7bc7d847b8f32f1fe5b4c9c06ef608080e /mpm/python/usrp_mpm/periph_manager/e320_periphs.py
parenta1d6530ce50ca9590739ffa40464747d3db968eb (diff)
downloaduhd-300a5e3f6e5e845b4b8d093222e1c51ca4640f79.tar.gz
uhd-300a5e3f6e5e845b4b8d093222e1c51ca4640f79.tar.bz2
uhd-300a5e3f6e5e845b4b8d093222e1c51ca4640f79.zip
mpm: initial commit of E320 code
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager/e320_periphs.py')
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e320_periphs.py383
1 files changed, 383 insertions, 0 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
new file mode 100644
index 000000000..d98c5a0e5
--- /dev/null
+++ b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
@@ -0,0 +1,383 @@
+#
+# Copyright 2018 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+E320 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
+E320_SFP_TYPES = {
+ 0: "", # Port not connected
+ 1: "1G",
+ 2: "10G",
+ 3: "A", # Aurora
+}
+
+E320_FPGA_TYPES_BY_SFP = {
+ (""): "",
+ ("1G"): "1G",
+ ("10G"): "XG",
+ ("A"): "AA",
+}
+
+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,
+ 0xFF, # use_mask
+ ddr
+ )
+
+class MboardRegsControl(object):
+ """
+ Control the FPGA Motherboard registers
+ """
+ # Motherboard registers
+ MB_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_SFP_PORT_INFO = 0x0028
+ MB_GPIO_CTRL = 0x002C
+ MB_GPIO_MASTER = 0x0030
+ MB_GPIO_RADIO_SRC = 0x0034
+ MB_GPS_CTRL = 0x0038
+ MB_GPS_STATUS = 0x003C
+ MB_DBOARD_CTRL = 0x0040
+ MB_DBOARD_STATUS = 0x0044
+
+ # Bitfield locations for the MB_CLOCK_CTRL register.
+ MB_CLOCK_CTRL_PPS_SEL_INT = 0
+ MB_CLOCK_CTRL_PPS_SEL_EXT = 1
+ MB_CLOCK_CTRL_REF_SEL = 2
+ MB_CLOCK_CTRL_REF_CLK_LOCKED = 3
+
+ # Bitfield locations for the MB_GPIO_CTRL register.
+ MB_GPIO_CTRL_BUFFER_OE_N = 0
+ MB_GPIO_CTRL_EN_VAR_SUPPLY = 1
+ MB_GPIO_CTRL_EN_2V5 = 2
+ MB_GPIO_CTRL_EN_3V3 = 3
+
+ # Bitfield locations for the MB_GPS_CTRL register.
+ MB_GPS_CTRL_PWR_EN = 0
+ MB_GPS_CTRL_RST_N = 1
+ MB_GPS_CTRL_INITSURV_N = 2
+
+ # Bitfield locations for the MB_GPS_STATUS register.
+ MB_GPS_STATUS_LOCK = 0
+ MB_GPS_STATUS_ALARM = 1
+ MB_GPS_STATUS_PHASELOCK = 2
+ MB_GPS_STATUS_SURVEY = 3
+ MB_GPS_STATUS_WARMUP = 4
+
+ # Bitfield locations for the MB_DBOARD_CTRL register.
+ MB_DBOARD_CTRL_MIMO = 0
+ MB_DBOARD_CTRL_TX_CHAN_SEL = 1
+
+ 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.MB_COMPAT_NUM)
+ minor = compat_number & 0xff
+ major = (compat_number>>16) & 0xff
+ return (major, minor)
+
+ def enable_fp_gpio(self, value):
+ """ Enable front panel GPIO buffers and power supply
+ and set voltage 1.8, 2.5 or 3.3 V
+ Setting value to 0 would disable gpio
+ """
+ if value == 0:
+ enable = False
+ else:
+ enable = True
+ self.set_fp_gpio_voltage(value)
+ mask = 0xFFFFFFFF ^ ((0b1 << self.MB_GPIO_CTRL_BUFFER_OE_N) | \
+ (0b1 << self.MB_GPIO_CTRL_EN_VAR_SUPPLY))
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_GPIO_CTRL) & mask
+ reg_val = reg_val | (not enable << self.MB_GPIO_CTRL_BUFFER_OE_N) | \
+ (enable << self.MB_GPIO_CTRL_EN_VAR_SUPPLY)
+ self.log.trace("Writing MB_GPIO_CTRL to 0x{:08X}".format(reg_val))
+ return self.poke32(self.MB_GPIO_CTRL, reg_val)
+
+ def set_fp_gpio_voltage(self, value):
+ """ Set Front Panel GPIO voltage (in volts)
+ 3V3 2V5 | Voltage
+ -----------------
+ 0 0 | 1.8 V
+ 0 1 | 2.5 V
+ 1 0 | 3.3 V
+ Arguments:
+ value : 1.8, 2.5 or 3.3
+ """
+ assert value in (1.8, 2.5, 3.3)
+ if value == 1.8:
+ voltage_reg = 0
+ elif value == 2.5:
+ voltage_reg = 1
+ elif value == 3.3:
+ voltage_reg = 2
+ mask = 0xFFFFFFFF ^ ((0b1 << self.MB_GPIO_CTRL_EN_3V3) | \
+ (0b1 << self.MB_GPIO_CTRL_EN_2V5))
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_GPIO_CTRL) & mask
+ reg_val = reg_val | (voltage_reg << self.MB_GPIO_CTRL_EN_2V5)
+ self.log.trace("Writing MB_GPIO_CTRL to 0x{:08X}".format(reg_val))
+ return self.poke32(self.MB_GPIO_CTRL, reg_val)
+
+ def get_fp_gpio_voltage(self):
+ """
+ Get Front Panel GPIO voltage (in volts)
+ """
+ mask = 0x3 << self.MB_GPIO_CTRL_EN_2V5
+ voltage = [1.8, 2.5, 3.3]
+ with self.regs.open():
+ reg_val = (self.peek32(self.MB_GPIO_CTRL) & mask) >> self.MB_GPIO_CTRL_EN_2V5
+ return voltage[reg_val]
+
+ def set_fp_gpio_master(self, value):
+ """set driver for front panel GPIO
+ Arguments:
+ value {unsigned} -- value is a single bit bit mask of 8 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 8 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 8 pins GPIO
+ 00: means the pin is driven by radio 0
+ 01: means the pin is driven by radio 1
+ """
+ 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 8 pins GPIO.
+ 00: means the pin is driven by radio 0
+ 01: means the pin is driven by radio 1
+ """
+ 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' or time_source == 'gpsdo':
+ self.log.trace("Setting time source to internal (GPSDO)"
+ "({:.1f} MHz reference)...".format(ref_clk_freq))
+ pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT
+ elif time_source == 'external':
+ self.log.debug("Setting time source to external...")
+ pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_EXT
+ else:
+ assert False, "Cannot set to invalid time source: {}".format(time_source)
+ 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 set_clock_source(self, clock_source, ref_clk_freq):
+ """
+ Set clock source
+ """
+ if clock_source == 'internal' or clock_source == 'gpsdo':
+ self.log.trace("Setting clock source to internal (GPSDO)"
+ "({:.1f} MHz reference)...".format(ref_clk_freq))
+ ref_sel_val = 0b0
+ elif clock_source == 'external':
+ self.log.debug("Setting clock source to external..."
+ "({:.1f} MHz reference)...".format(ref_clk_freq))
+ ref_sel_val = 0b1
+ else:
+ assert False, "Cannot set to invalid clock source: {}".format(clock_source)
+ mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_REF_SEL)
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask
+ reg_val = reg_val | (ref_sel_val << self.MB_CLOCK_CTRL_REF_SEL)
+ self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
+ self.poke32(self.MB_CLOCK_CTRL, reg_val)
+
+ def get_fpga_type(self):
+ """
+ Reads the type of the FPGA image currently loaded
+ Returns a string with the type (ie 1G, XG, AU, etc.)
+ """
+ with self.regs.open():
+ sfp_info_rb = self.peek32(self.MB_SFP_PORT_INFO)
+ # Print the registers values as 32-bit hex values
+ self.log.trace("SFP Info: 0x{0:0{1}X}".format(sfp_info_rb, 8))
+ try:
+ sfp_type = E320_SFP_TYPES.get((sfp_info_rb & 0x0000FF00) >> 8, "")
+ self.log.trace("SFP type: {}".format(sfp_type))
+ return sfp_type
+ except KeyError:
+ self.log.warning("Unrecognized SFP type: {}"
+ .format(sfp_type))
+ return ""
+
+ def get_gps_locked_val(self):
+ """
+ Get GPS LOCK status
+ """
+ mask = 0b1 << self.MB_GPS_STATUS_LOCK
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_GPS_STATUS) & mask
+ gps_locked = reg_val & 0x1 #FIXME
+ if gps_locked:
+ self.log.trace("GPS locked!")
+ # Can return this value because the gps_locked value is on the LSB
+ return gps_locked
+
+ def get_gps_status(self):
+ """
+ Get GPS status
+ """
+ mask = 0x1F
+ with self.regs.open():
+ gps_status = self.peek32(self.MB_GPS_STATUS) & mask
+ return gps_status
+
+ def enable_gps(self, enable):
+ """
+ Turn power to the GPS (CLK_GPS_PWR_EN) off or on.
+ Power signal is GPS_3V3.
+ """
+ self.log.trace("{} power to GPS".format(
+ "Enabling" if enable else "Disabling"
+ ))
+ mask = 0xFFFFFFFF ^ (0b1 << self.MB_GPS_CTRL_PWR_EN)
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_GPS_CTRL) & mask
+ reg_val = reg_val | (enable << self.MB_GPS_CTRL_PWR_EN)
+ self.log.trace("Writing MB_GPS_CTRL to 0x{:08X}".format(reg_val))
+ return self.poke32(self.MB_GPS_CTRL, reg_val)
+
+ def get_refclk_lock(self):
+ """
+ Check the status of the reference clock (adf4002) in FPGA.
+ """
+ mask = 0b1 << self.MB_CLOCK_CTRL_REF_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("Reference Clock reporting unlocked. "
+ "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val))
+ else:
+ self.log.trace("Reference Clock locked!")
+ return locked
+
+ def set_channel_mode(self, channel_mode):
+ """
+ Set channel mode in FPGA and select which tx channel to use
+ channel mode = "MIMO" for mimo
+ channel mode = "SISO_TX1", "SISO_TX0" for siso tx1, tx0 respectively.
+ """
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_DBOARD_CTRL)
+ if channel_mode == "MIMO":
+ reg_val = (0b1 << self.MB_DBOARD_CTRL_MIMO)
+ self.log.trace("Setting channel mode in AD9361 interface: {}".format("2R2T" if channel_mode == 2 else "1R1T"))
+ else:
+ # Warn if user tries to set either tx0/tx1 in mimo mode
+ # as both will be set automatically
+ if channel_mode == "SISO_TX1":
+ # in SISO mode, Channel 1
+ reg_val = (0b1 << self.MB_DBOARD_CTRL_TX_CHAN_SEL) | (0b0 << self.MB_DBOARD_CTRL_MIMO)
+ self.log.trace("Setting TX channel in AD9361 interface to: TX1")
+ elif channel_mode == "SISO_TX0":
+ # in SISO mode, Channel 0
+ reg_val = (0b0 << self.MB_DBOARD_CTRL_TX_CHAN_SEL) | (0b0 << self.MB_DBOARD_CTRL_MIMO)
+ self.log.trace("Setting TX channel in AD9361 interface to: TX0")
+ self.log.trace("Writing MB_DBOARD_CTRL to 0x{:08X}".format(reg_val))
+ self.poke32(self.MB_DBOARD_CTRL, reg_val)
+