diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-04-25 17:00:34 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:52 -0800 |
commit | 151ba5fb06dfdb6fcc46ccfdabf5f1e064236981 (patch) | |
tree | fa941b0589b09a22957e8b7e3966679748a9b202 /mpm/lib | |
parent | 1262dfb3ccf5a9916685b3399587593174c6583e (diff) | |
download | uhd-151ba5fb06dfdb6fcc46ccfdabf5f1e064236981.tar.gz uhd-151ba5fb06dfdb6fcc46ccfdabf5f1e064236981.tar.bz2 uhd-151ba5fb06dfdb6fcc46ccfdabf5f1e064236981.zip |
mpm: Major refactoring
- Created clean interfaces for SPI and registers
- Severed most links to UHD
- Added a lockable class which allows exposing mutexes into Python
Diffstat (limited to 'mpm/lib')
-rw-r--r-- | mpm/lib/CMakeLists.txt | 19 | ||||
-rw-r--r-- | mpm/lib/chips/CMakeLists.txt | 55 | ||||
-rw-r--r-- | mpm/lib/chips/lmk04828_spi_iface.cpp | 40 | ||||
-rw-r--r-- | mpm/lib/dboards/CMakeLists.txt | 26 | ||||
-rw-r--r-- | mpm/lib/dboards/magnesium_manager.cpp | 39 | ||||
-rw-r--r-- | mpm/lib/exception.cpp | 46 | ||||
-rw-r--r-- | mpm/lib/lmk04828/CMakeLists.txt | 49 | ||||
-rw-r--r-- | mpm/lib/lmk04828/lmk04828_spi_iface.cpp | 48 | ||||
-rw-r--r-- | mpm/lib/mykonos/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_ctrl.cpp | 30 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_device.cpp | 42 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_device.hpp | 9 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_spi_iface.cpp | 41 | ||||
-rw-r--r-- | mpm/lib/mykonos/adi_ctrl.cpp | 58 | ||||
-rw-r--r-- | mpm/lib/spi/CMakeLists.txt | 2 | ||||
-rw-r--r-- | mpm/lib/spi/spi_regs_iface.cpp | 100 | ||||
-rw-r--r-- | mpm/lib/spi/spidev.c | 116 | ||||
-rw-r--r-- | mpm/lib/spi/spidev.h | 56 | ||||
-rw-r--r-- | mpm/lib/spi/spidev_iface.cpp | 153 | ||||
-rw-r--r-- | mpm/lib/types/CMakeLists.txt | 5 | ||||
-rw-r--r-- | mpm/lib/types/lockable.cpp | 53 | ||||
-rw-r--r-- | mpm/lib/xbar_iface.cpp | 6 |
22 files changed, 704 insertions, 290 deletions
diff --git a/mpm/lib/CMakeLists.txt b/mpm/lib/CMakeLists.txt index cfdde2d03..57dae3be2 100644 --- a/mpm/lib/CMakeLists.txt +++ b/mpm/lib/CMakeLists.txt @@ -15,15 +15,18 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -######################################################################## -# This file included, use CMake directory variables -######################################################################## +SET(UHD_HOST_ROOT ${CMAKE_SOURCE_DIR}/../host) - -ADD_SUBDIRECTORY(spi) +ADD_SUBDIRECTORY(dboards) +ADD_SUBDIRECTORY(chips) ADD_SUBDIRECTORY(mykonos) -ADD_SUBDIRECTORY(lmk04828) +ADD_SUBDIRECTORY(spi) +ADD_SUBDIRECTORY(types) USRP_PERIPHS_ADD_OBJECT(periphs - xbar_iface.cpp - ) + exception.cpp + xbar_iface.cpp + ${UHD_HOST_ROOT}/lib/exception.cpp +) + +# vim: set sw=4 et: diff --git a/mpm/lib/chips/CMakeLists.txt b/mpm/lib/chips/CMakeLists.txt new file mode 100644 index 000000000..519ab1fe8 --- /dev/null +++ b/mpm/lib/chips/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +#MACRO(ETTUS_PYTHON_GEN_SOURCE pyfile outfile) + ##ensure that the directory exists for outfile + #GET_FILENAME_COMPONENT(outfile_dir ${outfile} PATH) + #FILE(MAKE_DIRECTORY ${outfile_dir}) + #IF(NOT PYTHON_EXECUTABLE) + #MESSAGE( FATAL_ERROR "No python executable found to generate ic_regmaps!" ) + #ENDIF(NOT PYTHON_EXECUTABLE) + ##make the outfile depend on the python script + #ADD_CUSTOM_COMMAND( + #OUTPUT ${outfile} DEPENDS ${pyfile} ${ETTUS_PYTHON_GEN_SOURCE_DEPS} + #COMMAND ${PYTHON_EXECUTABLE} -B ${pyfile} ${outfile} + #COMMENT "Generating ${outfile}" + #) + + ##make lmk04828 depend on the outfile + #LIST(APPEND lmk04828_srcs ${ARGV}) +#ENDMACRO(ETTUS_PYTHON_GEN_SOURCE) + +#################################################### +# LMK04828 +#################################################### + +# Register definitions need to be generated +#SET(UHD_HOST_ROOT ${CMAKE_SOURCE_DIR}/../host) +#MESSAGE("uhd host root: ${UHD_HOST_ROOT}") +#SET(UHD_IC_REG_MAP_PATH ${UHD_HOST_ROOT}/lib/ic_reg_maps) + +#SET(ETTUS_PYTHON_GEN_SOURCE_DEPS ${UHD_IC_REG_MAP_PATH}/common.py) +#ETTUS_PYTHON_GEN_SOURCE( + #${UHD_IC_REG_MAP_PATH}/gen_lmk04828_regs.py + #${CMAKE_CURRENT_BINARY_DIR}/lmk04828_regs.hpp +#) +#SET(LIBUHD_PYTHON_GEN_SOURCE_DEPS) + +# Define the object +USRP_PERIPHS_ADD_OBJECT(chips + lmk04828_spi_iface.cpp +) diff --git a/mpm/lib/chips/lmk04828_spi_iface.cpp b/mpm/lib/chips/lmk04828_spi_iface.cpp new file mode 100644 index 000000000..6f21a0f46 --- /dev/null +++ b/mpm/lib/chips/lmk04828_spi_iface.cpp @@ -0,0 +1,40 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <mpm/chips/lmk04828_spi_iface.hpp> +#include <mpm/spi/spi_regs_iface.hpp> + +using namespace mpm::spi; + +static const int LMK_SPI_SPEED_HZ = 1000000; +static const size_t LMK_ADDR_SHIFT = 8; +static const size_t LMK_DATA_SHIFT = 0; +static const size_t LMK_READ_FLAG = 1 << 23; +static const size_t LMK_WRITE_FLAG = 0; + +mpm::types::regs_iface::sptr mpm::chips::make_lmk04828_iface( + const std::string &spi_device +) { + return make_spi_regs_iface( + spi_iface::make_spidev(spi_device, LMK_SPI_SPEED_HZ), + LMK_ADDR_SHIFT, + LMK_DATA_SHIFT, + LMK_READ_FLAG, + LMK_WRITE_FLAG + ); +} + diff --git a/mpm/lib/dboards/CMakeLists.txt b/mpm/lib/dboards/CMakeLists.txt new file mode 100644 index 000000000..349be0b93 --- /dev/null +++ b/mpm/lib/dboards/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + + +USRP_PERIPHS_ADD_OBJECT(dboards + magnesium_manager.cpp +) + diff --git a/mpm/lib/dboards/magnesium_manager.cpp b/mpm/lib/dboards/magnesium_manager.cpp new file mode 100644 index 000000000..f66364aec --- /dev/null +++ b/mpm/lib/dboards/magnesium_manager.cpp @@ -0,0 +1,39 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <mpm/dboards/magnesium_manager.hpp> +#include <mpm/chips/lmk04828_spi_iface.hpp> +#include <mpm/ad937x/ad937x_spi_iface.hpp> + +using namespace mpm::dboards; +using namespace mpm::chips; + +magnesium_manager::magnesium_manager( + const std::string &lmk_spidev, + const std::string &mykonos_spidev +) : _spi_mutex(std::make_shared<std::mutex>()) + , _spi_lock(mpm::types::lockable::make(_spi_mutex)) + , _clock_ctrl(mpm::chips::make_lmk04828_iface(lmk_spidev)) + , _mykonos_ctrl(ad937x_ctrl::make( + _spi_mutex, + make_ad937x_iface(mykonos_spidev), + mpm::ad937x::gpio::gain_pins_t() + )) +{ + +} + diff --git a/mpm/lib/exception.cpp b/mpm/lib/exception.cpp new file mode 100644 index 000000000..6a6677a71 --- /dev/null +++ b/mpm/lib/exception.cpp @@ -0,0 +1,46 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <mpm/exception.hpp> +#include <boost/format.hpp> +#include <functional> + +using namespace mpm; + +exception::exception(const std::string &what): + std::runtime_error(what){/* NOP */} + +#define make_exception_impl(name, class, base) \ + class::class(const std::string &what): \ + base(str(boost::format("%s: %s") % name % what)){} \ + unsigned class::code(void) const{return std::hash<std::string>()(#class) & 0xfff;} \ + class *class::dynamic_clone(void) const{return new class(*this);} \ + void class::dynamic_throw(void) const{throw *this;} + +make_exception_impl("AssertionError", assertion_error, exception) +make_exception_impl("LookupError", lookup_error, exception) +make_exception_impl("IndexError", index_error, lookup_error) +make_exception_impl("KeyError", key_error, lookup_error) +make_exception_impl("TypeError", type_error, exception) +make_exception_impl("ValueError", value_error, exception) +make_exception_impl("RuntimeError", runtime_error, exception) +make_exception_impl("NotImplementedError", not_implemented_error, runtime_error) +make_exception_impl("EnvironmentError", environment_error, exception) +make_exception_impl("IOError", io_error, environment_error) +make_exception_impl("OSError", os_error, environment_error) +make_exception_impl("SystemError", system_error, exception) + diff --git a/mpm/lib/lmk04828/CMakeLists.txt b/mpm/lib/lmk04828/CMakeLists.txt deleted file mode 100644 index b3621034a..000000000 --- a/mpm/lib/lmk04828/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -MACRO(ETTUS_PYTHON_GEN_SOURCE pyfile outfile) - #ensure that the directory exists for outfile - GET_FILENAME_COMPONENT(outfile_dir ${outfile} PATH) - FILE(MAKE_DIRECTORY ${outfile_dir}) - IF(NOT PYTHON_EXECUTABLE) - MESSAGE( FATAL_ERROR "No python executable found to generate ic_regmaps!" ) - ENDIF(NOT PYTHON_EXECUTABLE) - #make the outfile depend on the python script - ADD_CUSTOM_COMMAND( - OUTPUT ${outfile} DEPENDS ${pyfile} ${ETTUS_PYTHON_GEN_SOURCE_DEPS} - COMMAND ${PYTHON_EXECUTABLE} -B ${pyfile} ${outfile} - COMMENT "Generating ${outfile}" - ) - - #make lmk04828 depend on the outfile - LIST(APPEND lmk04828_srcs ${ARGV}) -ENDMACRO(ETTUS_PYTHON_GEN_SOURCE) - -#################################################### -# LMK04828 -#################################################### - -# Register definitions need to be generated -SET(UHD_HOST_ROOT ${CMAKE_SOURCE_DIR}/../host) -MESSAGE("uhd host root: ${UHD_HOST_ROOT}") -SET(UHD_IC_REG_MAP_PATH ${UHD_HOST_ROOT}/lib/ic_reg_maps) - -SET(ETTUS_PYTHON_GEN_SOURCE_DEPS ${UHD_IC_REG_MAP_PATH}/common.py) -ETTUS_PYTHON_GEN_SOURCE( - ${UHD_IC_REG_MAP_PATH}/gen_lmk04828_regs.py - ${CMAKE_CURRENT_BINARY_DIR}/lmk04828_regs.hpp -) -SET(LIBUHD_PYTHON_GEN_SOURCE_DEPS) - - -# LMK04828 driver files -LIST(APPEND lmk04828_srcs - ${UHD_HOST_ROOT}/lib/usrp/common/lmk04828.cpp - ${UHD_HOST_ROOT}/lib/types/serial.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lmk04828_spi_iface.cpp - ) - -# Extra files needed -LIST(APPEND lmk04828_srcs ${UHD_HOST_ROOT}/lib/exception.cpp) - -# For include/uhd/exception.hpp and include/uhd/config.h - -USRP_PERIPHS_ADD_OBJECT(lmk04828 ${lmk04828_srcs}) -TARGET_INCLUDE_DIRECTORIES(lmk04828 PUBLIC ${UHD_HOST_ROOT}/include ${CMAKE_CURRENT_BINARY_DIR} ${UHD_HOST_ROOT}/lib/usrp/common) diff --git a/mpm/lib/lmk04828/lmk04828_spi_iface.cpp b/mpm/lib/lmk04828/lmk04828_spi_iface.cpp deleted file mode 100644 index 1251f4535..000000000 --- a/mpm/lib/lmk04828/lmk04828_spi_iface.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include <mpm/lmk04828/lmk04828_spi_iface.hpp> -#include <uhd/exception.hpp> -#include <boost/make_shared.hpp> -#include <functional> - -lmk04828_spi_iface::lmk04828_spi_iface(uhd::spi_iface::sptr iface) : _spi_iface(iface) - { - // Use default SPI Config options - config = uhd::spi_config_t(uhd::spi_config_t::EDGE_RISE); - } - -lmk04828_iface::write_fn_t lmk04828_spi_iface::get_write_fn() - { - return std::bind(&lmk04828_spi_iface::spi_write, this, std::placeholders::_1); - } - -lmk04828_iface::read_fn_t lmk04828_spi_iface::get_read_fn() - { - return std::bind(&lmk04828_spi_iface::spi_read, this, std::placeholders::_1); - } - -void lmk04828_spi_iface::spi_write(std::vector<uint32_t> writes) { - for (uint32_t write : writes) { - _spi_iface->write_spi(DEFAULT_SLAVE, config, write, LMK_SPI_NUM_BITS); - } - } - -uint8_t lmk04828_spi_iface::spi_read(uint32_t addr) { - // Format LMK SPI read transaction - // r/w[23] 0[22:21] addr[20:8] data[7:0] = 24 bits - uint32_t transaction = 0; - transaction |= LMK_SPI_READ_FLAG << LMK_SPI_READ_FLAG_OFFSET; - //transaction &= LMK_SPI_RESERVED_FIELD_MASK; - transaction |= addr << LMK_SPI_READ_ADDR_OFFSET; - - uint32_t data = _spi_iface->read_spi(DEFAULT_SLAVE, config, transaction, LMK_SPI_NUM_BITS); - - if ((data & 0xFFFFFF00) != 0) { - // There's more than 8 bits of data! - throw uhd::runtime_error("LMK SPI read returned too much data"); - } - - return data & 0xFF; - } - -lmk04828_spi_iface::sptr lmk04828_spi_iface::make(uhd::spi_iface::sptr iface){ - return boost::make_shared<lmk04828_spi_iface>(iface); -} diff --git a/mpm/lib/mykonos/CMakeLists.txt b/mpm/lib/mykonos/CMakeLists.txt index f5e64064e..b79474cea 100644 --- a/mpm/lib/mykonos/CMakeLists.txt +++ b/mpm/lib/mykonos/CMakeLists.txt @@ -28,6 +28,7 @@ ENDMACRO(MYKONOS_APPEND_SOURCES) SET(mykonos_sources ${CMAKE_CURRENT_SOURCE_DIR}/ad937x_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ad937x_device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ad937x_spi_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/adi_ctrl.cpp ${UHD_HOST_ROOT}/lib/types/ranges.cpp ) diff --git a/mpm/lib/mykonos/ad937x_ctrl.cpp b/mpm/lib/mykonos/ad937x_ctrl.cpp index d6360dad6..69ffeb6c2 100644 --- a/mpm/lib/mykonos/ad937x_ctrl.cpp +++ b/mpm/lib/mykonos/ad937x_ctrl.cpp @@ -18,12 +18,15 @@ #include "ad937x_device.hpp" #include "adi/mykonos.h" #include "mpm/ad937x/ad937x_ctrl.hpp" +#include <mpm/exception.hpp> #include <sstream> #include <set> #include <functional> #include <iostream> +#include <algorithm> +using namespace mpm::chips; using namespace mpm::ad937x::device; static uhd::direction_t _get_direction_from_antenna(const std::string& antenna) @@ -36,7 +39,7 @@ static uhd::direction_t _get_direction_from_antenna(const std::string& antenna) return uhd::direction_t::TX_DIRECTION; } else { - throw uhd::runtime_error("ad937x_ctrl got an invalid channel string."); + throw mpm::runtime_error("ad937x_ctrl got an invalid channel string."); } return uhd::direction_t::RX_DIRECTION; } @@ -51,7 +54,7 @@ static chain_t _get_chain_from_antenna(const std::string& antenna) return chain_t::TWO; } else { - throw uhd::runtime_error("ad937x_ctrl got an invalid channel string."); + throw mpm::runtime_error("ad937x_ctrl got an invalid channel string."); } return chain_t::ONE; } @@ -66,7 +69,7 @@ std::set<size_t> _get_valid_fir_lengths(const std::string& which) case uhd::direction_t::TX_DIRECTION: return{ 16, 32, 48, 64, 80, 96 }; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); return std::set<size_t>(); } } @@ -111,7 +114,7 @@ uhd::meta_range_t ad937x_ctrl::get_gain_range(const std::string &which) case uhd::direction_t::TX_DIRECTION: return uhd::meta_range_t(ad937x_device::MIN_TX_GAIN, ad937x_device::MAX_TX_GAIN, ad937x_device::TX_GAIN_STEP); default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); return uhd::meta_range_t(); } } @@ -121,7 +124,7 @@ class ad937x_ctrl_impl : public ad937x_ctrl public: ad937x_ctrl_impl( std::shared_ptr<std::mutex> spi_mutex, - uhd::spi_iface::sptr iface, + mpm::types::regs_iface::sptr iface, mpm::ad937x::gpio::gain_pins_t gain_pins) : spi_mutex(spi_mutex), device(iface.get(), gain_pins), @@ -251,7 +254,7 @@ public: auto dir = _get_direction_from_antenna(which); if (dir != uhd::direction_t::RX_DIRECTION) { - throw uhd::runtime_error("set_agc not valid for non-rx channels"); + throw mpm::runtime_error("set_agc not valid for non-rx channels"); } ad937x_device::gain_mode_t gain_mode; @@ -266,7 +269,7 @@ public: gain_mode = ad937x_device::gain_mode_t::HYBRID; } else { - throw uhd::runtime_error("invalid agc mode"); + throw mpm::runtime_error("invalid agc mode"); } std::lock_guard<std::mutex> lock(*spi_mutex); @@ -319,7 +322,7 @@ public: auto lengths = _get_valid_fir_lengths(which); if (std::find(lengths.begin(), lengths.end(), fir.size()) == lengths.end()) { - throw uhd::value_error("invalid filter length"); + throw mpm::value_error("invalid filter length"); } std::lock_guard<std::mutex> lock(*spi_mutex); @@ -370,7 +373,7 @@ public: // double comparison here should be okay because of clipping if (inc_step != dec_step) { - throw uhd::value_error("TX gain increment and decrement steps must be equal"); + throw mpm::value_error("TX gain increment and decrement steps must be equal"); } } @@ -381,11 +384,14 @@ public: private: ad937x_device device; std::shared_ptr<std::mutex> spi_mutex; - uhd::spi_iface::sptr _iface; + mpm::types::regs_iface::sptr _iface; }; -ad937x_ctrl::sptr ad937x_ctrl::make(std::shared_ptr<std::mutex> spi_mutex, uhd::spi_iface::sptr iface, mpm::ad937x::gpio::gain_pins_t gain_pins) -{ +ad937x_ctrl::sptr ad937x_ctrl::make( + std::shared_ptr<std::mutex> spi_mutex, + mpm::types::regs_iface::sptr iface, + mpm::ad937x::gpio::gain_pins_t gain_pins +) { return std::make_shared<ad937x_ctrl_impl>(spi_mutex, iface, gain_pins); } diff --git a/mpm/lib/mykonos/ad937x_device.cpp b/mpm/lib/mykonos/ad937x_device.cpp index 138e4e165..68065e67d 100644 --- a/mpm/lib/mykonos/ad937x_device.cpp +++ b/mpm/lib/mykonos/ad937x_device.cpp @@ -200,7 +200,7 @@ void ad937x_device::begin_initialization() uint8_t product_id = get_product_id(); if (product_id != AD9371_PRODUCT_ID) { - throw runtime_error(str( + throw mpm::runtime_error(str( boost::format("AD9371 product ID does not match expected ID! Read: %X Expected: %X") % int(product_id) % int(AD9371_PRODUCT_ID) )); @@ -208,7 +208,7 @@ void ad937x_device::begin_initialization() if (!get_pll_lock_status(pll_t::CLK_SYNTH)) { - throw runtime_error("AD937x CLK_SYNTH PLL failed to lock in initialize()"); + throw mpm::runtime_error("AD937x CLK_SYNTH PLL failed to lock in initialize()"); } uint8_t mcs_status = 0; @@ -223,7 +223,7 @@ void ad937x_device::finish_initialization() if ((mcs_status & 0x0A) != 0x0A) { - throw runtime_error("Multichip sync failed!"); + throw mpm::runtime_error("Multichip sync failed!"); } _call_api_function(std::bind(MYKONOS_initSubRegisterTables, mykonos_config.device)); @@ -289,18 +289,14 @@ void ad937x_device::enable_jesd_loopback(uint8_t enable) _call_api_function(std::bind(MYKONOS_setRxFramerDataSource, mykonos_config.device, enable)); } -ad937x_device::ad937x_device(spi_iface* iface, gain_pins_t gain_pins) : +ad937x_device::ad937x_device( + mpm::types::regs_iface* iface, + gain_pins_t gain_pins +) : full_spi_settings(iface), mykonos_config(&full_spi_settings.spi_settings), gain_ctrl(gain_pins) { - std::cout << "full spi settings addr " << &full_spi_settings << std::endl; - iface->read_spi(0, uhd::spi_config_t::EDGE_RISE, 400, 24); - std::cout << "adi spi settings addr " << &(full_spi_settings.spi_settings) << std::endl; - std::cout << "iface addr " << std::hex << iface << std::dec << std::endl; - - std::cout << "myk dev addr " << std::hex << mykonos_config.device->spiSettings << std::dec << std::endl; - } uint8_t ad937x_device::get_product_id() @@ -373,7 +369,7 @@ double ad937x_device::tune(direction_t direction, double value) mykonos_config.device->rx->rxPllLoFrequency_Hz = integer_value; break; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } _call_api_function(std::bind(MYKONOS_setRfPllFrequency, mykonos_config.device, pll, integer_value)); @@ -393,7 +389,7 @@ double ad937x_device::get_freq(direction_t direction) case TX_DIRECTION: pll = TX_PLL; break; case RX_DIRECTION: pll = RX_PLL; break; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } // TODO: coercion here causes extra device accesses, when the formula is provided on pg 119 of the user guide @@ -420,7 +416,7 @@ bool ad937x_device::get_pll_lock_status(pll_t pll) case pll_t::CALPLL_SDM: return (pll_status & 0x10) > 0; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); return false; } } @@ -470,7 +466,7 @@ double ad937x_device::set_gain(direction_t direction, chain_t chain, double valu func = MYKONOS_setTx2Attenuation; break; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } _call_api_function(std::bind(func, mykonos_config.device, attenuation)); break; @@ -490,13 +486,13 @@ double ad937x_device::set_gain(direction_t direction, chain_t chain, double valu func = MYKONOS_setRx2ManualGain; break; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } _call_api_function(std::bind(func, mykonos_config.device, gain)); break; } default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } return coerced_value; } @@ -518,10 +514,10 @@ void ad937x_device::set_agc_mode(direction_t direction, gain_mode_t mode) _call_api_function(std::bind(MYKONOS_setRxGainControlMode, mykonos_config.device, HYBRID)); break; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } } @@ -540,7 +536,7 @@ void ad937x_device::set_fir( mykonos_config.rx_fir_config.set_fir(gain, fir); break; default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } } @@ -556,7 +552,7 @@ std::vector<int16_t> ad937x_device::get_fir( case RX_DIRECTION: return mykonos_config.rx_fir_config.get_fir(gain); default: - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } } @@ -585,7 +581,7 @@ void ad937x_device::set_gain_pin_step_sizes(direction_t direction, chain_t chain gain_ctrl.config.at(direction).at(chain).dec_step = static_cast<uint8_t>(inc_step / 0.05); gain_ctrl.config.at(direction).at(chain).inc_step = static_cast<uint8_t>(dec_step / 0.05); } else { - UHD_THROW_INVALID_CODE_PATH(); + MPM_THROW_INVALID_CODE_PATH(); } _apply_gain_pins(direction, chain); } @@ -600,7 +596,7 @@ void ad937x_device::_apply_gain_pins(direction_t direction, chain_t chain) // TX direction does not support different steps per direction if (direction == TX_DIRECTION) { - UHD_ASSERT_THROW(chan.inc_step == chan.dec_step); + MPM_ASSERT_THROW(chan.inc_step == chan.dec_step); } switch (direction) diff --git a/mpm/lib/mykonos/ad937x_device.hpp b/mpm/lib/mykonos/ad937x_device.hpp index 729912adf..17a09f249 100644 --- a/mpm/lib/mykonos/ad937x_device.hpp +++ b/mpm/lib/mykonos/ad937x_device.hpp @@ -27,8 +27,8 @@ #include "adi/t_mykonos_gpio.h" #include "adi/mykonos_debug/t_mykonos_dbgjesd.h" -#include <uhd/exception.hpp> - +#include <mpm/spi/spi_iface.hpp> +#include <mpm/exception.hpp> #include <boost/noncopyable.hpp> #include <memory> #include <functional> @@ -39,7 +39,10 @@ public: enum class gain_mode_t { MANUAL, AUTOMATIC, HYBRID }; enum class pll_t {CLK_SYNTH, RX_SYNTH, TX_SYNTH, SNIFF_SYNTH, CALPLL_SDM}; - ad937x_device(uhd::spi_iface* iface, mpm::ad937x::gpio::gain_pins_t gain_pins); + ad937x_device( + mpm::types::regs_iface* iface, + mpm::ad937x::gpio::gain_pins_t gain_pins + ); void begin_initialization(); void finish_initialization(); diff --git a/mpm/lib/mykonos/ad937x_spi_iface.cpp b/mpm/lib/mykonos/ad937x_spi_iface.cpp new file mode 100644 index 000000000..66ea95d67 --- /dev/null +++ b/mpm/lib/mykonos/ad937x_spi_iface.cpp @@ -0,0 +1,41 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <mpm/ad937x/ad937x_spi_iface.hpp> +#include <mpm/spi/spi_regs_iface.hpp> + +using namespace mpm::spi; + +static const int MYK_SPI_SPEED_HZ = 1000000; +static const size_t MYK_ADDR_SHIFT = 8; +static const size_t MYK_DATA_SHIFT = 0; +static const size_t MYK_READ_FLAG = 1 << 23; +static const size_t MYK_WRITE_FLAG = 0; + +mpm::types::regs_iface::sptr mpm::chips::make_ad937x_iface( + const std::string &spi_device +) { + return make_spi_regs_iface( + spi_iface::make_spidev(spi_device, MYK_SPI_SPEED_HZ), + MYK_ADDR_SHIFT, + MYK_DATA_SHIFT, + MYK_READ_FLAG, + MYK_WRITE_FLAG + ); +} + + diff --git a/mpm/lib/mykonos/adi_ctrl.cpp b/mpm/lib/mykonos/adi_ctrl.cpp index 9a5f73607..be3fa0ddb 100644 --- a/mpm/lib/mykonos/adi_ctrl.cpp +++ b/mpm/lib/mykonos/adi_ctrl.cpp @@ -24,10 +24,10 @@ #include <chrono> #include <thread> -static const uint32_t MYKONOS_READ_BIT = (1 << 23); - -ad9371_spiSettings_t::ad9371_spiSettings_t(uhd::spi_iface* uhd_iface) : - spi_iface(uhd_iface) +ad9371_spiSettings_t::ad9371_spiSettings_t( + mpm::types::regs_iface* spi_iface_ +) : + spi_iface(spi_iface_) { spi_settings.chipSelectIndex = 0; // set later spi_settings.writeBitPolarity = 1; // unused @@ -41,11 +41,6 @@ ad9371_spiSettings_t::ad9371_spiSettings_t(uhd::spi_iface* uhd_iface) : spi_settings.spiClkFreq_Hz = 250000000; // currently unused } -uhd::spi_config_t::edge_t _get_edge(const spiSettings_t & sps) -{ - return (sps.CPOL ^ sps.CPHA) ? uhd::spi_config_t::EDGE_FALL : uhd::spi_config_t::EDGE_RISE; -} - // TODO: change // not implemented to meaningful errors // close hardware pointers @@ -90,14 +85,14 @@ commonErr_t CMB_setSPIChannel(uint16_t chipSelectIndex) // single SPI byte write function commonErr_t CMB_SPIWriteByte(spiSettings_t *spiSettings, uint16_t addr, uint8_t data) { - // TODO: crash and burn for these errors? - if (spiSettings == nullptr || spiSettings->MSBFirst == 0) return COMMONERR_FAILED; + if (spiSettings == nullptr || spiSettings->MSBFirst == 0) { + // TODO: crash and burn for these errors? + return COMMONERR_FAILED; + } - ad9371_spiSettings_t *mpm_spi = ad9371_spiSettings_t::make(spiSettings); - uhd::spi_config_t config(_get_edge(*spiSettings)); - uint32_t data_word = (0) | (addr << 8) | (data); + ad9371_spiSettings_t *spi = ad9371_spiSettings_t::make(spiSettings); try { - mpm_spi->spi_iface->write_spi(spiSettings->chipSelectIndex, config, data_word, 24); + spi->spi_iface->poke8(addr, data); return COMMONERR_OK; } catch (const std::exception &e) { std::cout << "AAAAAAAAAAAAH" << std::endl; @@ -107,22 +102,22 @@ commonErr_t CMB_SPIWriteByte(spiSettings_t *spiSettings, uint16_t addr, uint8_t commonErr_t CMB_SPIWriteBytes(spiSettings_t *spiSettings, uint16_t *addr, uint8_t *data, uint32_t count) { - // TODO: crash and burn for these errors? if (spiSettings == nullptr || addr == nullptr || data == nullptr || spiSettings->MSBFirst == 0) { + // TODO: crash and burn for these errors? return COMMONERR_FAILED; } - ad9371_spiSettings_t *mpm_spi = ad9371_spiSettings_t::make(spiSettings); - uhd::spi_config_t config(_get_edge(*spiSettings)); + ad9371_spiSettings_t *spi = ad9371_spiSettings_t::make(spiSettings); try { for (size_t i = 0; i < count; ++i) { uint32_t data_word = (0) | (addr[i] << 8) | (data[i]); - mpm_spi->spi_iface->write_spi(spiSettings->chipSelectIndex, config, data_word, 24); + + spi->spi_iface->poke8(addr[i], data[i]); } return COMMONERR_OK; } catch (const std::exception &e) { @@ -141,13 +136,9 @@ commonErr_t CMB_SPIReadByte (spiSettings_t *spiSettings, uint16_t addr, uint8_t return COMMONERR_FAILED; } - ad9371_spiSettings_t *mpm_spi = ad9371_spiSettings_t::make(spiSettings); - uhd::spi_config_t config(_get_edge(*spiSettings)); - uint32_t read_word = MYKONOS_READ_BIT | (addr << 8); - + ad9371_spiSettings_t *spi = ad9371_spiSettings_t::make(spiSettings); try { - *readdata = static_cast<uint8_t>( - mpm_spi->spi_iface->read_spi(spiSettings->chipSelectIndex, config, read_word, 24)); + *readdata = spi->spi_iface->peek8(addr); return COMMONERR_OK; } catch (const std::exception &e) { std::cout << "AAAAAAAAAAAAH READ" << std::endl; @@ -162,15 +153,12 @@ commonErr_t CMB_SPIWriteField( uint16_t addr, uint8_t field_val, uint8_t mask, uint8_t start_bit ) { - ad9371_spiSettings_t *mpm_spi = ad9371_spiSettings_t::make(spiSettings); - uhd::spi_config_t config(_get_edge(*spiSettings)); - uint32_t read_word = (0) | (addr << 8); + ad9371_spiSettings_t *spi = ad9371_spiSettings_t::make(spiSettings); try { - uint32_t current_value = mpm_spi->spi_iface->read_spi(spiSettings->chipSelectIndex, config, read_word, 24); - uint8_t new_value = static_cast<uint8_t>((current_value & ~mask) | (field_val << start_bit)); - uint32_t write_word = (0) | (addr << 8) | new_value; - mpm_spi->spi_iface->write_spi(spiSettings->chipSelectIndex, config, write_word, 24); + uint8_t current_value = spi->spi_iface->peek8(addr); + uint8_t new_value = ((current_value & ~mask) | (field_val << start_bit)); + spi->spi_iface->poke8(addr, new_value); return COMMONERR_OK; } catch (const std::exception &e) { std::cout << "AAAAAAAAAAAAH WRITE FIELD" << std::endl; @@ -186,12 +174,10 @@ commonErr_t CMB_SPIReadField( uint16_t addr, uint8_t *field_val, uint8_t mask, uint8_t start_bit ) { - ad9371_spiSettings_t *mpm_spi = ad9371_spiSettings_t::make(spiSettings); - uhd::spi_config_t config(_get_edge(*spiSettings)); - uint32_t read_word = MYKONOS_READ_BIT | (addr << 8); + ad9371_spiSettings_t *spi = ad9371_spiSettings_t::make(spiSettings); try { - uint32_t value = mpm_spi->spi_iface->read_spi(spiSettings->chipSelectIndex, config, read_word, 24); + uint8_t value = spi->spi_iface->peek8(addr); *field_val = static_cast<uint8_t>((value & mask) >> start_bit); return COMMONERR_OK; } catch (const std::exception &e) { diff --git a/mpm/lib/spi/CMakeLists.txt b/mpm/lib/spi/CMakeLists.txt index 517e88561..d9c704a6a 100644 --- a/mpm/lib/spi/CMakeLists.txt +++ b/mpm/lib/spi/CMakeLists.txt @@ -1,5 +1,7 @@ SET(SPI_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/spidev_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spi_regs_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spidev.c ) USRP_PERIPHS_ADD_OBJECT(spi ${SPI_SOURCES}) diff --git a/mpm/lib/spi/spi_regs_iface.cpp b/mpm/lib/spi/spi_regs_iface.cpp new file mode 100644 index 000000000..eb6e229f9 --- /dev/null +++ b/mpm/lib/spi/spi_regs_iface.cpp @@ -0,0 +1,100 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <mpm/types/regs_iface.hpp> +#include <mpm/spi/spi_iface.hpp> +#include <mpm/spi/spi_regs_iface.hpp> +#include <mpm/exception.hpp> + +using mpm::types::regs_iface; + +/*! SPI implementation of the regs iface + * + * Uses spidev + */ +class spi_regs_iface_impl : public regs_iface +{ +public: + + spi_regs_iface_impl( + mpm::spi::spi_iface::sptr spi_iface, + uint32_t addr_shift, + uint32_t data_shift, + uint32_t read_flags, + uint32_t write_flags = 0 + ) : _spi_iface(spi_iface), + _addr_shift(addr_shift), + _data_shift(data_shift), + _read_flags(read_flags), + _write_flags(write_flags) + { + /* nop */ + } + + uint8_t peek8( + const uint32_t addr + ) { + uint32_t transaction = 0 + | (addr << _addr_shift) + | _read_flags + ; + + uint32_t data = _spi_iface->transfer24_8(transaction); + if ((data & 0xFFFFFF00) != 0) { + throw mpm::runtime_error("SPI read returned too much data"); + } + + return uint8_t(data & 0xFF); + } + + void poke8( + const uint32_t addr, + const uint8_t data + ) { + uint32_t transaction = 0 + | _write_flags + | (addr << _addr_shift) + | (data << _data_shift) + ; + + _spi_iface->transfer24_8(transaction); + } + +private: + mpm::spi::spi_iface::sptr _spi_iface; + + uint32_t _addr_shift; + uint32_t _data_shift; + uint32_t _read_flags; + uint32_t _write_flags; +}; + +regs_iface::sptr mpm::spi::make_spi_regs_iface( + mpm::spi::spi_iface::sptr spi_iface, + uint32_t addr_shift, + uint32_t data_shift, + uint32_t read_flags, + uint32_t write_flags +) { + return std::make_shared<spi_regs_iface_impl>( + spi_iface, + addr_shift, + data_shift, + read_flags, + write_flags + ); +} diff --git a/mpm/lib/spi/spidev.c b/mpm/lib/spi/spidev.c new file mode 100644 index 000000000..929b61aa9 --- /dev/null +++ b/mpm/lib/spi/spidev.c @@ -0,0 +1,116 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "spidev.h" +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/types.h> +#include <linux/spi/spidev.h> +#include <stdio.h> + +int init_spi(int *fd, const char *device, + const uint32_t mode, + const uint32_t speed_hz, + const uint8_t bits_per_word, + const uint16_t delay_us +) { + int err; + + *fd = open(device, O_RDWR); + if (*fd < 0) { + fprintf(stderr, "%s: Failed to open device\n", __func__); + return err; + } + + uint32_t requested_mode = mode; + err = ioctl(*fd, SPI_IOC_WR_MODE32, &mode); + if (err < 0) { + fprintf(stderr, "%s: Failed to set mode\n", __func__); + return err;; + } + + err = ioctl(*fd, SPI_IOC_RD_MODE32, &mode); + if (err < 0) { + fprintf(stderr, "%s: Failed to get mode\n", __func__); + return err; + } + if (requested_mode != mode) { + return 2; + } + + uint8_t requested_bits_per_word; + err = ioctl(*fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word); + if (err < 0) { + fprintf(stderr, "%s: Failed to set bits per word\n", __func__); + return err; + } + err = ioctl(*fd, SPI_IOC_RD_BITS_PER_WORD, &bits_per_word); + if (err) { + fprintf(stderr, "%s: Failed to get bits per word\n", __func__); + return err; + } + if (requested_bits_per_word != bits_per_word) { + return 2; + } + + uint32_t requested_speed_hz = speed_hz; + err = ioctl(*fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz); + if (err < 0) { + fprintf(stderr, "%s: Failed to set speed\n", __func__); + return err; + } + err = ioctl(*fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed_hz); + if (err < 0) { + fprintf(stderr, "%s: Failed to get speed\n", __func__); + return err; + } + if (requested_speed_hz != speed_hz) { + return 2; + } + + return 0; +} + +int transfer( + int fd, + uint8_t *tx, uint8_t *rx, uint32_t len, + uint32_t speed_hz, uint8_t bits_per_word, uint16_t delay_us +) { + int err; + + struct spi_ioc_transfer tr = { + .tx_buf = (unsigned long) tx, + .rx_buf = (unsigned long) rx, + .len = len, + .speed_hz = speed_hz, + .delay_usecs = delay_us, + .bits_per_word = bits_per_word, + .cs_change = 0, + .tx_nbits = 1, // Standard SPI + .rx_nbits = 1 // Standard SPI + }; + + err = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); + if (err < 0) { + fprintf(stderr, "%s: Failed ioctl: %d\n", __func__, err); + perror("ioctl: \n"); + return err; + } + + return 0; +} + diff --git a/mpm/lib/spi/spidev.h b/mpm/lib/spi/spidev.h new file mode 100644 index 000000000..30ea9ecc5 --- /dev/null +++ b/mpm/lib/spi/spidev.h @@ -0,0 +1,56 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <stdint.h> + +/*! Initialize a spidev interface + * + * \param fd Return value of the file descriptor + * \param device Path of spidev device, e.g. "/dev/spidev0.0" + * \param mode SPI mode. See linux/spi/spidev.h + * \param speed_hz The *maximum* SPI speed in Hz + * \param bits_per_word Just set it to 8. + * \param delay_us Delay between writes in microseconds + * + * \returns 0 if all is good, or an error code otherwise + */ +int init_spi(int *fd, const char *device, + const uint32_t mode, + const uint32_t speed_hz, + const uint8_t bits_per_word, + const uint16_t delay_us +); + +/*! Do a SPI transaction over spidev + * + * \param tx Buffer of data to be written + * \param rx Must match tx buffer length; result will be written here + * \param len Total number of bytes in this transaction + * \param speed_hz Speed of this transaction in Hz + * \param bits_per_word 8, dude + * \param delay_us Delay between transfers + * + * Assumption: spidev was configured properly beforehand. + * + * \returns 0 if all is golden + */ +int transfer( + int fd, + uint8_t *tx, uint8_t *rx, uint32_t len, + uint32_t speed_hz, uint8_t bits_per_word, uint16_t delay_us +); + diff --git a/mpm/lib/spi/spidev_iface.cpp b/mpm/lib/spi/spidev_iface.cpp index 919cf338e..fe37f16d5 100644 --- a/mpm/lib/spi/spidev_iface.cpp +++ b/mpm/lib/spi/spidev_iface.cpp @@ -15,14 +15,15 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "mpm/spi/spidev_iface.hpp" -#include <fcntl.h> -#include <sys/ioctl.h> -#include <linux/types.h> +#include <mpm/spi/spi_iface.hpp> +#include <mpm/exception.hpp> +extern "C" { +#include "spidev.h" +} #include <linux/spi/spidev.h> -#include <boost/format.hpp> +#include <boost/format.hpp> #include <iostream> using namespace mpm::spi; @@ -30,68 +31,29 @@ using namespace mpm::spi; /****************************************************************************** * Implementation *****************************************************************************/ -class spidev_iface_impl : public spidev_iface +class spidev_iface_impl : public spi_iface { public: spidev_iface_impl( - const std::string &device - ) { - int ret; - - _fd = open(device.c_str(), O_RDWR); - if (_fd < 0) { - throw std::runtime_error(str( - boost::format("Could not open spidev device %s") - % device - )); - } - - int MODE = 3; - ret = ioctl(_fd, SPI_IOC_WR_MODE32, &MODE); - if (ret == -1) { - throw std::runtime_error(str( - boost::format("Could not set spidev mode to %X for spidev %s") - % uint16_t(_mode) % device - )); - } - - ret = ioctl(_fd, SPI_IOC_RD_MODE32, &_mode); - if (ret == -1) { - throw std::runtime_error(str( - boost::format("Could not get spidev mode for spidev %s") - % device - )); - } - - ret = ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits); - if (ret == -1) { - throw std::runtime_error(str( - boost::format("Could not set spidev bits per word to %d for spidev %s") - % uint16_t(_bits) % device - )); - } + const std::string &device, + const int max_speed_hz + ) : _speed(max_speed_hz) + { - ret = ioctl(_fd, SPI_IOC_RD_BITS_PER_WORD, &_bits); - if (ret == -1) { - throw std::runtime_error(str( - boost::format("Could not get spidev bits per word for spidev %s") + if (!init_spi( + &_fd, + device.c_str(), + _mode, _speed, _bits, _delay + )) { + throw mpm::runtime_error(str( + boost::format("Could not initialize spidev device %s") % device )); } - - ret = ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_speed); - if (ret == -1) { - throw std::runtime_error(str( - boost::format("Could not set spidev max speed to %d for spidev %s") - % _speed % device - )); - } - - ret = ioctl(_fd, SPI_IOC_RD_MAX_SPEED_HZ, &_speed); - if (ret == -1) { - throw std::runtime_error(str( - boost::format("Could not get spidev max speed for spidev %s") + if (_fd < 0) { + throw mpm::runtime_error(str( + boost::format("Could not open spidev device %s") % device )); } @@ -102,60 +64,32 @@ public: close(_fd); } - uint32_t transact_spi( - int /* which_slave */, - const uhd::spi_config_t & /* config */, - uint32_t data, - size_t num_bits, - bool readback + uint32_t transfer24_8( + const uint32_t data_ ) { int ret(0); + uint32_t data = data_; uint8_t *tx_data = reinterpret_cast<uint8_t *>(&data); - assert(num_bits == 24); - uint8_t tx[] = {tx_data[2], tx_data[1], tx_data[0]}; - + // Create tx and rx buffers: + uint8_t tx[] = {tx_data[2], tx_data[1], tx_data[0]}; // FIXME guarantee endianness uint8_t rx[3]; // Buffer length must match tx buffer - struct spi_ioc_transfer tr; - tr.tx_buf = (unsigned long) &tx[0]; - tr.rx_buf = (unsigned long) &rx[0]; - tr.len = num_bits >> 3; - tr.speed_hz = _speed; - tr.delay_usecs = _delay; - tr.bits_per_word = _bits; - tr.cs_change = 0; - tr.tx_nbits = 1; // Standard SPI - tr.rx_nbits = 1; // Standard SPI - - ret = ioctl(_fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - throw std::runtime_error("Could not send spidev message"); - - return rx[2]; // Assumes that only a single byte is being read. - } - - uint32_t read_spi( - int which_slave, - const uhd::spi_config_t &config, - uint32_t data, - size_t num_bits - ) { - return transact_spi( - which_slave, config, data, num_bits, true - ); - } + if (transfer( + _fd, + &tx[0], &rx[0], + 3, + _speed, _bits, _delay + ) != 0) { + throw mpm::runtime_error(str( + boost::format("SPI Transaction failed!") + )); + } - void write_spi( - int which_slave, - const uhd::spi_config_t &config, - uint32_t data, - size_t num_bits - ) { - transact_spi( - which_slave, config, data, num_bits, false - ); + // Assumes that only a single byte is being read. + // TODO the function does not advertise this. Should probably fix. + return uint32_t(rx[2]); } private: @@ -169,9 +103,12 @@ private: /****************************************************************************** * Factory *****************************************************************************/ -spidev_iface::sptr spidev_iface::make( - const std::string &device +spi_iface::sptr spi_iface::make_spidev( + const std::string &device, + const int speed_hz ) { - return sptr(new spidev_iface_impl(device)); + return std::make_shared<spidev_iface_impl>( + device, speed_hz + ); } diff --git a/mpm/lib/types/CMakeLists.txt b/mpm/lib/types/CMakeLists.txt new file mode 100644 index 000000000..d4f52f7ed --- /dev/null +++ b/mpm/lib/types/CMakeLists.txt @@ -0,0 +1,5 @@ +SET(TYPES_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/lockable.cpp +) + +USRP_PERIPHS_ADD_OBJECT(types ${TYPES_SOURCES}) diff --git a/mpm/lib/types/lockable.cpp b/mpm/lib/types/lockable.cpp new file mode 100644 index 000000000..75c3aa784 --- /dev/null +++ b/mpm/lib/types/lockable.cpp @@ -0,0 +1,53 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <mpm/types/lockable.hpp> + +using namespace mpm::types; + +class lockable_impl : public lockable +{ +public: + lockable_impl( + std::shared_ptr<std::mutex> spi_mutex + ) : _spi_mutex(spi_mutex) + { + /* nop */ + } + + void lock() + { + _spi_mutex->lock(); + } + + void unlock() + { + _spi_mutex->unlock(); + } + +private: + std::shared_ptr<std::mutex> _spi_mutex; +}; + +lockable::sptr lockable::make( + std::shared_ptr<std::mutex> spi_mutex +) { + return std::make_shared<lockable_impl>( + spi_mutex + ); +} + diff --git a/mpm/lib/xbar_iface.cpp b/mpm/lib/xbar_iface.cpp index 8bd89533c..4a86d9ba6 100644 --- a/mpm/lib/xbar_iface.cpp +++ b/mpm/lib/xbar_iface.cpp @@ -16,7 +16,7 @@ // #include "mpm/xbar_iface.hpp" -#include <uhd/exception.hpp> +#include <mpm/exception.hpp> #include <boost/format.hpp> #include <sys/ioctl.h> #include <fcntl.h> @@ -38,7 +38,7 @@ void xbar_iface::set_route(uint8_t dst_addr, uint8_t dst_port) { rfnoc_crossbar_cmd cmd = {.dest_addr = dst_addr, .dest_port = dst_port}; int err = ioctl(_fd, RFNCBWROUTIOC, &cmd); if (err < 0) { - throw uhd::os_error(str(boost::format("setting crossbar route failed! Error: %d") % err)); + throw mpm::os_error(str(boost::format("setting crossbar route failed! Error: %d") % err)); } } @@ -47,7 +47,7 @@ void xbar_iface::del_route(uint8_t dst_addr, uint8_t dst_port){ rfnoc_crossbar_cmd cmd = {.dest_addr = dst_addr, .dest_port = dst_port}; int err = ioctl(_fd, RFNCDELROUTIOC, &cmd); if (err < 0){ - throw uhd::os_error(str(boost::format("deleting crossbar route failed! Error: %d") % err)); + throw mpm::os_error(str(boost::format("deleting crossbar route failed! Error: %d") % err)); } } |