diff options
author | Toni Jones <toni.jones@ni.com> | 2019-08-26 13:09:04 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-03-04 12:09:52 -0600 |
commit | 136214240e4275df4d540f058ece2194cec1c7b5 (patch) | |
tree | 645e8c12dc5924b6ee9ecb45da36be98aa727551 | |
parent | 1a9033b3e96654dcd374f9d2effe5751b36130f2 (diff) | |
download | uhd-136214240e4275df4d540f058ece2194cec1c7b5.tar.gz uhd-136214240e4275df4d540f058ece2194cec1c7b5.tar.bz2 uhd-136214240e4275df4d540f058ece2194cec1c7b5.zip |
mpm: Implement 32 bit register interface with SPI
Implement SPI transfers which are 12 bytes in length to support
access for 32 bit register interfaces. 12 byte transactions are
necessary for Titanium MB PS CPLD SPI transactions. This implementation
supports 48 bits of TX data per transfer and offsets all flags and
data shifts from the end of the TX data portion of the transfer buffer
rather than the end of the entire transfer buffer.
-rw-r--r-- | mpm/include/mpm/spi/spi_iface.hpp | 8 | ||||
-rw-r--r-- | mpm/include/mpm/spi/spi_python.hpp | 3 | ||||
-rw-r--r-- | mpm/include/mpm/spi/spi_regs_iface.hpp | 8 | ||||
-rw-r--r-- | mpm/include/mpm/types/regs_iface.hpp | 8 | ||||
-rw-r--r-- | mpm/include/mpm/types/types_python.hpp | 4 | ||||
-rw-r--r-- | mpm/lib/i2c/i2c_regs_iface.cpp | 12 | ||||
-rw-r--r-- | mpm/lib/spi/spi_regs_iface.cpp | 59 | ||||
-rw-r--r-- | mpm/lib/spi/spidev_iface.cpp | 32 |
8 files changed, 120 insertions, 14 deletions
diff --git a/mpm/include/mpm/spi/spi_iface.hpp b/mpm/include/mpm/spi/spi_iface.hpp index e295d1bb7..39390f7e2 100644 --- a/mpm/include/mpm/spi/spi_iface.hpp +++ b/mpm/include/mpm/spi/spi_iface.hpp @@ -36,6 +36,14 @@ public: */ virtual uint32_t transfer24_16(const uint32_t data) = 0; + /*! Convenience function: SPI xfer is 64 bits write, 40 bits read. + * + * \param data The write data for this xfer + * + * \return 40 bits worth of the return xfer + */ + virtual uint64_t transfer64_40(const uint64_t data) = 0; + /*! * \param device The path to the spidev used (e.g. "/dev/spidev0.0") * \param speed_hz Transaction speed in Hz diff --git a/mpm/include/mpm/spi/spi_python.hpp b/mpm/include/mpm/spi/spi_python.hpp index 464df8d8b..0cadcad53 100644 --- a/mpm/include/mpm/spi/spi_python.hpp +++ b/mpm/include/mpm/spi/spi_python.hpp @@ -18,5 +18,6 @@ void export_spi(py::module& top_module) m.def("make_spidev", &mpm::spi::spi_iface::make_spidev); py::class_<mpm::spi::spi_iface, std::shared_ptr<mpm::spi::spi_iface>>(m, "spi_iface") - .def("transfer24_8", &mpm::spi::spi_iface::transfer24_8); + .def("transfer24_8", &mpm::spi::spi_iface::transfer24_8) + .def("transfer64_40", &mpm::spi::spi_iface::transfer64_40); } diff --git a/mpm/include/mpm/spi/spi_regs_iface.hpp b/mpm/include/mpm/spi/spi_regs_iface.hpp index 6351cf9b5..c731946fe 100644 --- a/mpm/include/mpm/spi/spi_regs_iface.hpp +++ b/mpm/include/mpm/spi/spi_regs_iface.hpp @@ -14,8 +14,8 @@ namespace mpm { namespace spi { mpm::types::regs_iface::sptr 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 = 0); + uint64_t read_flags, + uint64_t write_flags = 0); /*! Convenience factory for regs_iface based on SPI based on spidev */ @@ -24,7 +24,7 @@ mpm::types::regs_iface::sptr make_spidev_regs_iface(const std::string& device, const int spi_mode, uint32_t addr_shift, uint32_t data_shift, - uint32_t read_flags, - uint32_t write_flags = 0); + uint64_t read_flags, + uint64_t write_flags = 0); }}; /* namespace mpm::spi */ diff --git a/mpm/include/mpm/types/regs_iface.hpp b/mpm/include/mpm/types/regs_iface.hpp index b47c31821..97fdd232c 100644 --- a/mpm/include/mpm/types/regs_iface.hpp +++ b/mpm/include/mpm/types/regs_iface.hpp @@ -33,6 +33,14 @@ public: /*! Write a 16-bit value to a given address */ virtual void poke16(const uint32_t addr, const uint16_t data) = 0; + + /*! Return a 32-bit value from a given address + */ + virtual uint32_t peek32(const uint64_t addr) = 0; + + /*! Write a 32-bit value to a given address + */ + virtual void poke32(const uint64_t addr, const uint32_t data) = 0; }; }}; // namespace mpm::types diff --git a/mpm/include/mpm/types/types_python.hpp b/mpm/include/mpm/types/types_python.hpp index cb2af5c4b..1c4c1c3b5 100644 --- a/mpm/include/mpm/types/types_python.hpp +++ b/mpm/include/mpm/types/types_python.hpp @@ -25,7 +25,9 @@ void export_types(py::module& top_module) .def("peek8", ®s_iface::peek8) .def("poke8", ®s_iface::poke8) .def("peek16", ®s_iface::peek16) - .def("poke16", ®s_iface::poke16); + .def("poke16", ®s_iface::poke16) + .def("peek32", ®s_iface::peek32) + .def("poke32", ®s_iface::poke32); py::class_<log_buf, std::shared_ptr<log_buf>>(m, "log_buf") .def_static("make_singleton", &log_buf::make_singleton) diff --git a/mpm/lib/i2c/i2c_regs_iface.cpp b/mpm/lib/i2c/i2c_regs_iface.cpp index 575e19d26..0fd5e716c 100644 --- a/mpm/lib/i2c/i2c_regs_iface.cpp +++ b/mpm/lib/i2c/i2c_regs_iface.cpp @@ -93,6 +93,18 @@ public: } } + uint32_t peek32(const uint64_t addr) + { + throw mpm::not_implemented_error( + "I2C regs iface does not implement 32 bit transactions."); + } + + void poke32(const uint64_t addr, const uint32_t data) + { + throw mpm::not_implemented_error( + "I2C regs iface does not implement 32 bit transactions."); + } + private: mpm::i2c::i2c_iface::sptr _i2c_iface; diff --git a/mpm/lib/spi/spi_regs_iface.cpp b/mpm/lib/spi/spi_regs_iface.cpp index 842f45d42..d4776d859 100644 --- a/mpm/lib/spi/spi_regs_iface.cpp +++ b/mpm/lib/spi/spi_regs_iface.cpp @@ -21,8 +21,8 @@ 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) + uint64_t read_flags, + uint64_t write_flags = 0) : _spi_iface(spi_iface) , _addr_shift(addr_shift) , _data_shift(data_shift) @@ -72,20 +72,63 @@ public: _spi_iface->transfer24_16(transaction); } + uint32_t peek32(const uint64_t addr) + { + /* Note: _addr_shift and _read_flags will be offset from the + * TX portion of the message (first 2 bytes) rather than the + * end of the message as is done with smaller transfers. + */ + uint64_t transaction = 0 | (addr << _addr_shift) | _read_flags; + + uint64_t data = _spi_iface->transfer64_40(transaction); + + // Actual RX data is the last 5 bytes + if ((data & 0xFFFFFF00000000F8) != 0) { + throw mpm::runtime_error("SPI read returned too much data"); + } + + // Status data is the last byte + bool ack = (data >> 2) & 0x1; + uint8_t status = data & 0x3; + if (!ack) { + throw mpm::runtime_error("Ctrlport SPI read had no ACK"); + } + if (status != 0) { + // TODO: Differentiate error codes + throw mpm::runtime_error("Ctrlport SPI error"); + } + + // Register data is the 4 bytes above the last one + data = (data >> 8) & 0xFFFFFFFF; + return data; + } + + void poke32(const uint64_t addr, const uint32_t data) + { + /* Note: _addr_shift and _write_flags will be offset from the + * TX portion of the message (first 6 bytes) rather than the + * end of the message as is done with smaller transfers. + */ + uint64_t transaction = 0 | _write_flags | (addr << _addr_shift) + | (data << _data_shift); + + _spi_iface->transfer64_40(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; + uint64_t _read_flags; + uint64_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) + uint64_t read_flags, + uint64_t write_flags) { return std::make_shared<spi_regs_iface_impl>( spi_iface, addr_shift, data_shift, read_flags, write_flags); @@ -96,8 +139,8 @@ mpm::types::regs_iface::sptr mpm::spi::make_spidev_regs_iface(const std::string& const int spi_mode, uint32_t addr_shift, uint32_t data_shift, - uint32_t read_flags, - uint32_t write_flags) + uint64_t read_flags, + uint64_t write_flags) { auto spi_iface_sptr = mpm::spi::spi_iface::make_spidev(device, speed_hz, spi_mode); return std::make_shared<spi_regs_iface_impl>( diff --git a/mpm/lib/spi/spidev_iface.cpp b/mpm/lib/spi/spidev_iface.cpp index c8a2133e8..c706725b8 100644 --- a/mpm/lib/spi/spidev_iface.cpp +++ b/mpm/lib/spi/spidev_iface.cpp @@ -79,6 +79,38 @@ public: return uint32_t(rx[1] << 8 | rx[2]); } + uint64_t transfer64_40(const uint64_t data_) + { + uint64_t data = data_; + uint8_t* tx_data = reinterpret_cast<uint8_t*>(&data); + + // Create tx and rx buffers: + /* Address and TX data only represents up to 6 out of 8 bytes to + transfer. The remaining bytes are buffer for processing gap + and response status. */ + uint8_t tx[] = {tx_data[5], + tx_data[4], + tx_data[3], + tx_data[2], + tx_data[1], + tx_data[0], + 0, + 0}; + uint8_t rx[8]; // Buffer length must match tx buffer + + if (transfer(_fd, &tx[0], &rx[0], 8, _speed, _bits, _delay) != 0) { + throw mpm::runtime_error(str(boost::format("SPI Transaction failed!"))); + } + + uint64_t result = rx[3]; + result = (result << 8) | rx[4]; + result = (result << 8) | rx[5]; + result = (result << 8) | rx[6]; + result = (result << 8) | rx[7]; + + return result; + } + private: int _fd; const uint32_t _mode; |