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.
This commit is contained in:
Dhiren Wijesinghe 2021-06-18 14:07:18 -05:00 committed by Aaron Rossetto
parent 426f078ba0
commit c36fc5f6c0
3 changed files with 227 additions and 39 deletions

View file

@ -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

View file

@ -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

View file

@ -659,8 +659,13 @@ class X4XXBIST(bist.UsrpBIST):
mask = 0xDB6D
else:
mask = 0xFFF
mpm_c.dio_set_pin_masters(inport, mask)
mpm_c.dio_set_pin_masters(outport, mask)
bank_convert = {
"PORTA": "GPIO0",
"PORTB": "GPIO1",
}
ps_control_args = ["MPM"] * 12
mpm_c.dio_set_gpio_src(bank_convert[inport], ps_control_args)
mpm_c.dio_set_gpio_src(bank_convert[outport], ps_control_args)
mpm_c.dio_set_voltage_level(inport, voltage)
mpm_c.dio_set_voltage_level(outport, voltage)
mpm_c.dio_set_pin_directions(inport, 0x00000)
@ -677,13 +682,13 @@ class X4XXBIST(bist.UsrpBIST):
for voltage in ["1V8", "2V5", "3V3"]:
for mode in ["DIO", "HDMI"]:
for pattern in [0xFFFF, 0xA5A5, 0x5A5A, 0x0000]:
sys.stderr.write("test: PortA -> PortB, {}, {}, 0x{:04X}"
sys.stderr.write("test: PortA -> PortB, {}, {}, 0x{:04X}\n"
.format(voltage, mode, pattern))
status, data = _run_sub_test(
"PORTB", "PORTA", mode, voltage, pattern)
if not status:
return status, data
sys.stderr.write("test: PortB -> PortA, {}, {}, 0x{:04X}"
sys.stderr.write("test: PortB -> PortA, {}, {}, 0x{:04X}\n"
.format(voltage, mode, pattern))
status, data = _run_sub_test(
"PORTA", "PORTB", mode, voltage, pattern)