diff options
author | Lane Kolbly <lane.kolbly@ni.com> | 2021-10-18 16:45:46 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-11-03 06:04:19 -0700 |
commit | c23dc3b0122a46353810d1ccbe98c08b080850e8 (patch) | |
tree | d89ab38b0565190737bd5e16b65c601f4df58fd6 /host/lib | |
parent | 3162b92bedda20f5b376137f5e918ebe07406fbb (diff) | |
download | uhd-c23dc3b0122a46353810d1ccbe98c08b080850e8.tar.gz uhd-c23dc3b0122a46353810d1ccbe98c08b080850e8.tar.bz2 uhd-c23dc3b0122a46353810d1ccbe98c08b080850e8.zip |
host: x4xx: Implement GPIO API
This implements the GPIO API for X410 through get_gpio_attr and
set_gpio_attr. In ATR mode, which channel's ATR state is chosen by the
set_gpio_src call, setting e.g. DB0_RF0 for channel 0 or DB0_RF1 for
channel 1. In manual mode, all 24 bits (for both ports) are set in
a single register write.
Although the front panel of the device has two ports, labelled GPIO0 and
GPIO1, this API exposes them as though they were a single 24-bit GPIO
port.
Diffstat (limited to 'host/lib')
-rw-r--r-- | host/lib/usrp/x400/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_gpio_control.cpp | 127 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_gpio_control.hpp | 82 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_radio_control.cpp | 35 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_radio_control.hpp | 10 |
5 files changed, 255 insertions, 0 deletions
diff --git a/host/lib/usrp/x400/CMakeLists.txt b/host/lib/usrp/x400/CMakeLists.txt index 3586d3a35..7885b0e3d 100644 --- a/host/lib/usrp/x400/CMakeLists.txt +++ b/host/lib/usrp/x400/CMakeLists.txt @@ -14,6 +14,7 @@ if(ENABLE_X400) LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/adc_self_calibration.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x400_gpio_control.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x400_radio_control.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x400_rfdc_control.cpp ) diff --git a/host/lib/usrp/x400/x400_gpio_control.cpp b/host/lib/usrp/x400/x400_gpio_control.cpp new file mode 100644 index 000000000..3213f6e64 --- /dev/null +++ b/host/lib/usrp/x400/x400_gpio_control.cpp @@ -0,0 +1,127 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "x400_gpio_control.hpp" + +using namespace uhd::rfnoc::x400; + +namespace { +namespace gpio_regmap { +// Relative to the channel's ATR base +constexpr uint32_t ATR_IDLE_OFFSET = 0x0; +constexpr uint32_t ATR_RX_OFFSET = 0x4; +constexpr uint32_t ATR_TX_OFFSET = 0x8; +constexpr uint32_t ATR_XX_OFFSET = 0xC; + +constexpr uint32_t ATR_STRIDE = 0x10; + +// Relative to the radio control base +constexpr uint32_t CLASSIC_MODE_OFFSET = 0x44; +constexpr uint32_t DDR_OFFSET = 0x48; +constexpr uint32_t DISABLED_OFFSET = 0x4C; +constexpr uint32_t READBACK_OFFSET = 0x50; + +constexpr uint32_t DIO_MIRROR_WINDOW = 0x1000; + +// Relative to the DIO register map +constexpr uint32_t DIO_DIRECTION_REG = 0x4; +} // namespace gpio_regmap + +// There are two ports, each with 12 pins +constexpr size_t NUM_PINS_PER_PORT = 12; + +// These values should match the values in MPM's x4xx_periphs.py "DIO_PORT_MAP" +constexpr uint32_t PORTA_MAPPING[12] = {1, 0, 2, 3, 5, 4, 6, 7, 9, 8, 10, 11}; +constexpr uint32_t PORTB_MAPPING[12] = {10, 11, 9, 8, 6, 7, 5, 4, 2, 3, 1, 0}; +} // namespace + +const char* uhd::rfnoc::x400::GPIO_BANK_NAME = "GPIO"; + +gpio_control::gpio_control(uhd::usrp::x400_rpc_iface::sptr rpcc, uhd::wb_iface::sptr iface) + : _rpcc(rpcc), _regs(iface) +{ + _rpcc->dio_set_port_mapping("DIO"); + _rpcc->dio_set_voltage_level("PORTA", "3V3"); + _rpcc->dio_set_voltage_level("PORTB", "3V3"); + + // Hardcode classic ATR (channels operate independently) + _regs->poke32(gpio_regmap::CLASSIC_MODE_OFFSET, 0x1); + + // Initialize everything as inputs + _rpcc->dio_set_pin_directions("PORTA", 0x0); + _rpcc->dio_set_pin_directions("PORTB", 0x0); + + for (size_t bank = 0; bank < 2; bank++) { + const wb_iface::wb_addr_type atr_base = bank * gpio_regmap::ATR_STRIDE; + usrp::gpio_atr::gpio_atr_offsets regmap{ + atr_base + gpio_regmap::ATR_IDLE_OFFSET, + atr_base + gpio_regmap::ATR_RX_OFFSET, + atr_base + gpio_regmap::ATR_TX_OFFSET, + atr_base + gpio_regmap::ATR_XX_OFFSET, + gpio_regmap::DDR_OFFSET, + gpio_regmap::DISABLED_OFFSET, + gpio_regmap::READBACK_OFFSET, + }; + _gpios.push_back(usrp::gpio_atr::gpio_atr_3000::make(_regs, regmap)); + } +} + +void gpio_control::set_gpio_attr( + const uhd::usrp::gpio_atr::gpio_attr_t attr, const uint32_t value) +{ + if (attr == uhd::usrp::gpio_atr::GPIO_DDR) { + // We have to adjust the MB CPLD as well. MPM takes care of coordinating + // the FPGA and the CPLD. + _rpcc->dio_set_pin_directions("PORTA", value & 0xFFF); + _rpcc->dio_set_pin_directions("PORTB", value >> 12); + } + + _gpios[0]->set_gpio_attr(attr, internalize_value(value)); + if (is_atr_attr(attr)) { + _gpios[1]->set_gpio_attr(attr, internalize_value(value)); + } +} + +bool gpio_control::is_atr_attr(const uhd::usrp::gpio_atr::gpio_attr_t attr) +{ + return attr == uhd::usrp::gpio_atr::GPIO_ATR_0X || attr == uhd::usrp::gpio_atr::GPIO_ATR_RX + || attr == uhd::usrp::gpio_atr::GPIO_ATR_TX || attr == uhd::usrp::gpio_atr::GPIO_ATR_XX; +} + +uint32_t gpio_control::internalize_value(const uint32_t value) +{ + return (value & 0xFFF) | ((value & 0x00FFF000) << 4); +} + +uint32_t gpio_control::publicize_value(const uint32_t value) +{ + return (value & 0xFFF) | ((value & 0x0FFF0000) >> 4); +} + +uint32_t gpio_control::unmap_dio(const uint32_t bank, const uint32_t raw_form) +{ + const uint32_t* const mapping = bank == 1 ? PORTB_MAPPING : PORTA_MAPPING; + uint32_t result = 0; + for (size_t i = 0; i < NUM_PINS_PER_PORT; i++) { + if ((raw_form & (1 << i)) != 0) { + result |= 1 << mapping[i]; + } + } + return result; +} + +uint32_t gpio_control::get_gpio_attr(const uhd::usrp::gpio_atr::gpio_attr_t attr) +{ + if (attr == uhd::usrp::gpio_atr::GPIO_DDR) { + // Retrieve the actual state from the FPGA mirror of the CPLD state + const uint32_t raw_value = _regs->peek32( + gpio_regmap::DIO_MIRROR_WINDOW + gpio_regmap::DIO_DIRECTION_REG); + return (unmap_dio(1, raw_value >> 16) << NUM_PINS_PER_PORT) + | unmap_dio(0, raw_value & 0xFFFF); + } + + return publicize_value(_gpios[0]->get_attr_reg(attr)); +} diff --git a/host/lib/usrp/x400/x400_gpio_control.hpp b/host/lib/usrp/x400/x400_gpio_control.hpp new file mode 100644 index 000000000..a0c0593ec --- /dev/null +++ b/host/lib/usrp/x400/x400_gpio_control.hpp @@ -0,0 +1,82 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> +#include <uhdlib/usrp/common/rpc.hpp> +#include <vector> + +namespace uhd { namespace rfnoc { namespace x400 { + +// The name of the X400's GPIO bank +extern const char* GPIO_BANK_NAME; + +/*! Abstract X400's GPIO control to match the "gpio_attr" control scheme. + * + * The front panel has two ports on it, labelled GPIO0 and GPIO1. The registers + * to control all of the GPIOs contain 24 bits, split between bit indices + * [31:16] and [11:0]. Additionally, the underlying radio control registers + * support a full 16-entry lookup table for the ATR state, with the 4 bits + * being a combination of the ATR state for the two channels. The classic + * "gpio_attr" control scheme only considers channels independently - i.e., + * a single 4-entry lookup table for each channel. X400 supports this behaviour + * as well via the "classic ATR" switch, which this class uses. + * + * All of the public values are exposed as a single 24-bit wide field, [23:0] + * + * The data direction registers (DDR) have to be set in two places: Both in the + * internal radio control registers, as well as in MPM to configure the DIO + * board. + */ +class gpio_control { +public: + using sptr = std::shared_ptr<gpio_control>; + + /*! Constructs a gpio_control given the given offset. Assumes that the + * 16-table ATR entry begins at address 0x0 in \p iface. + * + * \param rpcc RPC object to talk to MPM + * \param iface wb_iface to talk to the radio registers + */ + gpio_control(uhd::usrp::x400_rpc_iface::sptr rpcc, wb_iface::sptr iface); + + /*! Set the given GPIO attribute. See gpio_atr_3000 for details. + */ + void set_gpio_attr(const usrp::gpio_atr::gpio_attr_t attr, const uint32_t value); + + /*! Get the given GPIO attribute. See gpio_atr_3000 for details. + */ + uint32_t get_gpio_attr(const usrp::gpio_atr::gpio_attr_t attr); + +private: + /*! Converts from the public-facing [23:0] format to the internal [31:16], + * [11:0] format. + */ + static uint32_t internalize_value(const uint32_t value); + + /*! Converts from the internal [31:16], [11:0] format to the public-facing + * [23:0] format. + */ + static uint32_t publicize_value(const uint32_t value); + + /*! Convert from the internal FPGA pin mapping to the "DIO" mapping. This + * matches the "DIO_PORT_MAP" field in MPM's x4xx_periphs.py file. + */ + static uint32_t unmap_dio(const uint32_t bank, const uint32_t raw_form); + + /*! Returns whether the given attribute is setting one of the ATR entries. + */ + static bool is_atr_attr(const usrp::gpio_atr::gpio_attr_t attr); + + uhd::usrp::x400_rpc_iface::sptr _rpcc; + wb_iface::sptr _regs; + + // There are two GPIOs, one for each channel. These two are set in unison. + std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _gpios; +}; + +}}} // namespace uhd::rfnoc::x400 diff --git a/host/lib/usrp/x400/x400_radio_control.cpp b/host/lib/usrp/x400/x400_radio_control.cpp index 281ae7916..8b0fbf906 100644 --- a/host/lib/usrp/x400/x400_radio_control.cpp +++ b/host/lib/usrp/x400/x400_radio_control.cpp @@ -12,6 +12,7 @@ #include <uhdlib/usrp/dboard/debug_dboard.hpp> #include <uhdlib/usrp/dboard/null_dboard.hpp> #include <uhdlib/usrp/dboard/zbx/zbx_dboard.hpp> +#include <uhdlib/rfnoc/reg_iface_adapter.hpp> namespace uhd { namespace rfnoc { @@ -178,6 +179,9 @@ x400_radio_control_impl::x400_radio_control_impl(make_args_ptr make_args) get_num_output_ports(), _adc_self_calibration, get_unique_id()); register_feature(_fpga_onload); _mb_control->_fpga_onload->request_cb(_fpga_onload); + + _gpios = std::make_shared<x400::gpio_control>(_rpcc, + RFNOC_MAKE_WB_IFACE(regmap::PERIPH_BASE + 0xC000, 0)); } void x400_radio_control_impl::_init_prop_tree() @@ -327,6 +331,37 @@ double x400_radio_control_impl::set_rate(const double rate) return get_rate(); } +std::vector<std::string> x400_radio_control_impl::get_gpio_banks() const +{ + return {x400::GPIO_BANK_NAME}; +} + +uint32_t x400_radio_control_impl::get_gpio_attr( + const std::string& bank, const std::string& attr) +{ + std::lock_guard<std::recursive_mutex> l(_lock); + if (bank != x400::GPIO_BANK_NAME) { + throw uhd::key_error("Invalid GPIO bank " + bank); + } + if (usrp::gpio_atr::gpio_attr_rev_map.count(attr) == 0) { + throw uhd::key_error("Invalid GPIO attribute " + attr); + } + return _gpios->get_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr)); +} + +void x400_radio_control_impl::set_gpio_attr( + const std::string& bank, const std::string& attr, const uint32_t value) +{ + std::lock_guard<std::recursive_mutex> l(_lock); + if (bank != x400::GPIO_BANK_NAME) { + throw uhd::key_error("Invalid GPIO bank " + bank); + } + if (usrp::gpio_atr::gpio_attr_rev_map.count(attr) == 0) { + throw uhd::key_error("Invalid GPIO attribute " + attr); + } + _gpios->set_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr), value); +} + eeprom_map_t x400_radio_control_impl::get_db_eeprom() { std::lock_guard<std::recursive_mutex> l(_lock); diff --git a/host/lib/usrp/x400/x400_radio_control.hpp b/host/lib/usrp/x400/x400_radio_control.hpp index 65b37cc2b..8848926c7 100644 --- a/host/lib/usrp/x400/x400_radio_control.hpp +++ b/host/lib/usrp/x400/x400_radio_control.hpp @@ -23,6 +23,8 @@ #include <mutex> #include <string> #include <vector> +#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> +#include "x400_gpio_control.hpp" namespace uhd { namespace rfnoc { @@ -55,6 +57,12 @@ public: uhd::eeprom_map_t get_db_eeprom() override; + // GPIO methods + std::vector<std::string> get_gpio_banks() const override; + uint32_t get_gpio_attr(const std::string& bank, const std::string& attr) override; + void set_gpio_attr( + const std::string& bank, const std::string& attr, const uint32_t value) override; + // Shim calls for every method in rf_control_core double set_rate(const double rate) override; std::string get_tx_antenna(const size_t chan) const override; @@ -171,6 +179,8 @@ private: uhd::features::adc_self_calibration_iface::sptr _adc_self_calibration; + x400::gpio_control::sptr _gpios; + class fpga_onload : public uhd::features::fpga_load_notification_iface { public: |