diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-11-14 13:12:20 -0800 |
---|---|---|
committer | Brent Stapleton <brent.stapleton@ettus.com> | 2020-01-23 11:37:51 -0800 |
commit | 0cf54ce07335f60642a14df7e6107422a5aeb9a0 (patch) | |
tree | ae49a1f983fbeab9aa9f652204940d741b98e030 /mpm | |
parent | 09af39604c91bbb725a366e41bab4654274870c2 (diff) | |
download | uhd-0cf54ce07335f60642a14df7e6107422a5aeb9a0.tar.gz uhd-0cf54ce07335f60642a14df7e6107422a5aeb9a0.tar.bz2 uhd-0cf54ce07335f60642a14df7e6107422a5aeb9a0.zip |
mpm/mpmd: Expose APIs to drive GPIO sources
The N310 has a feature that allows the front panel GPIOs to be driven by
various sources: The PS, or any of the radio channels. The MPM-based
APIs did not expose any way to change that.
Changes:
- Add MPM APIs to PeripheralManagerBase and n3xx classes
- Improve comments and explanations
- Add host-side hooks into these new APIs in mpmd_mb_controller
- Implement these APIs for N3xx
The N3xx devices will have the option to set the GPIO source to "PS", or
to one of "RF0", "RF1", "RF2", "RF3" (if there are four channels; the
N300 and N320 can only go up to RF1).
Note: The N310 radio does not have separate FP-GPIO banks for channels
0 and 1, which needs to be fixed in a separate commit.
Diffstat (limited to 'mpm')
-rwxr-xr-x | mpm/python/n3xx_bist | 13 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/bist.py | 4 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/base.py | 38 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx.py | 98 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py | 4 |
5 files changed, 120 insertions, 37 deletions
diff --git a/mpm/python/n3xx_bist b/mpm/python/n3xx_bist index a8bda66f5..1202fe121 100755 --- a/mpm/python/n3xx_bist +++ b/mpm/python/n3xx_bist @@ -472,8 +472,8 @@ class N3XXBIST(bist.UsrpBIST): - read_patterns: A list of patterns that were read back """ assert 'gpio' in self.tests_to_run - # patterns = list(range(64)) - GPIO_WIDTH = 12 + # Our FP-GPIO has 12 programmable pins + gpio_width = 12 patterns = range(64) if self.args.dry_run: return True, { @@ -484,16 +484,17 @@ class N3XXBIST(bist.UsrpBIST): gpio_tca6424 = n3xx_periphs.TCA6424(self.mb_rev) gpio_tca6424.set("FPGA-GPIO-EN") mb_regs = n3xx_periphs.MboardRegsControl(n3xx.n3xx.mboard_regs_label, self.log) + # We set all 12 pins to be driven by the PS mb_regs.set_fp_gpio_master(0xFFF) # Allow some time for the front-panel GPIOs to become usable time.sleep(.5) - ddr1 = 0x03f - ddr2 = 0xfc0 + ddr1 = 0x03f # Lower 6 pins are outputs + ddr2 = 0xfc0 # Upper 6 pins are inputs def _run_gpio(ddr, patterns): " Run a GPIO test for a given set of patterns " gpio_ctrl = n3xx_periphs.FrontpanelGPIO(ddr) for pattern in patterns: - bist.gpio_set_all(gpio_ctrl, pattern, GPIO_WIDTH, ddr) + bist.gpio_set_all(gpio_ctrl, pattern, gpio_width, ddr) time.sleep(0.1) gpio_rb = gpio_ctrl.get_all() if pattern != gpio_rb: @@ -502,7 +503,7 @@ class N3XXBIST(bist.UsrpBIST): return True, {'write_patterns': list(patterns), 'read_patterns': list(patterns)} status, data = _run_gpio(ddr1, patterns) - if not status: + if not status: return status, data status, data = _run_gpio(ddr2, patterns) return status, data diff --git a/mpm/python/usrp_mpm/bist.py b/mpm/python/usrp_mpm/bist.py index e26d6d36c..b20020e1f 100644 --- a/mpm/python/usrp_mpm/bist.py +++ b/mpm/python/usrp_mpm/bist.py @@ -246,7 +246,9 @@ def get_tpm_caps_info(): return result def gpio_set_all(gpio_bank, value, gpio_size, ddr_mask): - """Helper function for set gpio. + """ + Helper function to set GPIOs + What this function do is take decimal value and convert to a binary string then try to set those individual bits to the gpio_bank. Arguments: diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py index 6d363fd89..8f75a97c6 100644 --- a/mpm/python/usrp_mpm/periph_manager/base.py +++ b/mpm/python/usrp_mpm/periph_manager/base.py @@ -925,3 +925,41 @@ class PeriphManagerBase(object): Gets the RFNoC-related clocks present in the FPGA design """ raise NotImplementedError("get_clocks() not implemented.") + + ####################################################################### + # GPIO API + ####################################################################### + def get_gpio_banks(self): + """ + Returns a list of GPIO banks over which MPM has any control + """ + self.log.debug("get_gpio_banks(): No banks defined on this device.") + return [] + + def get_gpio_srcs(self, bank): + """ + Return a list of valid GPIO sources for a given bank + """ + assert bank in self.get_gpio_banks(), \ + "Invalid GPIO bank: {}".format(bank) + return [] + + def get_gpio_src(self, bank): + """ + 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. + """ + assert bank in self.get_gpio_banks(), \ + "Invalid GPIO bank: {}".format(bank) + raise NotImplementedError("get_gpio_src() not available on this device!") + + def set_gpio_src(self, bank, src): + """ + Set the GPIO source for a given bank. + """ + assert bank in self.get_gpio_banks(), \ + "Invalid GPIO bank: {}".format(bank) + assert src in self.get_gpio_srcs(bank), \ + "Invalid GPIO source: {}".format(src) + raise NotImplementedError("set_gpio_src() not available on this device!") diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py index fc9a91e7d..d7b50a5f9 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py @@ -44,6 +44,9 @@ N32X_QSFP_I2C_LABEL = 'qsfp-i2c' N3XX_FPGA_COMPAT = (7, 0) N3XX_MONITOR_THREAD_INTERVAL = 1.0 # seconds N3XX_BUS_CLK = 200e6 +N3XX_GPIO_BANKS = "FP0" +N3XX_GPIO_SRC_PS = "PS" +N3XX_FPGPIO_WIDTH = 12 # Import daughterboard PIDs from their respective classes MG_PID = Magnesium.pids[0] @@ -362,6 +365,17 @@ class n3xx(ZynqComponents, PeriphManagerBase): "(e.g., HG, XG images).") # Init FPGA type self._update_fpga_type() + # Init FP-GPIO sources + self._fp_gpio_srcs = [N3XX_GPIO_SRC_PS,] + if self.device_info['product'] == 'n320': + for chan_idx in range(len(self.dboards)): + self._fp_gpio_srcs.append("RF{}".format(chan_idx)) + else: + for chan_idx in range(len(self.dboards)): + self._fp_gpio_srcs.append("RF{}".format(2*chan_idx)) + self._fp_gpio_srcs.append("RF{}".format(2*chan_idx+1)) + self.log.debug("Found the following GPIO sources: {}" + .format(",".join(self._fp_gpio_srcs))) # Init CHDR transports self._xport_mgrs = { 'udp': N3xxXportMgrUDP(self.log.getChild('UDP'), args), @@ -727,41 +741,67 @@ class n3xx(ZynqComponents, PeriphManagerBase): 'gpsdo': 20e6, }[self._clock_source] - 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 + ########################################################################### + # GPIO API + ########################################################################### + def get_gpio_banks(self): + """ + Returns a list of GPIO banks over which MPM has any control """ - self.mboard_regs_control.set_fp_gpio_master(value) + return N3XX_GPIO_BANKS - 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 + def get_gpio_srcs(self, bank): + """ + Return a list of valid GPIO sources for a given bank """ - return self.mboard_regs_control.get_fp_gpio_master() + assert bank in self.get_gpio_banks(), "Invalid GPIO bank: {}".format(bank) + return self._fp_gpio_srcs - 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 + def get_gpio_src(self, bank): + """ + 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. + """ + assert bank in self.get_gpio_banks(), "Invalid GPIO bank: {}".format(bank) + gpio_master_reg = self.mboard_regs_control.get_fp_gpio_master() + gpio_radio_src_reg = self.mboard_regs_control.get_fp_gpio_radio_src() + def get_gpio_src_i(gpio_pin_index): + """ + Return the current radio source given a pin index. + """ + if gpio_master_reg & (1 << gpio_pin_index): + return N3XX_GPIO_SRC_PS + radio_src = (gpio_radio_src_reg >> (2 * gpio_pin_index)) & 0b11 + return "RF{}".format(radio_src) + return [get_gpio_src_i(i) for i in range(N3XX_FPGPIO_WIDTH)] + + def set_gpio_src(self, bank, src): + """ + Set the GPIO source for a given bank. """ - self.mboard_regs_control.set_fp_gpio_radio_src(value) + assert bank in self.get_gpio_banks(), "Invalid GPIO bank: {}".format(bank) + assert len(src) == N3XX_FPGPIO_WIDTH, \ + "Invalid number of GPIO sources!" + gpio_master_reg = 0x000 + gpio_radio_src_reg = self.mboard_regs_control.get_fp_gpio_radio_src() + for src_index, src_name in enumerate(src): + if src_name not in self.get_gpio_srcs(bank): + raise RuntimeError( + "Invalid GPIO source name `{}' at bit position {}!" + .format(src_name, src_index)) + gpio_master_flag = (src_name == N3XX_GPIO_SRC_PS) + gpio_master_reg = gpio_master_reg | (gpio_master_flag << src_index) + if gpio_master_flag: + continue + # If PS is not the master, we also need to update the radio source: + radio_index = int(src_name[2:]) & 0b11 + gpio_radio_src_reg = gpio_radio_src_reg | (radio_index << (2*src_index)) + self.log.trace("Updating GPIO source: master==0x{:03X} radio_src={:06X}" + .format(gpio_master_reg, gpio_radio_src_reg)) + self.mboard_regs_control.set_fp_gpio_master(gpio_master_reg) + self.mboard_regs_control.set_fp_gpio_radio_src(gpio_radio_src_reg) - 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 - """ - return self.mboard_regs_control.get_fp_gpio_radio_src() ########################################################################### # Hardware periphal controls ########################################################################### diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py index ef2372e62..bcff03c8e 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py @@ -195,6 +195,8 @@ class MboardRegsControl(MboardRegsCommon): """set driver for front panel GPIO Arguments: value {unsigned} -- value is a single bit 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: return self.poke32(self.MB_GPIO_MASTER, value) @@ -211,7 +213,7 @@ class MboardRegsControl(MboardRegsCommon): 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 + value {unsigned} -- value is 2x12 bits, two bits per GPIO pin 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 |