aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm
diff options
context:
space:
mode:
authorDhiren Wijesinghe <dhiren.wijesinghe@ni.com>2021-06-18 14:07:18 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2021-11-03 06:04:50 -0700
commitc36fc5f6c088efe00eac1f0de32c4377c81fc883 (patch)
treebc83ae588edd852d9a5ced2f0a00c4c4fc630140 /mpm/python/usrp_mpm
parent426f078ba015f1b0a1521ad447a5a5f79f9fc2a1 (diff)
downloaduhd-c36fc5f6c088efe00eac1f0de32c4377c81fc883.tar.gz
uhd-c36fc5f6c088efe00eac1f0de32c4377c81fc883.tar.bz2
uhd-c36fc5f6c088efe00eac1f0de32c4377c81fc883.zip
mpm: x4xx: add DIO GPIO API configuration methods
These methods allow for reconfiguration of GPIO masters for x4xx. The method names are get_gpio_banks, get_gpio_srcs, get_gpio_src, and set_gpio_src.
Diffstat (limited to 'mpm/python/usrp_mpm')
-rw-r--r--mpm/python/usrp_mpm/periph_manager/x4xx.py56
-rw-r--r--mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py197
2 files changed, 218 insertions, 35 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx.py b/mpm/python/usrp_mpm/periph_manager/x4xx.py
index 30938a6b2..ad8f2fdcc 100644
--- a/mpm/python/usrp_mpm/periph_manager/x4xx.py
+++ b/mpm/python/usrp_mpm/periph_manager/x4xx.py
@@ -302,6 +302,9 @@ class x4xx(ZynqComponents, PeriphManagerBase):
try:
self._init_peripherals(args)
self.init_dboards(args)
+ # We need to init dio_control separately from peripherals
+ # since it needs information about available dboards
+ self._init_dio_control(args)
self._clk_mgr.set_dboard_reset_cb(
lambda enable: [db.reset_clock(enable) for db in self.dboards])
except Exception as ex:
@@ -535,13 +538,6 @@ class x4xx(ZynqComponents, PeriphManagerBase):
# is possible to always enable the iPass cable present forwarding.
self.ctrlport_regs.enable_cable_present_forwarding(True)
- # Init DIO
- if self._check_compat_aux_board(DIOAUX_EEPROM, DIOAUX_PID):
- self.dio_control = DioControl(self.mboard_regs_control,
- self.cpld_control, self.log)
- # add dio_control public methods to MPM API
- self._add_public_methods(self.dio_control, "dio")
-
# Init QSFP modules
for idx, config in enumerate(X400_QSFP_I2C_CONFIGS):
attr = QSFPModule(
@@ -566,6 +562,18 @@ class x4xx(ZynqComponents, PeriphManagerBase):
self._status_monitor_thread.start()
# Init complete.
self.log.debug("Device info: {}".format(self.device_info))
+
+ def _init_dio_control(self, _):
+ """
+ Turn on gpio peripherals. This may throw an error on failure, so make
+ sure to catch it.
+ """
+ if self._check_compat_aux_board(DIOAUX_EEPROM, DIOAUX_PID):
+ self.dio_control = DioControl(self.mboard_regs_control,
+ self.cpld_control, self.log,
+ self.dboards)
+ # add dio_control public methods to MPM API
+ self._add_public_methods(self.dio_control, "dio")
def _check_compat_aux_board(self, name, pid):
"""
@@ -1018,6 +1026,40 @@ class x4xx(ZynqComponents, PeriphManagerBase):
}
]
+ ###########################################################################
+ # GPIO API
+ ###########################################################################
+
+ def get_gpio_banks(self):
+ """
+ Returns a list of GPIO banks over which MPM has any control
+ """
+ return self.dio_control.get_gpio_banks()
+
+ def get_gpio_srcs(self, bank: str):
+ """
+ Return a list of valid GPIO sources for a given bank
+ """
+ return self.dio_control.get_gpio_srcs(bank)
+
+ def get_gpio_src(self, bank: str):
+ """
+ Return the currently selected GPIO source for a given bank. The return
+ value is a list of strings. The length of the vector is identical to
+ the number of controllable GPIO pins on this bank. CUSTOM is for
+ miscellaneous pin source, and USER_APP is for LabView pin source.
+ """
+ return self.dio_control.get_gpio_src(bank)
+
+ def set_gpio_src(self, bank: str, *src):
+ """
+ Set the GPIO source for a given bank.
+ src input is big-endian
+ Usage:
+ > set_gpio_src <bank> <srcs>
+ > set_gpio_src GPIO0 PS DB1_RF1 PS PS MPM PS PS PS MPM USER_APP PS
+ """
+ self.dio_control.set_gpio_src(bank, *src)
###########################################################################
# Utility for validating RPU core number
diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py
index be0487872..3cfd1eda8 100644
--- a/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py
@@ -142,9 +142,30 @@ class DioControl:
FPGA_DIO_DIRECTION_REGISTER = FPGA_DIO_REGISTER_BASE + 0x4
FPGA_DIO_INPUT_REGISTER = FPGA_DIO_REGISTER_BASE + 0x8
FPGA_DIO_OUTPUT_REGISTER = FPGA_DIO_REGISTER_BASE + 0xC
+ FPGA_DIO_SOURCE_REGISTER = FPGA_DIO_REGISTER_BASE + 0x10
+ FPGA_DIO_RADIO_SOURCE_REGISTER = FPGA_DIO_REGISTER_BASE + 0x14
+ FPGA_DIO_INTERFACE_DIO_SELECT_REGISTER = FPGA_DIO_REGISTER_BASE + 0x18
+ FPGA_DIO_OVERRIDE_REGISTER = FPGA_DIO_REGISTER_BASE + 0x1C
+ FPGA_DIO_SW_DIO_CONTROL_REGISTER = FPGA_DIO_REGISTER_BASE + 0x20
+
+ # DIO register addresses
+ RADIO_DIO_REGISTER_BASE = 0x8C000
+ RADIO_DIO_CLASSIC_ATR_CONFIG_REGISTER = RADIO_DIO_REGISTER_BASE + 0x40
+
# DIO registers addresses in CPLD
CPLD_DIO_DIRECTION_REGISTER = 0x30
+ # GPIO attributes
+ X4XX_GPIO_BANKS = ["GPIO0", "GPIO1"]
+ X4XX_GPIO_SRC_PS = "PS"
+ X4XX_GPIO_SRC_MPM = "MPM"
+ X4XX_GPIO_SRC_USER_APP = "USER_APP"
+ X4XX_GPIO_SRC_RADIO = [
+ ["DB0_RF0", "DB0_RF1", "DB0_SPI"],
+ ["DB1_RF0", "DB1_RF1", "DB1_SPI"]
+ ]
+ X4XX_GPIO_WIDTH = 12
+
class _PortMapDescriptor:
"""
Helper class to hold port mapping relevant information
@@ -171,7 +192,33 @@ class DioControl:
self.power_good = Gpio('%s_PWR_GOOD' % prefix, Gpio.INPUT)
- def __init__(self, mboard_regs, mboard_cpld, log):
+ class _GpioReg:
+ """
+ Helper class for manipulating GPIO source configuration registers of this form:
+ [31..28]: Reserved
+ [27..16]: Port B
+ [15..12]: Reserved
+ [11..0]: Port A
+ """
+
+ def __init__(self, dio_control, bank, offset):
+ self.offset = offset
+ self.value = dio_control.mboard_regs.peek32(offset)
+ self.mboard_regs = dio_control.mboard_regs
+ self.bank_offset = 0 if bank == dio_control.X4XX_GPIO_BANKS[0] else 16
+
+ def set_pin(self, pin_index, value):
+ self.value &= ~(1 << (pin_index + self.bank_offset))
+ self.value |= (value << (pin_index + self.bank_offset))
+
+ def get_pin(self, pin_index):
+ return bool((self.value >> (pin_index + self.bank_offset)) & 0x1)
+
+ def save(self):
+ self.mboard_regs.poke32(self.offset, self.value)
+
+
+ def __init__(self, mboard_regs, mboard_cpld, log, dboards):
"""
Initializes access to hardware components as well as creating known
port mappings
@@ -206,10 +253,23 @@ class DioControl:
self._dio0_fault_monitor.start()
self._dio1_fault_monitor.start()
+ # Init GPIO sources
+ gpio_srcs = [
+ self.X4XX_GPIO_SRC_PS,
+ self.X4XX_GPIO_SRC_MPM,
+ self.X4XX_GPIO_SRC_USER_APP
+ ]
+ for dboard in dboards:
+ gpio_srcs.extend(self.X4XX_GPIO_SRC_RADIO[dboard.slot_idx])
+
+ self._gpio_srcs = { gpio_bank : gpio_srcs for gpio_bank in self.X4XX_GPIO_BANKS }
+
+ self.log.debug(f"Found the following GPIO sources: {', '.join(gpio_srcs)}")
+
def _monitor_dio_fault(self, dio_port, fault, tear_down):
"""
Monitor the DIO_INT lines to detect an external power fault.
- If there is a fault, turn off external power.
+ If there is a fault, turn off external power.
"""
self.log.trace("Launching monitor loop...")
fault_line = Gpio(fault, Gpio.FALLING_EDGE)
@@ -218,7 +278,7 @@ class DioControl:
if fault_line.event_wait():
# If we saw a fault, disable the external power
self.log.warning("DIO fault occurred on port {} - turning off external power"
- .format(dio_port))
+ .format(dio_port))
self.set_external_power(dio_port, 0)
# If the event wait gets interrupted because we are trying to tear down then stop
# the monitoring process. If not, keep monitoring
@@ -420,6 +480,112 @@ class DioControl:
for i, width in enumerate(col_widths)
]) + "\n"
+ def get_gpio_banks(self):
+ """
+ Returns a list of GPIO banks over which MPM has any control
+ """
+ return self.X4XX_GPIO_BANKS
+
+ def get_gpio_srcs(self, bank: str):
+ """
+ Return a list of valid GPIO sources for a given bank
+ """
+ assert bank in self.get_gpio_banks(), f"Invalid GPIO bank: {bank}"
+ return self._gpio_srcs[bank]
+
+ def get_gpio_src(self, bank: str):
+ """
+ Return the currently selected GPIO source for a given bank. The return
+ value is a list of strings. The length of the vector is identical to
+ the number of controllable GPIO pins on this bank. USER_APP is a GPIO
+ source that can be used in custom FPGA designs (e.g. LabView binary uses
+ this pin source).
+ """
+ assert bank in self.get_gpio_banks(), f"Invalid GPIO bank: {bank}"
+
+ master_reg = self._GpioReg(self, bank, self.FPGA_DIO_MASTER_REGISTER)
+ source_reg = self._GpioReg(self, bank, self.FPGA_DIO_SOURCE_REGISTER)
+ radio_source_reg = self._GpioReg(self, bank, self.FPGA_DIO_RADIO_SOURCE_REGISTER)
+ interface_select_reg = self._GpioReg(self, bank, self.FPGA_DIO_INTERFACE_DIO_SELECT_REGISTER)
+ override_reg = self._GpioReg(self, bank, self.FPGA_DIO_OVERRIDE_REGISTER)
+ sw_control_reg = self._GpioReg(self, bank, self.FPGA_DIO_SW_DIO_CONTROL_REGISTER)
+ classic_atr_config_reg = self._GpioReg(self, bank, self.RADIO_DIO_CLASSIC_ATR_CONFIG_REGISTER)
+
+ def get_gpio_src_i(gpio_pin_index):
+ """
+ Return the current source given a pin index.
+ """
+ if source_reg.get_pin(gpio_pin_index):
+ if override_reg.get_pin(gpio_pin_index):
+ db = int(interface_select_reg.get_pin(gpio_pin_index))
+ return f"DB{db}_SPI"
+ else:
+ db = int(radio_source_reg.get_pin(gpio_pin_index))
+ ch = int(classic_atr_config_reg.get_pin(gpio_pin_index))
+ return f"DB{db}_RF{ch}"
+ else:
+ if master_reg.get_pin(gpio_pin_index):
+ if sw_control_reg.get_pin(gpio_pin_index):
+ return self.X4XX_GPIO_SRC_PS
+ else:
+ return self.X4XX_GPIO_SRC_MPM
+ else:
+ return self.X4XX_GPIO_SRC_USER_APP
+
+ return [get_gpio_src_i(i) for i in range(self.X4XX_GPIO_WIDTH)]
+
+ def set_gpio_src(self, bank: str, src):
+ """
+ Set the GPIO source for a given bank.
+ src input is big-endian
+ Usage:
+ > set_gpio_src <bank> <srcs>
+ > set_gpio_src GPIO0 PS DB1_RF0 PS PS MPM PS PS PS MPM USER_APP PS
+ """
+ assert bank in self.get_gpio_banks(), f"Invalid GPIO bank: {bank}"
+ assert len(src) == self.X4XX_GPIO_WIDTH, f"Invalid number of GPIO sources! Expecting {self.X4XX_GPIO_WIDTH}, but got {len(src)}."
+
+ for pin_index, src_name in enumerate(src):
+ if src_name not in self.get_gpio_srcs(bank):
+ raise RuntimeError(f"Invalid GPIO source name `{src_name}' at bit position {pin_index}!")
+
+ master_reg = self._GpioReg(self, bank, self.FPGA_DIO_MASTER_REGISTER)
+ source_reg = self._GpioReg(self, bank, self.FPGA_DIO_SOURCE_REGISTER)
+ radio_source_reg = self._GpioReg(self, bank, self.FPGA_DIO_RADIO_SOURCE_REGISTER)
+ interface_select_reg = self._GpioReg(self, bank, self.FPGA_DIO_INTERFACE_DIO_SELECT_REGISTER)
+ override_reg = self._GpioReg(self, bank, self.FPGA_DIO_OVERRIDE_REGISTER)
+ sw_control_reg = self._GpioReg(self, bank, self.FPGA_DIO_SW_DIO_CONTROL_REGISTER)
+ classic_atr_config_reg = self._GpioReg(self, bank, self.RADIO_DIO_CLASSIC_ATR_CONFIG_REGISTER)
+
+ for pin_index, src_name in enumerate(src):
+ radio_srcs = [item for sublist in self.X4XX_GPIO_SRC_RADIO for item in sublist]
+ if src_name in radio_srcs:
+ source_reg.set_pin(pin_index, 1)
+ slot = int(src_name[2])
+ if src_name.endswith("_SPI"):
+ override_reg.set_pin(pin_index, 1)
+ interface_select_reg.set_pin(pin_index, slot)
+ else:
+ channel = int(src_name[6])
+ override_reg.set_pin(pin_index, 0)
+ radio_source_reg.set_pin(pin_index, slot)
+ classic_atr_config_reg.set_pin(pin_index, channel)
+ else:
+ source_reg.set_pin(pin_index, 0)
+ if src_name in (self.X4XX_GPIO_SRC_PS, self.X4XX_GPIO_SRC_MPM):
+ master_reg.set_pin(pin_index, 1)
+ sw_control_reg.set_pin(pin_index, int(src_name == self.X4XX_GPIO_SRC_PS))
+ else:
+ master_reg.set_pin(pin_index, 0)
+
+ master_reg.save()
+ source_reg.save()
+ radio_source_reg.save()
+ interface_select_reg.save()
+ override_reg.save()
+ sw_control_reg.save()
+ classic_atr_config_reg.save()
+
# --------------------------------------------------------------------------
# Public API
# --------------------------------------------------------------------------
@@ -453,31 +619,6 @@ class DioControl:
raise RuntimeError("Could not map %s to port mapping" % mapping)
self.mapping = self.port_mappings[map_name]
- def set_pin_master(self, port, pin, value=1):
- """
- Set master pin of a port. The master pin decides whether the DIO board
- pin is driven by the PS (1) or FPGA (0) register interface. To change
- the pin value the current register content is read first and modified
- before it is written back, so the register must be readable.
- :param port: port to change master assignment on
- :param pin: pin to change
- :param value: desired pin value
- """
- content = self._calc_register_value(self.FPGA_DIO_MASTER_REGISTER,
- port, pin, value)
- self.mboard_regs.poke32(self.FPGA_DIO_MASTER_REGISTER, content)
-
- def set_pin_masters(self, port, values):
- """
- Set all master pins of a port at once using a bit mask.
- :param port: port to change master pin assignment
- :param values: New pin assignment represented by an integer. Each bit of
- values corresponds to a pin on board according to current
- mapping scheme. Bits that do not correspond to a pin in
- the current mapping scheme are skipped.
- """
- self._set_pin_values(port, values, self.set_pin_master)
-
def set_pin_direction(self, port, pin, value=1):
"""
Set direction pin of a port. The direction pin decides whether the DIO