diff options
| author | Martin Anderseck <martin.anderseck@ni.com> | 2021-12-15 16:27:23 +0100 | 
|---|---|---|
| committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2022-01-13 14:35:55 -0600 | 
| commit | ee8e4f2c9b1bae8085dff6199c2bade0914748f7 (patch) | |
| tree | cf3ad50af9318a1556e4425b9389e760833fbc38 | |
| parent | 6bb7d61251abd303049dfd0f47bd0266656797fb (diff) | |
| download | uhd-ee8e4f2c9b1bae8085dff6199c2bade0914748f7.tar.gz uhd-ee8e4f2c9b1bae8085dff6199c2bade0914748f7.tar.bz2 uhd-ee8e4f2c9b1bae8085dff6199c2bade0914748f7.zip  | |
SPI: Implement SPI engine for x410
Add SPI Core host implementation for x410 and a discoverable
feature to make it accessible.
| -rw-r--r-- | host/docs/x400_gpio_api.dox | 63 | ||||
| -rw-r--r-- | host/include/uhd/features/discoverable_feature.hpp | 1 | ||||
| -rwxr-xr-x | host/include/uhd/features/spi_getter_iface.hpp | 60 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/usrp/cores/gpio_port_mapper.hpp | 26 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/usrp/cores/spi_core_4000.hpp | 46 | ||||
| -rw-r--r-- | host/lib/usrp/cores/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/cores/spi_core_4000.cpp | 175 | ||||
| -rw-r--r-- | host/lib/usrp/x400/x400_gpio_control.cpp | 37 | ||||
| -rw-r--r-- | host/lib/usrp/x400/x400_gpio_control.hpp | 17 | ||||
| -rw-r--r-- | host/lib/usrp/x400/x400_radio_control.cpp | 30 | ||||
| -rw-r--r-- | host/lib/usrp/x400/x400_radio_control.hpp | 49 | ||||
| -rw-r--r-- | host/tests/CMakeLists.txt | 1 | 
12 files changed, 491 insertions, 15 deletions
diff --git a/host/docs/x400_gpio_api.dox b/host/docs/x400_gpio_api.dox index 80c59cae7..592bff4ba 100644 --- a/host/docs/x400_gpio_api.dox +++ b/host/docs/x400_gpio_api.dox @@ -130,5 +130,68 @@ Valid values can be enumerated with the  uhd::features::gpio_power_iface::supported_voltages() call, and are "1V8",  "2V5", and "3V3". +\section x4x0_spi_iface The x4x0 SPI Mode + +The GPIO ports of the x4x0 can be used with the Serial Peripheral Interface (SPI) to control  +external components. To use SPI mode, set the pins you need on the desired GPIO port to be  +controlled by the SPI engine and configure the data direction. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} +auto usrp = uhd::usrp::multi_usrp::make(args); +auto& spi_getter_iface = +        usrp->get_radio_control().get_feature<uhd::features::spi_getter_iface>(); +usrp->set_gpio_src("GPIO0", +    {"DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI", +        "DB0_SPI"}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The example shows the usage of GPIO port 0 (GPIO0) for SPI and needs to be run for  +GPIO1 again to use that port with SPI, too. + +\subsection x4x0_spi_cfg Configuration of SPI lines + +The x4x0 SPI mode supports up to 4 slaves. All of these slaves may have a different SPI pin  +configuration. The pins available for the usage with SPI are listed in \ref x4x0gpio_fpanel_pins. +For GPIO0 the available pins are enumerated from 0 through 11, for GPIO1 the available pins are  +from 12 through 23. +The vector of slave configurations is passed to the spi_iface_getter to get the reference: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} +    uhd::features::spi_slave_config_t slave_cfg; +    slave_cfg.slave_clk  = 0; +    slave_cfg.slave_miso = 1; +    slave_cfg.slave_mosi = 2; +    slave_cfg.slave_ss   = 3; +    std::vector<uhd::features::spi_slave_config_t> slave_cfgs; +    slave_cfgs.push_back(slave_cfg); +    auto spi_ref = spi_getter_iface.get_spi_ref(slave_cfgs); + +    // Set data direction register (set all to outgoing except for MISO) +    usrp->set_gpio_attr("GPIOA", "DDR", 0xD, 0xF); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\subsection x4x0_spi_r_w Write and read on SPI + +With the SPI reference read and write operations can be performed. For doing this,  +some characteristics of the SPI need to be configured: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} +   uhd::spi_config_t config; +   config.divider = 4; +   config.miso_edge = config.EDGE_RISE; +   ... +   spi_ref->write_spi(0, config, 0xFEFE, 32); +   uint32_t read_data = spi_ref->read_spi(0, config, 0xFEFE, 32); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The SPI clock \f$SCLK\f$ is derived from the Radio clock and the SPI clock divider as follows:  + +\f[SCLK = \frac{Radio\_Clk}{SPI\_CLK\_DIV + 1}\f] +  */  // vim:ft=doxygen: diff --git a/host/include/uhd/features/discoverable_feature.hpp b/host/include/uhd/features/discoverable_feature.hpp index e56c3f6e3..be01a36c7 100644 --- a/host/include/uhd/features/discoverable_feature.hpp +++ b/host/include/uhd/features/discoverable_feature.hpp @@ -36,6 +36,7 @@ public:          REF_CLK_CALIBRATION,          TRIG_IO_MODE,          GPIO_POWER, +        SPI_GETTER_IFACE      };      virtual ~discoverable_feature() = default; diff --git a/host/include/uhd/features/spi_getter_iface.hpp b/host/include/uhd/features/spi_getter_iface.hpp new file mode 100755 index 000000000..1909de095 --- /dev/null +++ b/host/include/uhd/features/spi_getter_iface.hpp @@ -0,0 +1,60 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhd/features/discoverable_feature.hpp> +#include <uhd/types/serial.hpp> +#include <memory> + +namespace uhd { namespace features { + +/*! + * The SPI slave configuration struct: + * Used to configure the GPIO lines for SPI transactions + */ +struct spi_slave_config_t +{ +    //! Indicates which GPIO line to use for this the CS signal. +    uint8_t slave_ss; + +    //! Indicates which GPIO line to use for this the MISO signal. +    uint8_t slave_miso; + +    //! Indicates which GPIO line to use for this the MOSI signal. +    uint8_t slave_mosi; + +    //! Indicates which GPIO line to use for this the SCLK signal. +    uint8_t slave_clk; +}; + +/*! Interface to provide access to SPI Interface. + */ +class spi_getter_iface : public discoverable_feature +{ +public: +    using sptr = std::shared_ptr<spi_getter_iface>; + +    static discoverable_feature::feature_id_t get_feature_id() +    { +        return discoverable_feature::SPI_GETTER_IFACE; +    } + +    std::string get_feature_name() const +    { +        return "SPI Getter Interface"; +    } + +    virtual ~spi_getter_iface() = default; + +    /*! Return the SPI interface to read and write on. +     * \return SPI interface +     */ +    virtual uhd::spi_iface::sptr get_spi_ref( +        const std::vector<uhd::features::spi_slave_config_t>& spi_slave_config) const = 0; +}; + +}} // namespace uhd::features diff --git a/host/lib/include/uhdlib/usrp/cores/gpio_port_mapper.hpp b/host/lib/include/uhdlib/usrp/cores/gpio_port_mapper.hpp new file mode 100644 index 000000000..e8280e45f --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/gpio_port_mapper.hpp @@ -0,0 +1,26 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +namespace uhd { namespace mapper { + +class gpio_port_mapper +{ +public: +    /*! Converts from user-facing GPIO port numbering to internal representation working +     *  on int value. To be used in SPI core. +     */ +    virtual uint32_t map_value(const uint32_t& value) = 0; + +    /*! Converts internal GPIO port representation into user-facing port numbering working +     *  on int value. To be used in SPI core. +     */ +    virtual uint32_t unmap_value(const uint32_t& value) = 0; +}; + +}} // namespace uhd::mapper + diff --git a/host/lib/include/uhdlib/usrp/cores/spi_core_4000.hpp b/host/lib/include/uhdlib/usrp/cores/spi_core_4000.hpp new file mode 100644 index 000000000..4577b14cf --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/spi_core_4000.hpp @@ -0,0 +1,46 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/utils/noncopyable.hpp> +#include <uhdlib/usrp/cores/gpio_port_mapper.hpp> +#include <functional> +#include <memory> + +namespace uhd { namespace cores { +class spi_core_4000 : uhd::noncopyable, public uhd::spi_iface +{ +public: +    using sptr        = std::shared_ptr<spi_core_4000>; +    using mapper_sptr = std::shared_ptr<uhd::mapper::gpio_port_mapper>; +    using poke32_fn_t = std::function<void(uint32_t, uint32_t)>; +    using peek32_fn_t = std::function<uint32_t(uint32_t)>; + +    virtual ~spi_core_4000(void) = default; + +    //! makes a new spi core from iface and slave base +    static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback); + +    //! makes a new spi core from register iface and slave +    static sptr make(poke32_fn_t&& poke32_fn, +        peek32_fn_t&& peek_fn, +        const size_t spi_slave_cfg, +        const size_t spi_transaction_cfg, +        const size_t spi_transaction_go, +        const size_t spi_status, +        const mapper_sptr port_mapper); + +    //! Configures the SPI transaction. The vector index refers to the slave number. +    virtual void set_spi_slave_config( +        const std::vector<uhd::features::spi_slave_config_t>& spi_slave_configs) = 0; +}; + +}} // namespace uhd::cores + diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index 1c20ccc47..807eec685 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -30,6 +30,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/rx_vita_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_4000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/time_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp diff --git a/host/lib/usrp/cores/spi_core_4000.cpp b/host/lib/usrp/cores/spi_core_4000.cpp new file mode 100644 index 000000000..1d72dd88d --- /dev/null +++ b/host/lib/usrp/cores/spi_core_4000.cpp @@ -0,0 +1,175 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/exception.hpp> +#include <uhd/features/spi_getter_iface.hpp> +#include <uhdlib/usrp/cores/gpio_port_mapper.hpp> +#include <uhdlib/usrp/cores/spi_core_4000.hpp> +#include <chrono> +#include <memory> +#include <mutex> +#include <thread> + + +namespace uhd { namespace cores { + +class spi_core_4000_impl : public spi_core_4000 +{ +public: +    spi_core_4000_impl(poke32_fn_t&& poke32_fn, +        peek32_fn_t&& peek32_fn, +        const size_t spi_slave_cfg, +        const size_t spi_transaction_cfg, +        const size_t spi_transaction_go, +        const size_t spi_status, +        const mapper_sptr port_mapper) +        : _poke32(std::move(poke32_fn)) +        , _peek32(std::move(peek32_fn)) +        , _spi_slave_cfg(spi_slave_cfg) +        , _spi_transaction_cfg(spi_transaction_cfg) +        , _spi_transaction_go(spi_transaction_go) +        , _spi_status(spi_status) +        , _port_mapper(port_mapper) +    { +    } + +    void set_spi_slave_config( +        const std::vector<uhd::features::spi_slave_config_t>& ssc) override +    { +        if (ssc.size() > 4) { +            throw uhd::value_error( +                "Passed more than 4 SPI slaves. Maximum number of SPI slaves is 4."); +        } + +        _spi_slave_config = ssc; +    } + +    uint32_t transact_spi(const int which_slave, +        const spi_config_t& config, +        const uint32_t data, +        const size_t num_bits, +        const bool readback) override +    { +        if (static_cast<uint32_t>(which_slave) >= _spi_slave_config.size()) { +            throw uhd::value_error("No configuration given for requested SPI slave."); +        } +        if (config.divider > 0xFFFF) { +            throw uhd::value_error("Clock divider exceeds maximum value (65535)."); +        } +        std::lock_guard<std::mutex> lock(_mutex); +        uint32_t slave_ctrl = 0; +        if (config.mosi_edge == spi_config_t::EDGE_FALL) { +            slave_ctrl |= (1 << 27); +        } +        if (config.miso_edge == spi_config_t::EDGE_RISE) { +            slave_ctrl |= (1 << 26); +        } +        slave_ctrl |= ((num_bits & 0x3F) << 20); +        // slave_ss (which GPIO line for CS signal) +        slave_ctrl |= +            _port_mapper->map_value(_spi_slave_config[which_slave].slave_ss & 0x1F) << 15; +        // slave_miso (which GPIO line for MISO signal) +        slave_ctrl |= +            _port_mapper->map_value(_spi_slave_config[which_slave].slave_miso & 0x1F) +            << 10; +        // slave_mosi (which GPIO line for MOSI signal) +        slave_ctrl |= +            _port_mapper->map_value(_spi_slave_config[which_slave].slave_mosi & 0x1F) +            << 5; +        // slave_clk (which GPIO line for clk signal) +        slave_ctrl |= +            _port_mapper->map_value(_spi_slave_config[which_slave].slave_clk & 0x1F) << 0; + +        // conditionally send slave control +        if (_slave_ctrl_cache[which_slave] != slave_ctrl) { +            _poke32(_spi_slave_cfg + (which_slave * 0x4), slave_ctrl); +            _slave_ctrl_cache[which_slave] = slave_ctrl; +        } + +        uint32_t transaction_config = 0; +        // SPI slave select +        transaction_config |= ((which_slave & 0x3) << 16); +        // SPI clock divider +        transaction_config |= ((config.divider & 0xFFFF) << 0); + +        // conditionally send transaction config +        if (_transaction_cfg_cache != transaction_config) { +            _poke32(_spi_transaction_cfg, transaction_config); +            _transaction_cfg_cache = transaction_config; +        } + +        // load data word (in upper bits) +        const uint32_t data_out = data << (32 - num_bits); + +        // send data word +        _poke32(_spi_transaction_go, data_out); + +        // conditional readback +        if (readback) { +            uint32_t spi_response = 0; +            bool spi_ready        = false; +            // Poll the SPI status until we get a SPI Ready flag +            std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); +            while (!spi_ready) { +                spi_response = _peek32(_spi_status); +                spi_ready    = spi_ready_bit(spi_response); +                if (spi_timeout(t1, 5)) { +                    throw uhd::io_error( +                        "SPI Read did not receive a SPI Ready within 5 seconds"); +                    return 0; +                } +            } +            return (0xFFFFFF & spi_response); +        } + +        return 0; +    } + +private: +    poke32_fn_t _poke32; +    peek32_fn_t _peek32; +    const size_t _spi_slave_cfg; +    const size_t _spi_transaction_cfg; +    const size_t _spi_transaction_go; +    const size_t _spi_status; +    const mapper_sptr _port_mapper; +    std::vector<uint32_t> _slave_ctrl_cache{0, 0, 0, 0}; +    uint32_t _transaction_cfg_cache = 0; +    std::mutex _mutex; +    std::vector<uhd::features::spi_slave_config_t> _spi_slave_config; + +    /*! Gets the SPI_READY flag */ +    bool spi_ready_bit(uint32_t spi_response) +    { +        return (spi_response >> 24) & 0x1; +    } + +    /*! Find out if we timed out */ +    bool spi_timeout(std::chrono::steady_clock::time_point start, uint32_t timeout_s) +    { +        using namespace std::chrono; +        return (duration_cast<seconds>(steady_clock::now() - start)).count() > timeout_s; +    } +}; + +spi_core_4000::sptr spi_core_4000::make(spi_core_4000::poke32_fn_t&& poke32_fn, +    spi_core_4000::peek32_fn_t&& peek32_fn, +    const size_t spi_slave_cfg, +    const size_t spi_transaction_cfg, +    const size_t spi_transaction_go, +    const size_t spi_status, +    const mapper_sptr port_mapper) +{ +    return std::make_shared<spi_core_4000_impl>(std::move(poke32_fn), +        std::move(peek32_fn), +        spi_slave_cfg, +        spi_transaction_cfg, +        spi_transaction_go, +        spi_status, +        port_mapper); +} + +}} // namespace uhd::cores diff --git a/host/lib/usrp/x400/x400_gpio_control.cpp b/host/lib/usrp/x400/x400_gpio_control.cpp index 3213f6e64..18599eb88 100644 --- a/host/lib/usrp/x400/x400_gpio_control.cpp +++ b/host/lib/usrp/x400/x400_gpio_control.cpp @@ -6,6 +6,7 @@  #include "x400_gpio_control.hpp" +  using namespace uhd::rfnoc::x400;  namespace { @@ -33,6 +34,9 @@ constexpr uint32_t DIO_DIRECTION_REG = 0x4;  // There are two ports, each with 12 pins  constexpr size_t NUM_PINS_PER_PORT = 12; +// Start of Port B pin numbers relative to Port A: +constexpr size_t PORT_NUMBER_OFFSET = 16; +  // 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}; @@ -40,7 +44,8 @@ constexpr uint32_t PORTB_MAPPING[12] = {10, 11, 9, 8, 6, 7, 5, 4, 2, 3, 1, 0};  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) +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"); @@ -87,8 +92,10 @@ void gpio_control::set_gpio_attr(  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; +    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) @@ -125,3 +132,27 @@ uint32_t gpio_control::get_gpio_attr(const uhd::usrp::gpio_atr::gpio_attr_t attr      return publicize_value(_gpios[0]->get_attr_reg(attr));  } + +uint32_t uhd::rfnoc::x400::x400_gpio_port_mapping::map_value(const uint32_t& value) +{ +    const uint32_t bank           = value >= NUM_PINS_PER_PORT ? 1 : 0; +    uint32_t pin_intern           = value % NUM_PINS_PER_PORT; +    const uint32_t* const mapping = bank == 1 ? PORTB_MAPPING : PORTA_MAPPING; +    for (size_t i = 0; i < NUM_PINS_PER_PORT; i++) { +        if (mapping[i] == pin_intern) { +            return i + (bank * PORT_NUMBER_OFFSET); +        } +    } +    throw uhd::lookup_error( +        "Could not find corresponding GPIO pin number for given SPI pin " + value); +    return 0; +} + +uint32_t uhd::rfnoc::x400::x400_gpio_port_mapping::unmap_value(const uint32_t& value) +{ +    const uint32_t bank           = value >= PORT_NUMBER_OFFSET ? 1 : 0; +    uint32_t pin_number           = value % PORT_NUMBER_OFFSET; +    const uint32_t* const mapping = bank == 1 ? PORTB_MAPPING : PORTA_MAPPING; +    UHD_ASSERT_THROW(pin_number < NUM_PINS_PER_PORT); +    return mapping[pin_number] + (bank * NUM_PINS_PER_PORT); +}
\ No newline at end of file diff --git a/host/lib/usrp/x400/x400_gpio_control.hpp b/host/lib/usrp/x400/x400_gpio_control.hpp index a0c0593ec..01cfc134e 100644 --- a/host/lib/usrp/x400/x400_gpio_control.hpp +++ b/host/lib/usrp/x400/x400_gpio_control.hpp @@ -6,8 +6,9 @@  #pragma once -#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>  #include <uhdlib/usrp/common/rpc.hpp> +#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> +#include <uhdlib/usrp/cores/gpio_port_mapper.hpp>  #include <vector>  namespace uhd { namespace rfnoc { namespace x400 { @@ -32,7 +33,8 @@ extern const char* GPIO_BANK_NAME;   * internal radio control registers, as well as in MPM to configure the DIO   * board.   */ -class gpio_control { +class gpio_control +{  public:      using sptr = std::shared_ptr<gpio_control>; @@ -79,4 +81,13 @@ private:      std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _gpios;  }; -}}} // namespace uhd::rfnoc::x400 +class x400_gpio_port_mapping : public uhd::mapper::gpio_port_mapper +{ +public: +    x400_gpio_port_mapping(){}; + +    uint32_t map_value(const uint32_t& value) override; + +    uint32_t unmap_value(const uint32_t& value) override; +}; +}}} // namespace uhd::rfnoc::x400
\ No newline at end of file diff --git a/host/lib/usrp/x400/x400_radio_control.cpp b/host/lib/usrp/x400/x400_radio_control.cpp index a58f522bf..75719f6a2 100644 --- a/host/lib/usrp/x400/x400_radio_control.cpp +++ b/host/lib/usrp/x400/x400_radio_control.cpp @@ -5,14 +5,17 @@  //  #include "x400_radio_control.hpp" +#include "x400_gpio_control.hpp"  #include <uhd/rfnoc/registry.hpp> +#include <uhd/types/serial.hpp>  #include <uhd/utils/log.hpp>  #include <uhd/utils/math.hpp> +#include <uhdlib/rfnoc/reg_iface_adapter.hpp>  #include <uhdlib/usrp/common/x400_rfdc_control.hpp> +#include <uhdlib/usrp/cores/spi_core_4000.hpp>  #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 { @@ -182,8 +185,25 @@ x400_radio_control_impl::x400_radio_control_impl(make_args_ptr make_args)      auto mpm_rpc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::mpmd_rpc_iface>();      if (mpm_rpc->get_gpio_banks().size() > 0) { -        _gpios = std::make_shared<x400::gpio_control>(_rpcc, -            RFNOC_MAKE_WB_IFACE(regmap::PERIPH_BASE + 0xC000, 0)); +        _gpios = std::make_shared<x400::gpio_control>( +            _rpcc, RFNOC_MAKE_WB_IFACE(regmap::PERIPH_BASE + 0xC000, 0)); + +        auto gpio_port_mapper = std::shared_ptr<uhd::mapper::gpio_port_mapper>( +            new uhd::rfnoc::x400::x400_gpio_port_mapping); +        auto spicore = uhd::cores::spi_core_4000::make( +            [this](const uint32_t addr, const uint32_t data) { +                regs().poke32(addr, data, get_command_time(0)); +            }, +            [this]( +                const uint32_t addr) { return regs().peek32(addr, get_command_time(0)); }, +            x400_regs::SPI_SLAVE_CFG, +            x400_regs::SPI_TRANSACTION_CFG_REG, +            x400_regs::SPI_TRANSACTION_GO_REG, +            x400_regs::SPI_STATUS_REG, +            gpio_port_mapper); + +        _spi_getter_iface = std::make_shared<x400_spi_getter>(spicore); +        register_feature(_spi_getter_iface);      }  } @@ -328,8 +348,8 @@ double x400_radio_control_impl::set_rate(const double rate)      // X400 does not support runtime rate changes      if (!uhd::math::frequencies_are_equal(rate, get_rate())) {          RFNOC_LOG_WARNING("Requesting invalid sampling rate from device: " -                          << (rate / 1e6) << " MHz. Actual rate is: " -                          << (get_rate() / 1e6) << " MHz."); +                          << (rate / 1e6) +                          << " MHz. Actual rate is: " << (get_rate() / 1e6) << " MHz.");      }      return get_rate();  } diff --git a/host/lib/usrp/x400/x400_radio_control.hpp b/host/lib/usrp/x400/x400_radio_control.hpp index 8848926c7..cd45291bc 100644 --- a/host/lib/usrp/x400/x400_radio_control.hpp +++ b/host/lib/usrp/x400/x400_radio_control.hpp @@ -7,24 +7,28 @@  #pragma once  #include "adc_self_calibration.hpp" +#include "x400_gpio_control.hpp" +#include <uhd/features/spi_getter_iface.hpp>  #include <uhd/rfnoc/noc_block_base.hpp>  #include <uhd/types/device_addr.hpp>  #include <uhd/types/direction.hpp>  #include <uhd/types/ranges.hpp>  #include <uhdlib/features/fpga_load_notification_iface.hpp>  #include <uhdlib/rfnoc/radio_control_impl.hpp> +#include <uhdlib/rfnoc/reg_iface_adapter.hpp>  #include <uhdlib/rfnoc/rf_control/dboard_iface.hpp>  #include <uhdlib/usrp/common/mpmd_mb_controller.hpp>  #include <uhdlib/usrp/common/rpc.hpp>  #include <uhdlib/usrp/common/x400_rfdc_control.hpp> +#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> +#include <uhdlib/usrp/cores/spi_core_4000.hpp>  #include <uhdlib/utils/rpc.hpp>  #include <stddef.h>  #include <memory>  #include <mutex>  #include <string>  #include <vector> -#include <uhdlib/usrp/cores/gpio_atr_3000.hpp> -#include "x400_gpio_control.hpp" +  namespace uhd { namespace rfnoc { @@ -34,6 +38,22 @@ namespace x400_regs {  // other things in the RFDC.  constexpr uint32_t RFDC_CTRL_BASE = radio_control_impl::regmap::PERIPH_BASE + 0x8000; +constexpr uint32_t DIO_REGMAP_OFFSET = 0x2000; +constexpr uint32_t DIO_WINDOW_OFFSET = 0xC000; + +// SPI control registers +constexpr uint32_t SPI_SLAVE_CFG = +    DIO_REGMAP_OFFSET + DIO_WINDOW_OFFSET + radio_control_impl::regmap::PERIPH_BASE; + +//! Base address for SPI_TRANSACTION_CONFIG Register +constexpr uint32_t SPI_TRANSACTION_CFG_REG = SPI_SLAVE_CFG + 0x0010; + +//! Base address for SPI_TRANSACTION_GO Register +constexpr uint32_t SPI_TRANSACTION_GO_REG = SPI_SLAVE_CFG + 0x0014; + +//! Base address for SPI_STATUS Register +constexpr uint32_t SPI_STATUS_REG = SPI_SLAVE_CFG + 0x0018; +  } // namespace x400_regs  class x400_radio_control_impl : public radio_control_impl @@ -116,8 +136,7 @@ public:          const std::string& name, const size_t chan) override;      void set_rx_lo_export_enabled(          bool enabled, const std::string& name, const size_t chan) override; -    bool get_rx_lo_export_enabled( -        const std::string& name, const size_t chan) override; +    bool get_rx_lo_export_enabled(const std::string& name, const size_t chan) override;      double set_rx_lo_freq(          double freq, const std::string& name, const size_t chan) override;      double get_rx_lo_freq(const std::string& name, const size_t chan) override; @@ -179,6 +198,8 @@ private:      uhd::features::adc_self_calibration_iface::sptr _adc_self_calibration; +    uhd::features::spi_getter_iface::sptr _spi_getter_iface; +      x400::gpio_control::sptr _gpios;      class fpga_onload : public uhd::features::fpga_load_notification_iface @@ -203,6 +224,26 @@ private:      };      fpga_onload::sptr _fpga_onload; + +    class x400_spi_getter : public uhd::features::spi_getter_iface +    { +    public: +        using sptr = std::shared_ptr<spi_getter_iface>; + +        x400_spi_getter(uhd::cores::spi_core_4000::sptr _spi) : _spicore(_spi) {} + +        uhd::spi_iface::sptr get_spi_ref( +            const std::vector<uhd::features::spi_slave_config_t>& spi_slave_config) +            const override +        { +            _spicore->set_spi_slave_config(spi_slave_config); +            return _spicore; +        } + +    private: +        uhd::cores::spi_core_4000::sptr _spicore; +    };  }; +  }} // namespace uhd::rfnoc diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 758d2bbf0..7857c00d5 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -387,6 +387,7 @@ IF(ENABLE_X400)          ${UHD_SOURCE_DIR}/lib/utils/compat_check.cpp          ${UHD_SOURCE_DIR}/lib/features/discoverable_feature_registry.cpp          ${UHD_SOURCE_DIR}/lib/usrp/cores/gpio_atr_3000.cpp +	${UHD_SOURCE_DIR}/lib/usrp/cores/spi_core_4000.cpp          $<TARGET_OBJECTS:uhd_rpclib>          INCLUDE_DIRS ${UHD_SOURCE_DIR}/lib/deps/rpclib/include          INCLUDE_DIRS ${UHD_SOURCE_DIR}/lib/deps/flatbuffers/include  | 
