aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/cores
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/cores')
-rw-r--r--host/lib/usrp/cores/CMakeLists.txt1
-rw-r--r--host/lib/usrp/cores/spi_core_4000.cpp175
2 files changed, 176 insertions, 0 deletions
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