aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
authorLane Kolbly <lane.kolbly@ni.com>2021-10-18 16:45:46 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2021-11-03 06:04:19 -0700
commitc23dc3b0122a46353810d1ccbe98c08b080850e8 (patch)
treed89ab38b0565190737bd5e16b65c601f4df58fd6 /host/lib
parent3162b92bedda20f5b376137f5e918ebe07406fbb (diff)
downloaduhd-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.txt1
-rw-r--r--host/lib/usrp/x400/x400_gpio_control.cpp127
-rw-r--r--host/lib/usrp/x400/x400_gpio_control.hpp82
-rw-r--r--host/lib/usrp/x400/x400_radio_control.cpp35
-rw-r--r--host/lib/usrp/x400/x400_radio_control.hpp10
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: