diff options
| -rw-r--r-- | host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp | 8 | ||||
| -rw-r--r-- | host/lib/usrp/mpmd/mpmd_mb_controller.cpp | 40 | ||||
| -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 | 
7 files changed, 168 insertions, 37 deletions
| diff --git a/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp b/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp index 0c64ecbad..8111d05b8 100644 --- a/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp +++ b/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp @@ -82,6 +82,10 @@ public:      uhd::sensor_value_t get_sensor(const std::string& name);      std::vector<std::string> get_sensor_names();      uhd::usrp::mboard_eeprom_t get_eeprom(); +    std::vector<std::string> get_gpio_banks() const; +    std::vector<std::string> get_gpio_srcs(const std::string& bank) const; +    std::vector<std::string> get_gpio_src(const std::string& bank); +    void set_gpio_src(const std::string& bank, const std::vector<std::string>& src);  private:      /************************************************************************** @@ -94,6 +98,10 @@ private:      //! List of MB sensor names      std::unordered_set<std::string> _sensor_names; + +    //! Cache of available GPIO sources +    std::vector<std::string> _gpio_banks; +    std::unordered_map<std::string, std::vector<std::string>> _gpio_srcs;  };  }} // namespace uhd::rfnoc diff --git a/host/lib/usrp/mpmd/mpmd_mb_controller.cpp b/host/lib/usrp/mpmd/mpmd_mb_controller.cpp index 13e57b273..9aa61a5a8 100644 --- a/host/lib/usrp/mpmd/mpmd_mb_controller.cpp +++ b/host/lib/usrp/mpmd/mpmd_mb_controller.cpp @@ -23,10 +23,18 @@ mpmd_mb_controller::mpmd_mb_controller(          register_timekeeper(tk_idx, std::make_shared<mpmd_timekeeper>(tk_idx, _rpc));      } +    // Enumerate sensors      auto sensor_list =          _rpc->request_with_token<std::vector<std::string>>("get_mb_sensors");      UHD_LOG_DEBUG("MPMD", "Found " << sensor_list.size() << " motherboard sensors.");      _sensor_names.insert(sensor_list.cbegin(), sensor_list.cend()); + +    // Enumerate GPIO banks that are under mb_controller control +    _gpio_banks = _rpc->request_with_token<std::vector<std::string>>("get_gpio_banks"); +    for (const auto& bank : _gpio_banks) { +        _gpio_srcs.insert({bank, +            _rpc->request_with_token<std::vector<std::string>>("get_gpio_srcs", bank)}); +    }  }  /****************************************************************************** @@ -174,3 +182,35 @@ uhd::usrp::mboard_eeprom_t mpmd_mb_controller::get_eeprom()      return mb_eeprom_dict;  } +std::vector<std::string> mpmd_mb_controller::get_gpio_banks() const +{ +    return _gpio_banks; +} + +std::vector<std::string> mpmd_mb_controller::get_gpio_srcs(const std::string& bank) const +{ +    if (!_gpio_srcs.count(bank)) { +        UHD_LOG_ERROR("MPMD", "Invalid GPIO bank: `" << bank << "'"); +        throw uhd::key_error(std::string("Invalid GPIO bank: ") + bank); +    } +    return _gpio_srcs.at(bank); +} + +std::vector<std::string> mpmd_mb_controller::get_gpio_src(const std::string& bank) +{ +    if (!_gpio_srcs.count(bank)) { +        UHD_LOG_ERROR("MPMD", "Invalid GPIO bank: `" << bank << "'"); +        throw uhd::key_error(std::string("Invalid GPIO bank: ") + bank); +    } +    return _rpc->request_with_token<std::vector<std::string>>("get_gpio_srcs", bank); +} + +void mpmd_mb_controller::set_gpio_src( +    const std::string& bank, const std::vector<std::string>& src) +{ +    if (!_gpio_srcs.count(bank)) { +        UHD_LOG_ERROR("MPMD", "Invalid GPIO bank: `" << bank << "'"); +        throw uhd::key_error(std::string("Invalid GPIO bank: ") + bank); +    } +    _rpc->notify_with_token("set_gpio_src", bank, src); +} 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 | 
