Make DMA SPI driver aware of CPU cache, fix data corruption and other SPI issues on STM32H7 (#199)

* Handle cache alignment in DMA SPI driver

* Fix build on cache-less devices

* Fix a couple things I missed

* Run formatter, improve CacheAlignedBuffer docs

* Add missing license identifiers to source files

* Make CacheAlignedBuffer heap-allocatable, try and add exclusion for Nordic license in scancode_evaluate.py

* Formatting, docs, revert accidental change

* Update code blocks to pass spell checker
pull/15494/head
Jamie Smith 2023-12-19 10:21:47 -08:00 committed by GitHub
parent d9676cccca
commit 69d95b598a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 614 additions and 104 deletions

View File

@ -26,6 +26,7 @@
#include "drivers/DigitalOut.h" #include "drivers/DigitalOut.h"
#include "platform/SingletonPtr.h" #include "platform/SingletonPtr.h"
#include "platform/NonCopyable.h" #include "platform/NonCopyable.h"
#include "platform/CacheAlignedBuffer.h"
#if defined MBED_CONF_DRIVERS_SPI_COUNT_MAX && DEVICE_SPI_COUNT > MBED_CONF_DRIVERS_SPI_COUNT_MAX #if defined MBED_CONF_DRIVERS_SPI_COUNT_MAX && DEVICE_SPI_COUNT > MBED_CONF_DRIVERS_SPI_COUNT_MAX
#define SPI_PERIPHERALS_USED MBED_CONF_DRIVERS_SPI_COUNT_MAX #define SPI_PERIPHERALS_USED MBED_CONF_DRIVERS_SPI_COUNT_MAX
@ -194,6 +195,11 @@ const use_gpio_ssel_t use_gpio_ssel;
* the transfer but others can execute). Here's a sample of how to send the same data as above * the transfer but others can execute). Here's a sample of how to send the same data as above
* using the blocking async API:</p> * using the blocking async API:</p>
* *
* <p>Note that when using the asynchronous API, you must use the CacheAlignedBuffer class when declaring the
* receive buffer. This is because some processors' async SPI implementations require the received buffer to
* be at an address which is aligned to the processor cache line size. CacheAlignedBuffer takes care of this
* for you and provides functions (data(), begin(), end()) to access the underlying data in the buffer.</p>
*
* @code * @code
* #include "mbed.h" * #include "mbed.h"
* *
@ -203,8 +209,8 @@ const use_gpio_ssel_t use_gpio_ssel;
* device.format(8, 0); * device.format(8, 0);
* *
* uint8_t command[2] = {0x0A, 0x0B}; * uint8_t command[2] = {0x0A, 0x0B};
* uint8_t response[2]; * CacheAlignedBuffer<uint8_t, 2> response;
* int result = device.transfer_and_wait(command, sizeof(command), response, sizeof(response)); * int result = device.transfer_and_wait(command, sizeof(command), response, sizeof(command));
* } * }
* @endcode * @endcode
* *
@ -458,8 +464,9 @@ public:
* @param tx_buffer The TX buffer with data to be transferred. If NULL is passed, * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
* the default %SPI value is sent. * the default %SPI value is sent.
* @param tx_length The length of TX buffer in bytes. * @param tx_length The length of TX buffer in bytes.
* @param rx_buffer The RX buffer which is used for received data. If NULL is passed, * @param rx_buffer The RX buffer which is used for received data. Rather than a C array, a CacheAlignedBuffer
* received data are ignored. * structure must be passed so that cache alignment can be handled for data received from DMA.
* May be nullptr if rx_length is 0.
* @param rx_length The length of RX buffer in bytes. * @param rx_length The length of RX buffer in bytes.
* @param callback The event callback function. * @param callback The event callback function.
* @param event The logical OR of events to subscribe to. May be #SPI_EVENT_ALL, or some combination * @param event The logical OR of events to subscribe to. May be #SPI_EVENT_ALL, or some combination
@ -471,16 +478,18 @@ public:
*/ */
template<typename WordT> template<typename WordT>
typename std::enable_if<std::is_integral<WordT>::value, int>::type typename std::enable_if<std::is_integral<WordT>::value, int>::type
transfer(const WordT *tx_buffer, int tx_length, WordT *rx_buffer, int rx_length, const event_callback_t &callback, int event = SPI_EVENT_COMPLETE) transfer(const WordT *tx_buffer, int tx_length, CacheAlignedBuffer<WordT> &rx_buffer, int rx_length, const event_callback_t &callback, int event = SPI_EVENT_COMPLETE)
{ {
return transfer_internal(tx_buffer, tx_length, rx_buffer, rx_length, callback, event); MBED_ASSERT(rx_length <= static_cast<int>(rx_buffer.capacity()));
return transfer_internal(tx_buffer, tx_length, rx_buffer.data(), rx_length, callback, event);
} }
// Overloads of the above to support passing nullptr // Overloads of the above to support passing nullptr
template<typename WordT> template<typename WordT>
typename std::enable_if<std::is_integral<WordT>::value, int>::type typename std::enable_if<std::is_integral<WordT>::value, int>::type
transfer(const std::nullptr_t *tx_buffer, int tx_length, WordT *rx_buffer, int rx_length, const event_callback_t &callback, int event = SPI_EVENT_COMPLETE) transfer(const std::nullptr_t *tx_buffer, int tx_length, CacheAlignedBuffer<WordT> &rx_buffer, int rx_length, const event_callback_t &callback, int event = SPI_EVENT_COMPLETE)
{ {
MBED_ASSERT(rx_length <= static_cast<int>(rx_buffer.capacity()));
return transfer_internal(tx_buffer, tx_length, rx_buffer, rx_length, callback, event); return transfer_internal(tx_buffer, tx_length, rx_buffer, rx_length, callback, event);
} }
template<typename WordT> template<typename WordT>
@ -502,8 +511,10 @@ public:
* Internally, the chip vendor may implement this function using either DMA or interrupts. * Internally, the chip vendor may implement this function using either DMA or interrupts.
* *
* @param tx_buffer The TX buffer with data to be transferred. May be nullptr if tx_length is 0. * @param tx_buffer The TX buffer with data to be transferred. May be nullptr if tx_length is 0.
* @param tx_length The length of TX buffer in bytes. If 0, no transmission is done. * @param tx_length The length of TX buffer in bytes. If 0, the default %SPI data value is sent when receiving data.
* @param rx_buffer The RX buffer, which is used for received data. May be nullptr if tx_length is 0. * @param rx_buffer The RX buffer which is used for received data. Rather than a C array, a CacheAlignedBuffer
* structure must be passed so that cache alignment can be handled for data received from DMA.
* May be nullptr if rx_length is 0.
* @param rx_length The length of RX buffer in bytes If 0, no reception is done. * @param rx_length The length of RX buffer in bytes If 0, no reception is done.
* @param timeout timeout value. Use #rtos::Kernel::wait_for_u32_forever to wait forever (the default). * @param timeout timeout value. Use #rtos::Kernel::wait_for_u32_forever to wait forever (the default).
* *
@ -515,17 +526,19 @@ public:
*/ */
template<typename WordT> template<typename WordT>
typename std::enable_if<std::is_integral<WordT>::value, int>::type typename std::enable_if<std::is_integral<WordT>::value, int>::type
transfer_and_wait(const WordT *tx_buffer, int tx_length, WordT *rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout = rtos::Kernel::wait_for_u32_forever) transfer_and_wait(const WordT *tx_buffer, int tx_length, CacheAlignedBuffer<WordT> &rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout = rtos::Kernel::wait_for_u32_forever)
{ {
return transfer_and_wait_internal(tx_buffer, tx_length, rx_buffer, rx_length, timeout); MBED_ASSERT(rx_length <= static_cast<int>(rx_buffer.capacity()));
return transfer_and_wait_internal(tx_buffer, tx_length, rx_buffer.data(), rx_length, timeout);
} }
// Overloads of the above to support passing nullptr // Overloads of the above to support passing nullptr
template<typename WordT> template<typename WordT>
typename std::enable_if<std::is_integral<WordT>::value, int>::type typename std::enable_if<std::is_integral<WordT>::value, int>::type
transfer_and_wait(const std::nullptr_t *tx_buffer, int tx_length, WordT *rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout = rtos::Kernel::wait_for_u32_forever) transfer_and_wait(const std::nullptr_t *tx_buffer, int tx_length, CacheAlignedBuffer<WordT> &rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout = rtos::Kernel::wait_for_u32_forever)
{ {
return transfer_and_wait_internal(tx_buffer, tx_length, rx_buffer, rx_length, timeout); MBED_ASSERT(rx_length <= static_cast<int>(rx_buffer.capacity()));
return transfer_and_wait_internal(tx_buffer, tx_length, rx_buffer.data(), rx_length, timeout);
} }
template<typename WordT> template<typename WordT>
typename std::enable_if<std::is_integral<WordT>::value, int>::type typename std::enable_if<std::is_integral<WordT>::value, int>::type
@ -574,7 +587,8 @@ protected:
* the default SPI value is sent * the default SPI value is sent
* @param tx_length The length of TX buffer in bytes. * @param tx_length The length of TX buffer in bytes.
* @param rx_buffer The RX buffer which is used for received data. If NULL is passed, * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
* received data are ignored. * received data are ignored. This buffer is guaranteed to be cache aligned
* if the MCU has a cache.
* @param rx_length The length of RX buffer in bytes. * @param rx_length The length of RX buffer in bytes.
* @param callback The event callback function. * @param callback The event callback function.
* @param event The event mask of events to modify. * @param event The event mask of events to modify.
@ -599,6 +613,7 @@ protected:
* @param tx_buffer The TX buffer with data to be transferred. May be nullptr if tx_length is 0. * @param tx_buffer The TX buffer with data to be transferred. May be nullptr if tx_length is 0.
* @param tx_length The length of TX buffer in bytes. If 0, no transmission is done. * @param tx_length The length of TX buffer in bytes. If 0, no transmission is done.
* @param rx_buffer The RX buffer, which is used for received data. May be nullptr if tx_length is 0. * @param rx_buffer The RX buffer, which is used for received data. May be nullptr if tx_length is 0.
* This buffer is guaranteed to be cache aligned if the MCU has a cache.
* @param rx_length The length of RX buffer in bytes If 0, no reception is done. * @param rx_length The length of RX buffer in bytes If 0, no reception is done.
* @param timeout timeout value. Use #rtos::Kernel::wait_for_u32_forever to wait forever (the default). * @param timeout timeout value. Use #rtos::Kernel::wait_for_u32_forever to wait forever (the default).
* *
@ -722,6 +737,18 @@ protected:
* iff start_transfer() has been called and the chip has been selected but irq_handler_asynch() * iff start_transfer() has been called and the chip has been selected but irq_handler_asynch()
* has NOT been called yet. */ * has NOT been called yet. */
volatile bool _transfer_in_progress = false; volatile bool _transfer_in_progress = false;
// If there is a transfer in progress, this indicates whether it used DMA and therefore requires a cache
// flush at the end
bool _transfer_in_progress_uses_dma;
#if __DCACHE_PRESENT
// These variables store the location and length in bytes of the Rx buffer if an async transfer
// is in progress. They are used for invalidating the cache after the transfer completes.
void *_transfer_in_progress_rx_buffer;
size_t _transfer_in_progress_rx_len;
#endif
/* Event flags used for transfer_and_wait() */ /* Event flags used for transfer_and_wait() */
rtos::EventFlags _transfer_and_wait_flags; rtos::EventFlags _transfer_and_wait_flags;
#endif // DEVICE_SPI_ASYNCH #endif // DEVICE_SPI_ASYNCH

View File

@ -396,6 +396,16 @@ void SPI::abort_transfer()
_set_ssel(1); _set_ssel(1);
} }
#if __DCACHE_PRESENT
if (_transfer_in_progress_uses_dma && _transfer_in_progress_rx_len > 0) {
// If the cache is present, invalidate the Rx data so it's loaded from main RAM.
// We only want to do this if DMA actually got used for the transfer because, if interrupts
// were used instead, the cache might have the correct data and NOT the main memory.
SCB_InvalidateDCache_by_Addr(_transfer_in_progress_rx_buffer, _transfer_in_progress_rx_len);
}
#endif
_transfer_in_progress = false;
#if MBED_CONF_DRIVERS_SPI_TRANSACTION_QUEUE_LEN #if MBED_CONF_DRIVERS_SPI_TRANSACTION_QUEUE_LEN
dequeue_transaction(); dequeue_transaction();
#endif #endif
@ -466,8 +476,31 @@ void SPI::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer,
_callback = callback; _callback = callback;
_irq.callback(&SPI::irq_handler_asynch); _irq.callback(&SPI::irq_handler_asynch);
#if __DCACHE_PRESENT
// On devices with a cache, we need to carefully manage the Tx and Rx buffer cache invalidation.
// We can assume that asynchronous SPI implementations might rely on DMA, and that DMA will
// not interoperate with the CPU cache. So, manual flushing/invalidation will be required.
// This page is very useful for how to do this correctly:
// https://community.st.com/t5/stm32-mcus-products/maintaining-cpu-data-cache-coherence-for-dma-buffers/td-p/95746
if (tx_length > 0) {
// For chips with a cache, we need to evict the Tx data from cache to main memory.
// This ensures that the DMA controller can see the most up-to-date copy of the data.
SCB_CleanDCache_by_Addr(const_cast<void *>(tx_buffer), tx_length);
}
// Additionally, we have to make sure that there aren't any pending changes which could be written back
// to the Rx buffer memory by the cache at a later date, corrupting the DMA results.
if (rx_length > 0) {
SCB_InvalidateDCache_by_Addr(rx_buffer, rx_length);
}
_transfer_in_progress_rx_buffer = rx_buffer;
_transfer_in_progress_rx_len = rx_length;
#endif
_transfer_in_progress = true; _transfer_in_progress = true;
spi_master_transfer(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, bit_width, _irq.entry(), event, _usage);
_transfer_in_progress_uses_dma = spi_master_transfer(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, bit_width, _irq.entry(), event, _usage);
} }
void SPI::lock_deep_sleep() void SPI::lock_deep_sleep()
@ -508,7 +541,17 @@ void SPI::dequeue_transaction()
void SPI::irq_handler_asynch(void) void SPI::irq_handler_asynch(void)
{ {
int event = spi_irq_handler_asynch(&_peripheral->spi); int event = spi_irq_handler_asynch(&_peripheral->spi);
if (_callback && (event & SPI_EVENT_ALL)) { if ((event & SPI_EVENT_ALL)) {
#if __DCACHE_PRESENT
if (_transfer_in_progress_uses_dma && _transfer_in_progress_rx_len > 0) {
// If the cache is present, invalidate the Rx data so it's loaded from main RAM.
// We only want to do this if DMA actually got used for the transfer because, if interrupts
// were used instead, the cache might have the correct data and NOT the main memory.
SCB_InvalidateDCache_by_Addr(_transfer_in_progress_rx_buffer, _transfer_in_progress_rx_len);
}
#endif
// If using GPIO CS mode, unless we were asked to keep the peripheral selected, deselect it. // If using GPIO CS mode, unless we were asked to keep the peripheral selected, deselect it.
// If there's another transfer queued, we *do* want to deselect the peripheral now. // If there's another transfer queued, we *do* want to deselect the peripheral now.
// It will be reselected in start_transfer() which is called by dequeue_transaction() below. // It will be reselected in start_transfer() which is called by dequeue_transaction() below.
@ -518,7 +561,10 @@ void SPI::irq_handler_asynch(void)
_transfer_in_progress = false; _transfer_in_progress = false;
unlock_deep_sleep(); unlock_deep_sleep();
_callback.call(event & SPI_EVENT_ALL);
if (_callback) {
_callback.call(event & SPI_EVENT_ALL);
}
} }
#if MBED_CONF_DRIVERS_SPI_TRANSACTION_QUEUE_LEN #if MBED_CONF_DRIVERS_SPI_TRANSACTION_QUEUE_LEN
if (event & (SPI_EVENT_ALL | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE)) { if (event & (SPI_EVENT_ALL | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE)) {

View File

@ -25,6 +25,8 @@
#include "hal/dma_api.h" #include "hal/dma_api.h"
#include "hal/buffer.h" #include "hal/buffer.h"
#include <stdbool.h>
#if DEVICE_SPI #if DEVICE_SPI
/** /**
@ -393,7 +395,11 @@ const PinMap *spi_slave_cs_pinmap(void);
* @{ * @{
*/ */
/** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff /** Begin the asynchronous SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff.
*
* If the device has a data cache, the Tx data is guaranteed to have been flushed from the cache
* to main memory already. Additionally, the Rx buffer is guaranteed to be cache aligned, and will
* be invalidated by the SPI layer after the transfer is complete.
* *
* @param[in] obj The SPI object that holds the transfer information * @param[in] obj The SPI object that holds the transfer information
* @param[in] tx The transmit buffer * @param[in] tx The transmit buffer
@ -404,8 +410,16 @@ const PinMap *spi_slave_cs_pinmap(void);
* @param[in] event The logical OR of events to be registered * @param[in] event The logical OR of events to be registered
* @param[in] handler SPI interrupt handler * @param[in] handler SPI interrupt handler
* @param[in] hint A suggestion for how to use DMA with this transfer * @param[in] hint A suggestion for how to use DMA with this transfer
*
* @return True if DMA was actually used for the transfer, false otherwise (if interrupts or another CPU-based
* method is used to do the transfer).
*
* @note On MCUs with a data cache, the return value is used to determine if a cache invalidation needs to be done
* after the transfer is complete. If this function returns true, the driver layer will cache invalidate the Rx buffer under
* the assumption that the data needs to be re-read from main memory. Be careful, because if the read was not actually
* done by DMA, and the rx data is in the CPU cache, this invalidation will corrupt it.
*/ */
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint); bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint);
/** The asynchronous IRQ handler /** The asynchronous IRQ handler
* *

View File

@ -0,0 +1,340 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2023 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_CACHEALIGNEDBUFFER_H
#define MBED_CACHEALIGNEDBUFFER_H
#include <cstdlib>
#include "device.h"
namespace mbed {
namespace detail::cab {
/**
* @brief Calculate the needed capacity for a cache aligned buffer's backing buffer based on the
* needed capacity and element size.
*
* @param neededCapacity Capacity needed for the buffer
* @param elementSize Size of each element
*
* @return Needed backing buffer size
*/
constexpr inline size_t getNeededBackingBufferSize(size_t neededCapacity, size_t elementSize)
{
#if __DCACHE_PRESENT
// Allocate enough extra space that we can shift the start of the buffer towards higher addresses to be on a cache line.
// The worst case for this is when the first byte is allocated 1 byte past the start of a cache line, so we
// will need an additional (cache line size - 1) bytes.
// Additionally, since we are going to be invalidating this buffer, we can't allow any other variables to be
// in the same cache lines, or they might get corrupted.
// So, we need to round up the backing buffer size to the nearest multiple of the cache line size.
// The math for rounding up can be found here:
// https://community.st.com/t5/stm32-mcus-products/maintaining-cpu-data-cache-coherence-for-dma-buffers/td-p/95746
size_t requiredSizeRoundedUp = (neededCapacity * elementSize + __SCB_DCACHE_LINE_SIZE - 1) & ~((__SCB_DCACHE_LINE_SIZE) - 1);
return requiredSizeRoundedUp + __SCB_DCACHE_LINE_SIZE - 1;
#else
// No cache on this platform so don't need any extra space.
return neededCapacity * elementSize;
#endif
}
}
/** \addtogroup platform-public-api */
/** @{*/
/**
* \defgroup platform_CacheAlignedBuffer CacheAlignedBuffer class
* @{
*/
/**
* @brief CacheAlignedBuffer is used by Mbed in locations where we need a cache-aligned buffer.
*
* <p>Cache alignment is desirable in several different situations in embedded programming -- one common
* use is when working with DMA or other peripherals which write their results back to main memory.
* After these peripherals do their work, the data will be correct in main memory, but the CPU cache
* might also contain a value cached from that memory which is now incorrect.<p>
*
* <p>In order to read those results from memory without risk of getting old data from the
* CPU cache, one needs to align the buffer so it takes up an integer number of cache lines,
* then invalidate the cache lines so that the data gets reread from RAM.</p>
*
* <p>%CacheAlignedBuffer provides an easy way to allocate the correct amount of space so that
* a buffer of any size can be made cache-aligned. To instantiate a %CacheAlignedBuffer, create one of its
* subtypes, StaticCacheAlignedBuffer or DynamicCacheAlignedBuffer.</p>
*
* <h2> Converting Code to use CacheAlignedBuffer </h2>
* For code using static arrays, like this:
* @code
* uint8_t rxBuffer[16];
* spi.transfer_and_wait(nullptr, 0, rxBuffer, 16);
* @endcode
* Use a StaticCacheAlignedBuffer:
* @code
* StaticCacheAlignedBuffer<uint8_t, 16> rxBuffer;
* spi.transfer_and_wait(nullptr, 0, rxBuffer, 16);
* @endcode
* For code using dynamic allocation to handle unknown buffer sizes, like:
* @code
* uint16_t * rxBuffer = new uint16_t[bufferSize];
* spi.transfer_and_wait(nullptr, 0, rxBuffer, bufferSize);
* ...
* delete[] rxBuffer;
* @endcode
* use a DynamicCacheAlignedBuffer:
* @code
* DynamicCacheAlignedBuffer<uint16_t> rxBuffer(bufferSize);
* spi.transfer_and_wait(nullptr, 0, rxBuffer, bufferSize);
* @endcode
*
* @tparam DataT Type of the data to store in the buffer. Note: %CacheAlignedBuffer is not designed for
* using class types as DataT, and will not call constructors.
*/
template<typename DataT>
class CacheAlignedBuffer {
protected:
/// Pointer to the aligned buffer. Must be set in each constructor of each subclass.
DataT *_alignedBufferPtr;
/// Capacity of the aligned buffer, in terms of number of DataT elements
size_t _alignedBufferCapacity;
// Protected constructor to block instantiation
CacheAlignedBuffer() = default;
/**
* Find and return the first location in the given buffer that starts on a cache line.
* If this MCU does not use a cache, this function is a no-op.
*
* @param buffer Pointer to buffer
*
* @return Pointer to first data item, aligned at the start of a cache line.
*/
static inline DataT *findCacheLineStart(uint8_t *buffer)
{
#if __DCACHE_PRESENT
// Use integer division to divide the address down to the cache line size, which
// rounds to the cache line before the given address.
// So that we always go one cache line back even if the given address is on a cache line,
// subtract 1.
ptrdiff_t prevCacheLine = ((ptrdiff_t)(buffer - 1)) / __SCB_DCACHE_LINE_SIZE;
// Now we just have to multiply up again to get an address (adding 1 to go forward by 1 cache line)
return reinterpret_cast<DataT *>((prevCacheLine + 1) * __SCB_DCACHE_LINE_SIZE);
#else
return reinterpret_cast<DataT *>(buffer);
#endif
}
public:
// Iterator types
typedef DataT *iterator;
typedef DataT const *const_iterator;
/**
* @brief Get a pointer to the aligned data array inside the buffer
*/
DataT *data()
{
return _alignedBufferPtr;
}
/**
* @brief Get a pointer to the aligned data array inside the buffer (const version)
*/
DataT const *data() const
{
return _alignedBufferPtr;
}
/**
* @brief Element access
*/
DataT &operator[](size_t index)
{
return _alignedBufferPtr[index];
}
/**
* @brief Element access (const)
*/
DataT operator[](size_t index) const
{
return _alignedBufferPtr[index];
}
/**
* @brief Get iterator for start of buffer
*/
iterator begin()
{
return _alignedBufferPtr;
}
/**
* @brief Get iterator for start of buffer
*/
const_iterator begin() const
{
return _alignedBufferPtr;
}
/**
* @brief Get iterator for end of buffer
*/
iterator end()
{
return _alignedBufferPtr + _alignedBufferCapacity;
}
/**
* @brief Get iterator for end of buffer
*/
const_iterator end() const
{
return _alignedBufferPtr + _alignedBufferCapacity;
}
/**
* @return The maximum amount of DataT elements that this buffer can hold.
*/
constexpr size_t capacity()
{
return _alignedBufferCapacity;
}
};
/**
* @brief CacheAlignedBuffer type designed for static allocation.
*
* Use a StaticCacheAlignedBuffer when you want to create a cache-aligned buffer with a fixed size
* at compile time. %StaticCacheAlignedBuffers can be declared globally, as local variables, or using
* new[] and delete[].
*
* @tparam DataT Type of the data to store in the buffer. Note: %CacheAlignedBuffer is not designed for
* using class types as DataT, and will not call constructors.
* @tparam BufferSize Buffer size (number of elements) needed by the application for the buffer.
*/
template<typename DataT, size_t BufferSize>
class StaticCacheAlignedBuffer : public CacheAlignedBuffer<DataT> {
private:
uint8_t _backingBuffer[detail::cab::getNeededBackingBufferSize(BufferSize, sizeof(DataT))];
public:
/**
* @brief Construct new cache-aligned buffer. Buffer will be zero-initialized.
*/
StaticCacheAlignedBuffer():
_backingBuffer{}
{
this->_alignedBufferPtr = this->findCacheLineStart(_backingBuffer);
this->_alignedBufferCapacity = BufferSize;
}
/**
* @brief Copy from other cache-aligned buffer. Buffer memory will be copied.
*/
StaticCacheAlignedBuffer(StaticCacheAlignedBuffer const &other)
{
this->_alignedBufferPtr = this->findCacheLineStart(_backingBuffer);
memcpy(this->_alignedBufferPtr, other._alignedBufferPtr, BufferSize * sizeof(DataT));
this->_alignedBufferCapacity = BufferSize;
}
/**
* @brief Assign from other cache-aligned buffer. Buffer memory will be assigned.
*
* Only a buffer with the same data type and size can be assigned.
*/
StaticCacheAlignedBuffer &operator=(StaticCacheAlignedBuffer<DataT, BufferSize> const &other)
{
memmove(this->_alignedBufferPtr, other._alignedBufferPtr, BufferSize * sizeof(DataT));
}
};
/**
* @brief CacheAlignedBuffer type which allocates its backing buffer on the heap.
*
* Use a DynamicCacheAlignedBuffer when you want to create a cache-aligned buffer with a size
* known only at runtime. When constructed, %DynamicCacheAlignedBuffers allocate backing memory off the
* heap for the provided number of elements. The memory will be released when the buffer object is destroyed.
*
* @tparam DataT Type of the data to store in the buffer. Note: %CacheAlignedBuffer is not designed for
* using class types as DataT, and will not call constructors.
*/
template<typename DataT>
class DynamicCacheAlignedBuffer : public CacheAlignedBuffer<DataT> {
uint8_t *_heapMem;
public:
/**
* @brief Construct new cache-aligned buffer. Buffer will be zero-initialized and allocated from the heap.
*
* @param capacity Number of elements the buffer shall hold
*/
explicit DynamicCacheAlignedBuffer(size_t capacity):
_heapMem(new uint8_t[detail::cab::getNeededBackingBufferSize(capacity, sizeof(DataT))]())
{
this->_alignedBufferPtr = this->findCacheLineStart(_heapMem);
this->_alignedBufferCapacity = capacity;
}
/**
* @brief Copy from other cache-aligned buffer. A new backing buffer will be allocated on the heap and
* its data will be copied from the other buffer.
*/
DynamicCacheAlignedBuffer(DynamicCacheAlignedBuffer const &other):
_heapMem(new uint8_t[detail::cab::getNeededBackingBufferSize(other._alignedBufferCapacity, sizeof(DataT))])
{
this->_alignedBufferCapacity = other._alignedBufferCapacity;
this->_alignedBufferPtr = this->findCacheLineStart(_heapMem);
memcpy(this->_alignedBufferPtr, other._alignedBufferPtr, this->_alignedBufferCapacity * sizeof(DataT));
}
// Destructor
~DynamicCacheAlignedBuffer()
{
delete[] this->_heapMem;
}
/**
* @brief Assign from other cache-aligned buffer with the same type. A new buffer will be allocated
* of the correct size.
*/
DynamicCacheAlignedBuffer &operator=(DynamicCacheAlignedBuffer const &other)
{
// Check for self assignment
if (&other == this) {
return *this;
}
delete[] _heapMem;
_heapMem = new uint8_t[detail::cab::getNeededBackingBufferSize(other._alignedBufferCapacity, sizeof(DataT))];
this->_alignedBufferPtr = this->findCacheLineStart(_heapMem);
memcpy(this->_alignedBufferPtr, other._alignedBufferPtr, this->_alignedBufferCapacity * sizeof(DataT));
}
};
/// @}
}
#endif //MBED_CACHEALIGNEDBUFFER_H

View File

@ -57,6 +57,10 @@
* Access an SD Card using SPI bus * Access an SD Card using SPI bus
*/ */
class SDBlockDevice : public mbed::BlockDevice { class SDBlockDevice : public mbed::BlockDevice {
// Only HC block size is supported. Making this a static constant reduces code size.
static constexpr uint32_t _block_size = 512; /*!< Block size supported for SDHC card is 512 bytes */
public: public:
/** Creates an SDBlockDevice on a SPI bus specified by pins (using dynamic pin-map) /** Creates an SDBlockDevice on a SPI bus specified by pins (using dynamic pin-map)
* *
@ -302,7 +306,6 @@ private:
} }
rtos::Mutex _mutex; rtos::Mutex _mutex;
static const uint32_t _block_size;
uint32_t _erase_size; uint32_t _erase_size;
bool _is_initialized; bool _is_initialized;
bool _dbg; bool _dbg;
@ -314,6 +317,7 @@ private:
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
bool _async_spi_enabled = false; bool _async_spi_enabled = false;
mbed::StaticCacheAlignedBuffer<uint8_t, _block_size> _async_data_buffer;
#endif #endif
}; };

View File

@ -181,7 +181,6 @@ using namespace std::chrono;
#define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */ #define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */
#define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */ #define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */
#define BLOCK_SIZE_HC 512 /*!< Block size supported for SD card is 512 bytes */
#define WRITE_BL_PARTIAL 0 /*!< Partial block write - Not supported */ #define WRITE_BL_PARTIAL 0 /*!< Partial block write - Not supported */
#define SPI_CMD(x) (0x40 | (x & 0x3f)) #define SPI_CMD(x) (0x40 | (x & 0x3f))
@ -248,9 +247,6 @@ using namespace std::chrono;
#define SPI_READ_ERROR_ECC_C (0x1 << 2) /*!< Card ECC failed */ #define SPI_READ_ERROR_ECC_C (0x1 << 2) /*!< Card ECC failed */
#define SPI_READ_ERROR_OFR (0x1 << 3) /*!< Out of Range */ #define SPI_READ_ERROR_OFR (0x1 << 3) /*!< Out of Range */
// Only HC block size is supported. Making this a static constant reduces code size.
const uint32_t SDBlockDevice::_block_size = BLOCK_SIZE_HC;
#if MBED_CONF_SD_CRC_ENABLED #if MBED_CONF_SD_CRC_ENABLED
SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz, bool crc_on) SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz, bool crc_on)
: _sectors(0), _spi(mosi, miso, sclk, cs, use_gpio_ssel), _is_initialized(0), : _sectors(0), _spi(mosi, miso, sclk, cs, use_gpio_ssel), _is_initialized(0),
@ -269,7 +265,7 @@ SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName c
_init_sck = MBED_CONF_SD_INIT_FREQUENCY; _init_sck = MBED_CONF_SD_INIT_FREQUENCY;
_transfer_sck = hz; _transfer_sck = hz;
_erase_size = BLOCK_SIZE_HC; _erase_size = _block_size;
} }
#if MBED_CONF_SD_CRC_ENABLED #if MBED_CONF_SD_CRC_ENABLED
@ -290,7 +286,7 @@ SDBlockDevice::SDBlockDevice(const spi_pinmap_t &spi_pinmap, PinName cs, uint64_
_init_sck = MBED_CONF_SD_INIT_FREQUENCY; _init_sck = MBED_CONF_SD_INIT_FREQUENCY;
_transfer_sck = hz; _transfer_sck = hz;
_erase_size = BLOCK_SIZE_HC; _erase_size = _block_size;
} }
SDBlockDevice::~SDBlockDevice() SDBlockDevice::~SDBlockDevice()
@ -925,7 +921,15 @@ int SDBlockDevice::_read_bytes(uint8_t *buffer, uint32_t length)
// read data // read data
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
if (_async_spi_enabled) { if (_async_spi_enabled) {
_spi.transfer_and_wait(nullptr, 0, buffer, length); if (length > _async_data_buffer.capacity()) {
return SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
// Do read into cache aligned buffer, then copy data into application buffer
if (_spi.transfer_and_wait(nullptr, 0, _async_data_buffer, length) != 0) {
return SD_BLOCK_DEVICE_ERROR_WRITE;
}
memcpy(buffer, _async_data_buffer.data(), length);
} else } else
#endif #endif
{ {
@ -968,9 +972,15 @@ int SDBlockDevice::_read(uint8_t *buffer, uint32_t length)
// read data // read data
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
if (_async_spi_enabled) { if (_async_spi_enabled) {
if (_spi.transfer_and_wait(nullptr, 0, buffer, length) != 0) { if (length > _async_data_buffer.capacity()) {
return SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
// Do read into cache aligned buffer, then copy data into application buffer
if (_spi.transfer_and_wait(nullptr, 0, _async_data_buffer, length) != 0) {
return SD_BLOCK_DEVICE_ERROR_WRITE; return SD_BLOCK_DEVICE_ERROR_WRITE;
} }
memcpy(buffer, _async_data_buffer.data(), length);
} else } else
#endif #endif
{ {
@ -1067,7 +1077,13 @@ bd_size_t SDBlockDevice::_sd_sectors()
debug_if(SD_DBG, "Didn't get a response from the disk\n"); debug_if(SD_DBG, "Didn't get a response from the disk\n");
return 0; return 0;
} }
#ifdef DEVICE_SPI_ASYNCH
StaticCacheAlignedBuffer<uint8_t, 16> csd_buffer;
uint8_t *csd = csd_buffer.data();
#else
uint8_t csd[16]; uint8_t csd[16];
#endif
if (_read_bytes(csd, 16) != 0) { if (_read_bytes(csd, 16) != 0) {
debug_if(SD_DBG, "Couldn't read csd response from disk\n"); debug_if(SD_DBG, "Couldn't read csd response from disk\n");
return 0; return 0;
@ -1091,10 +1107,10 @@ bd_size_t SDBlockDevice::_sd_sectors()
// ERASE_BLK_EN = 1: Erase in multiple of 512 bytes supported // ERASE_BLK_EN = 1: Erase in multiple of 512 bytes supported
if (ext_bits(csd, 46, 46)) { if (ext_bits(csd, 46, 46)) {
_erase_size = BLOCK_SIZE_HC; _erase_size = _block_size;
} else { } else {
// ERASE_BLK_EN = 1: Erase in multiple of SECTOR_SIZE supported // ERASE_BLK_EN = 1: Erase in multiple of SECTOR_SIZE supported
_erase_size = BLOCK_SIZE_HC * (ext_bits(csd, 45, 39) + 1); _erase_size = _block_size * (ext_bits(csd, 45, 39) + 1);
} }
break; break;
@ -1105,7 +1121,7 @@ bd_size_t SDBlockDevice::_sd_sectors()
debug_if(SD_DBG, "Sectors: 0x%" PRIx64 "x : %" PRIu64 "\n", blocks, blocks); debug_if(SD_DBG, "Sectors: 0x%" PRIx64 "x : %" PRIu64 "\n", blocks, blocks);
debug_if(SD_DBG, "Capacity: %" PRIu64 " MB\n", (blocks / (2048U))); debug_if(SD_DBG, "Capacity: %" PRIu64 " MB\n", (blocks / (2048U)));
// ERASE_BLK_EN is fixed to 1, which means host can erase one or multiple of 512 bytes. // ERASE_BLK_EN is fixed to 1, which means host can erase one or multiple of 512 bytes.
_erase_size = BLOCK_SIZE_HC; _erase_size = _block_size;
break; break;
default: default:

View File

@ -234,7 +234,7 @@ const PinMap *spi_slave_cs_pinmap(void)
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, MBED_UNUSED uint8_t bit_width, uint32_t handler, uint32_t event, MBED_UNUSED DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, MBED_UNUSED uint8_t bit_width, uint32_t handler, uint32_t event, MBED_UNUSED DMAUsage hint)
{ {
struct spi_s *spi = cy_get_spi(obj); struct spi_s *spi = cy_get_spi(obj);
spi->async_handler = (void (*)(void))handler; spi->async_handler = (void (*)(void))handler;
@ -248,6 +248,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_SPI, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_spi_transfer_async"); MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_SPI, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_spi_transfer_async");
} }
spi->async_in_progress = true; spi->async_in_progress = true;
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)

View File

@ -358,7 +358,7 @@ static void spi_buffer_set(spi_t *obj, const void *tx, uint32_t tx_length, void
obj->rx_buff.width = bit_width; obj->rx_buff.width = bit_width;
} }
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
if (spi_active(obj)) { if (spi_active(obj)) {
return; return;
@ -400,6 +400,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
// Can't enter deep sleep as long as SPI transfer is active // Can't enter deep sleep as long as SPI transfer is active
sleep_manager_lock_deep_sleep(); sleep_manager_lock_deep_sleep();
} }
return hint != DMA_USAGE_NEVER; // DMA will be used as long as the hint is not NEVER
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)

View File

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2017 Nordic Semiconductor ASA * Copyright (c) 2017 Nordic Semiconductor ASA
* All rights reserved. * All rights reserved.
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:
@ -882,8 +883,10 @@ static void nordic_nrf5_spi_event_handler(nrfx_spi_evt_t const *p_event, void *p
* Parameter event The logical OR of events to be registered * Parameter event The logical OR of events to be registered
* Parameter handler SPI interrupt handler * Parameter handler SPI interrupt handler
* Parameter hint A suggestion for how to use DMA with this transfer * Parameter hint A suggestion for how to use DMA with this transfer
*
* Returns bool True if DMA was used for the transfer, false otherwise
*/ */
void spi_master_transfer(spi_t *obj, bool spi_master_transfer(spi_t *obj,
const void *tx, const void *tx,
size_t tx_length, size_t tx_length,
void *rx, void *rx,
@ -949,6 +952,12 @@ void spi_master_transfer(spi_t *obj,
/* Signal callback handler. */ /* Signal callback handler. */
callback(); callback();
} }
#if NRFX_CHECK(NRFX_SPIM_ENABLED)
return true; // We always use DMA when the SPIM preripheral is available
#else
return false; // We always use interrupts when the SPI peripheral is available
#endif
} }
/** The asynchronous IRQ handler /** The asynchronous IRQ handler

View File

@ -445,7 +445,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -573,6 +573,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -369,7 +369,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -497,6 +497,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -390,7 +390,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -518,6 +518,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -1,5 +1,6 @@
/* mbed Microcontroller Library /* mbed Microcontroller Library
* Copyright (c) 2015-2016 Nuvoton * Copyright (c) 2015-2016 Nuvoton
* SPDX-License-Identifier: Apache-2.0
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -383,7 +384,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -501,6 +502,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -488,7 +488,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -616,6 +616,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -439,7 +439,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -567,6 +567,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -1,5 +1,6 @@
/* mbed Microcontroller Library /* mbed Microcontroller Library
* Copyright (c) 2015-2017 Nuvoton * Copyright (c) 2015-2017 Nuvoton
* SPDX-License-Identifier: Apache-2.0
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -434,7 +435,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -549,6 +550,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
PDMA_Trigger(obj->spi.dma_chn_id_rx); PDMA_Trigger(obj->spi.dma_chn_id_rx);
PDMA_Trigger(obj->spi.dma_chn_id_tx); PDMA_Trigger(obj->spi.dma_chn_id_tx);
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -1,5 +1,6 @@
/* mbed Microcontroller Library /* mbed Microcontroller Library
* Copyright (c) 2015-2016 Nuvoton * Copyright (c) 2015-2016 Nuvoton
* SPDX-License-Identifier: Apache-2.0
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -389,7 +390,7 @@ void spi_slave_write(spi_t *obj, int value)
#endif #endif
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_SET_DATA_WIDTH(spi_base, bit_width); SPI_SET_DATA_WIDTH(spi_base, bit_width);
@ -503,6 +504,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
/* Don't enable SPI TX/RX threshold interrupts as commented above */ /* Don't enable SPI TX/RX threshold interrupts as commented above */
} }
return obj->spi.dma_usage != DMA_USAGE_NEVER;
} }
/** /**

View File

@ -518,7 +518,7 @@ static void spi_async_read(spi_t *obj)
* ASYNCHRONOUS HAL * ASYNCHRONOUS HAL
******************************************************************************/ ******************************************************************************/
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
int i; int i;
MBED_ASSERT(obj); MBED_ASSERT(obj);
@ -556,6 +556,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_irqs_set(obj, 1); spi_irqs_set(obj, 1);
spi_async_write(obj); spi_async_write(obj);
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)

View File

@ -526,7 +526,7 @@ static void spi_async_read(spi_t *obj)
* ASYNCHRONOUS HAL * ASYNCHRONOUS HAL
******************************************************************************/ ******************************************************************************/
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
int i; int i;
MBED_ASSERT(obj); MBED_ASSERT(obj);
@ -564,6 +564,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_irqs_set(obj, 1); spi_irqs_set(obj, 1);
spi_async_write(obj); spi_async_write(obj);
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)

View File

@ -290,7 +290,18 @@ HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart_IT(DMA_HandleTypeDef *hdma, uint32_
{ {
/* Enable Common interrupts*/ /* Enable Common interrupts*/
MODIFY_REG(((DMA_Stream_TypeDef *)hdma->Instance)->CR, (DMA_IT_TC | DMA_IT_TE | DMA_IT_DME | DMA_IT_HT), (DMA_IT_TC | DMA_IT_TE | DMA_IT_DME)); MODIFY_REG(((DMA_Stream_TypeDef *)hdma->Instance)->CR, (DMA_IT_TC | DMA_IT_TE | DMA_IT_DME | DMA_IT_HT), (DMA_IT_TC | DMA_IT_TE | DMA_IT_DME));
((DMA_Stream_TypeDef *)hdma->Instance)->FCR |= DMA_IT_FE;
/* Mbed CE mod: Only enable the FIFO Error interrupt if the FIFO is actually enabled.
* If it's not enabled, then this interrupt can trigger spuriously from memory bus
* stalls that the DMA engine encounters, and this creates random DMA failures.
* Reference forum thread here:
* https://community.st.com/t5/stm32-mcus-products/spi-dma-fifo-error-issue-feifx/td-p/537074
* also: https://community.st.com/t5/stm32-mcus-touch-gfx-and-gui/spi-dma-error-is-occurred-when-the-other-dma-memory-to-memory-is/td-p/191590
*/
if(((DMA_Stream_TypeDef *)hdma->Instance)->FCR & DMA_SxFCR_DMDIS)
{
((DMA_Stream_TypeDef *)hdma->Instance)->FCR |= DMA_IT_FE;
}
if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL)) if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL))
{ {

View File

@ -2,6 +2,7 @@
******************************************************************************* *******************************************************************************
* Copyright (c) 2015, STMicroelectronics * Copyright (c) 2015, STMicroelectronics
* All rights reserved. * All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -1410,13 +1411,15 @@ int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length,
int total = (tx_length > rx_length) ? tx_length : rx_length; int total = (tx_length > rx_length) ? tx_length : rx_length;
if (handle->Init.Direction == SPI_DIRECTION_2LINES) { if (handle->Init.Direction == SPI_DIRECTION_2LINES) {
const int word_size = 0x01 << bitshift;
int write_fill_frame = write_fill; int write_fill_frame = write_fill;
/* extend fill symbols for 16/32 bit modes */ /* extend fill symbols for 16/32 bit modes */
for (int i = 0; i < bitshift; i++) { for (int i = 0; i < word_size; i++) {
write_fill_frame = (write_fill_frame << 8) | write_fill; write_fill_frame = (write_fill_frame << 8) | write_fill;
} }
const int word_size = 0x01 << bitshift;
for (int i = 0; i < total; i += word_size) { for (int i = 0; i < total; i += word_size) {
int out = (i < tx_length) ? spi_get_word_from_buffer(tx_buffer + i, bitshift) : write_fill_frame; int out = (i < tx_length) ? spi_get_word_from_buffer(tx_buffer + i, bitshift) : write_fill_frame;
int in = spi_master_write(obj, out); int in = spi_master_write(obj, out);
@ -1534,8 +1537,8 @@ typedef enum {
} transfer_type_t; } transfer_type_t;
/// @returns the number of bytes transferred, or `0` if nothing transferred /// @returns True if DMA was used, false otherwise
static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer_type, const void *tx, void *rx, size_t length, DMAUsage hint) static bool spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer_type, const void *tx, void *rx, size_t length, DMAUsage hint)
{ {
struct spi_s *spiobj = SPI_S(obj); struct spi_s *spiobj = SPI_S(obj);
SPI_HandleTypeDef *handle = &(spiobj->handle); SPI_HandleTypeDef *handle = &(spiobj->handle);
@ -1547,7 +1550,6 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer
size_t words; size_t words;
obj->spi.transfer_type = transfer_type; obj->spi.transfer_type = transfer_type;
words = length >> bitshift; words = length >> bitshift;
bool useDMA = false; bool useDMA = false;
@ -1579,6 +1581,8 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer
useDMA = true; useDMA = true;
} }
obj->spi.curr_transfer_uses_dma = useDMA;
#endif #endif
DEBUG_PRINTF("SPI inst=0x%8X Start: type=%u, length=%u, DMA=%d\r\n", (int) handle->Instance, transfer_type, length, !!useDMA); DEBUG_PRINTF("SPI inst=0x%8X Start: type=%u, length=%u, DMA=%d\r\n", (int) handle->Instance, transfer_type, length, !!useDMA);
@ -1590,15 +1594,6 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer
NVIC_SetPriority(irq_n, 1); NVIC_SetPriority(irq_n, 1);
NVIC_EnableIRQ(irq_n); NVIC_EnableIRQ(irq_n);
#if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT)
if (useDMA && transfer_type != SPI_TRANSFER_TYPE_RX)
{
// For chips with a cache (e.g. Cortex-M7), we need to evict the Tx data from cache to main memory.
// This ensures that the DMA controller can see the most up-to-date copy of the data.
SCB_CleanDCache_by_Addr((volatile void*)tx, length);
}
#endif
// flush FIFO // flush FIFO
#if defined(SPI_FLAG_FRLVL) #if defined(SPI_FLAG_FRLVL)
HAL_SPIEx_FlushRxFifo(handle); HAL_SPIEx_FlushRxFifo(handle);
@ -1635,7 +1630,7 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer
if (useDMA) { if (useDMA) {
#if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT) #if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT)
// For chips with a cache (e.g. Cortex-M7), we need to evict the Tx data from cache to main memory. // For chips with a cache (e.g. Cortex-M7), we need to evict the Tx fill data from cache to main memory.
// This ensures that the DMA controller can see the most up-to-date copy of the data. // This ensures that the DMA controller can see the most up-to-date copy of the data.
SCB_CleanDCache_by_Addr(rx, length); SCB_CleanDCache_by_Addr(rx, length);
#endif #endif
@ -1650,15 +1645,6 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer
length = 0; length = 0;
} }
#if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT)
if (useDMA && transfer_type != SPI_TRANSFER_TYPE_TX)
{
// For chips with a cache (e.g. Cortex-M7), we need to invalidate the Rx data in cache.
// This ensures that the CPU will fetch the data from SRAM instead of using its cache.
SCB_InvalidateDCache_by_Addr(rx, length);
}
#endif
if (rc) { if (rc) {
#if defined(SPI_IP_VERSION_V2) #if defined(SPI_IP_VERSION_V2)
// enable SPI back in case of error // enable SPI back in case of error
@ -1667,14 +1653,13 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer
} }
#endif #endif
DEBUG_PRINTF("SPI: RC=%u\n", rc); DEBUG_PRINTF("SPI: RC=%u\n", rc);
length = 0;
} }
return length; return useDMA;
} }
// asynchronous API // asynchronous API
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
struct spi_s *spiobj = SPI_S(obj); struct spi_s *spiobj = SPI_S(obj);
SPI_HandleTypeDef *handle = &(spiobj->handle); SPI_HandleTypeDef *handle = &(spiobj->handle);
@ -1687,7 +1672,7 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
// don't do anything, if the buffers aren't valid // don't do anything, if the buffers aren't valid
if (!use_tx && !use_rx) { if (!use_tx && !use_rx) {
return; return false;
} }
// copy the buffers to the SPI object // copy the buffers to the SPI object
@ -1717,11 +1702,14 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
obj->tx_buff.length = size; obj->tx_buff.length = size;
obj->rx_buff.length = size; obj->rx_buff.length = size;
} }
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TXRX, tx, rx, size, hint); return spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TXRX, tx, rx, size, hint);
} else if (use_tx) { } else if (use_tx) {
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TX, tx, NULL, tx_length, hint); return spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TX, tx, NULL, tx_length, hint);
} else if (use_rx) { } else if (use_rx) {
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_RX, NULL, rx, rx_length, hint); return spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_RX, NULL, rx, rx_length, hint);
}
else {
return false;
} }
} }
@ -1830,21 +1818,10 @@ void spi_abort_asynch(spi_t *obj)
NVIC_ClearPendingIRQ(irq_n); NVIC_ClearPendingIRQ(irq_n);
NVIC_DisableIRQ(irq_n); NVIC_DisableIRQ(irq_n);
#ifdef STM32_SPI_CAPABILITY_DMA // Use HAL abort function.
// Abort DMA transfers if DMA channels have been allocated // Conveniently, this is smart enough to automatically abort the DMA transfer
if(spiobj->txDMAInitialized) { // if DMA was used.
HAL_DMA_Abort_IT(spiobj->handle.hdmatx); HAL_SPI_Abort_IT(handle);
}
if(spiobj->rxDMAInitialized) {
HAL_DMA_Abort_IT(spiobj->handle.hdmarx);
}
#endif
// clean-up
LL_SPI_Disable(SPI_INST(obj));
HAL_SPI_DeInit(handle);
init_spi(obj);
} }
#endif //DEVICE_SPI_ASYNCH #endif //DEVICE_SPI_ASYNCH

View File

@ -36,6 +36,7 @@ struct spi_s {
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
uint32_t event; uint32_t event;
uint8_t transfer_type; uint8_t transfer_type;
bool curr_transfer_uses_dma;
// Callback function for when we get an interrupt on an async transfer. // Callback function for when we get an interrupt on an async transfer.
// This will point, through a bit of indirection, to SPI::irq_handler_asynch() // This will point, through a bit of indirection, to SPI::irq_handler_asynch()

View File

@ -1116,8 +1116,10 @@ static void spi_activate_dma(spi_t *obj, void* rxdata, const void* txdata, int t
* * ALWAYS: use DMA if channels are available, and hold on to the channels after the transfer. * * ALWAYS: use DMA if channels are available, and hold on to the channels after the transfer.
* If the previous transfer has kept the channel, that channel will continue to get used. * If the previous transfer has kept the channel, that channel will continue to get used.
* *
* Returns true if DMA was used, false otherwise.
*
********************************************************************/ ********************************************************************/
void spi_master_transfer_dma(spi_t *obj, const void *txdata, void *rxdata, int tx_length, int rx_length, void* cb, DMAUsage hint) bool spi_master_transfer_dma(spi_t *obj, const void *txdata, void *rxdata, int tx_length, int rx_length, void* cb, DMAUsage hint)
{ {
/* Init DMA here to include it in the power figure */ /* Init DMA here to include it in the power figure */
dma_init(); dma_init();
@ -1128,11 +1130,13 @@ void spi_master_transfer_dma(spi_t *obj, const void *txdata, void *rxdata, int t
if (hint != DMA_USAGE_NEVER && obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED) { if (hint != DMA_USAGE_NEVER && obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED) {
/* setup has already been done, so just activate the transfer */ /* setup has already been done, so just activate the transfer */
spi_activate_dma(obj, rxdata, txdata, tx_length, rx_length); spi_activate_dma(obj, rxdata, txdata, tx_length, rx_length);
return true;
} else if (hint == DMA_USAGE_NEVER) { } else if (hint == DMA_USAGE_NEVER) {
/* use IRQ */ /* use IRQ */
obj->spi.spi->IFC = 0xFFFFFFFF; obj->spi.spi->IFC = 0xFFFFFFFF;
spi_master_write_asynch(obj); spi_master_write_asynch(obj);
spi_enable_interrupt(obj, (uint32_t)cb, true); spi_enable_interrupt(obj, (uint32_t)cb, true);
return false;
} else { } else {
/* try to acquire channels */ /* try to acquire channels */
dma_init(); dma_init();
@ -1147,11 +1151,13 @@ void spi_master_transfer_dma(spi_t *obj, const void *txdata, void *rxdata, int t
spi_master_dma_channel_setup(obj, cb); spi_master_dma_channel_setup(obj, cb);
/* and activate the transfer */ /* and activate the transfer */
spi_activate_dma(obj, rxdata, txdata, tx_length, rx_length); spi_activate_dma(obj, rxdata, txdata, tx_length, rx_length);
return true;
} else { } else {
/* DMA is unavailable, so fall back to IRQ */ /* DMA is unavailable, so fall back to IRQ */
obj->spi.spi->IFC = 0xFFFFFFFF; obj->spi.spi->IFC = 0xFFFFFFFF;
spi_master_write_asynch(obj); spi_master_write_asynch(obj);
spi_enable_interrupt(obj, (uint32_t)cb, true); spi_enable_interrupt(obj, (uint32_t)cb, true);
return false;
} }
} }
} }
@ -1167,8 +1173,11 @@ void spi_master_transfer_dma(spi_t *obj, const void *txdata, void *rxdata, int t
* @param[in] event The logical OR of events to be registered * @param[in] event The logical OR of events to be registered
* @param[in] handler SPI interrupt handler * @param[in] handler SPI interrupt handler
* @param[in] hint A suggestion for how to use DMA with this transfer * @param[in] hint A suggestion for how to use DMA with this transfer
*
* @return True if DMA was actually used for the transfer, false otherwise (if interrupts or another CPU-based
* method is used to do the transfer).
*/ */
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
if( spi_active(obj) ) return; if( spi_active(obj) ) return;
@ -1190,7 +1199,7 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_enable_event(obj, event, true); spi_enable_event(obj, event, true);
/* And kick off the transfer */ /* And kick off the transfer */
spi_master_transfer_dma(obj, tx, rx, tx_length, rx_length, (void*)handler, hint); return spi_master_transfer_dma(obj, tx, rx, tx_length, rx_length, (void*)handler, hint);
} }

View File

@ -2,6 +2,7 @@
******************************************************************************* *******************************************************************************
* (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2017 All rights reserved * (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2017 All rights reserved
* All rights reserved. * All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -409,7 +410,7 @@ const PinMap *spi_slave_cs_pinmap()
#ifdef DEVICE_SPI_ASYNCH #ifdef DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width,
uint32_t handler, uint32_t event, DMAUsage hint) uint32_t handler, uint32_t event, DMAUsage hint)
{ {
struct spi_s *obj_s = SPI_S(obj); struct spi_s *obj_s = SPI_S(obj);
@ -455,6 +456,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
SSP_Enable(spi); SSP_Enable(spi);
NVIC_EnableIRQ(obj_s->irqn); NVIC_EnableIRQ(obj_s->irqn);
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)

View File

@ -484,7 +484,7 @@ const PinMap *spi_slave_cs_pinmap()
#ifdef DEVICE_SPI_ASYNCH #ifdef DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
struct spi_s *obj_s = SPI_S(obj); struct spi_s *obj_s = SPI_S(obj);
tspi_t *p_obj = &(obj_s->p_obj); tspi_t *p_obj = &(obj_s->p_obj);
@ -535,6 +535,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
NVIC_EnableIRQ(obj_s->rxirqn); NVIC_EnableIRQ(obj_s->rxirqn);
} }
NVIC_EnableIRQ(obj_s->errirqn); NVIC_EnableIRQ(obj_s->errirqn);
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)

View File

@ -517,7 +517,7 @@ const PinMap *spi_slave_cs_pinmap()
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width,
uint32_t handler, uint32_t event, DMAUsage hint) uint32_t handler, uint32_t event, DMAUsage hint)
{ {
struct spi_s *spiobj = SPI_S(obj); struct spi_s *spiobj = SPI_S(obj);
@ -574,6 +574,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
NVIC_EnableIRQ(p_irqn->Error); NVIC_EnableIRQ(p_irqn->Error);
NVIC_EnableIRQ(p_irqn->Tx); NVIC_EnableIRQ(p_irqn->Tx);
NVIC_EnableIRQ(p_irqn->Rx); NVIC_EnableIRQ(p_irqn->Rx);
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)
{ {

View File

@ -398,7 +398,7 @@ const PinMap *spi_slave_cs_pinmap()
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width,
uint32_t handler, uint32_t event, DMAUsage hint) uint32_t handler, uint32_t event, DMAUsage hint)
{ {
struct spi_s *spiobj = SPI_S(obj); struct spi_s *spiobj = SPI_S(obj);
@ -455,6 +455,8 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
NVIC_EnableIRQ(p_irqn->Error); NVIC_EnableIRQ(p_irqn->Error);
NVIC_EnableIRQ(p_irqn->Tx); NVIC_EnableIRQ(p_irqn->Tx);
NVIC_EnableIRQ(p_irqn->Rx); NVIC_EnableIRQ(p_irqn->Rx);
return false; // Currently we always use interrupts, not DMA
} }
uint32_t spi_irq_handler_asynch(spi_t *obj) uint32_t spi_irq_handler_asynch(spi_t *obj)
{ {

View File

@ -72,14 +72,30 @@ def has_spdx_text_in_scancode_output(scancode_output_data_file_licenses):
) )
SPDX_LICENSE_REGEX = re.compile(r"SPDX-License-Identifier: *(\S+)")
def has_spdx_text_in_analysed_file(scanned_file_content): def has_spdx_text_in_analysed_file(scanned_file_content):
"""Returns true if the file analysed by ScanCode contains SPDX identifier.""" """Returns true if the file analysed by ScanCode contains SPDX identifier."""
return bool(re.findall("SPDX-License-Identifier:?", scanned_file_content)) return bool(SPDX_LICENSE_REGEX.findall(scanned_file_content))
def has_exempted_spdx_identifier(scanned_file_content):
def has_binary_license(scanned_file_content): """
"""Returns true if the file analysed by ScanCode contains a Permissive Binary License.""" Returns true if the file analysed by scancode contains an exempted SPDX identifier.
return bool(re.findall("Permissive Binary License", scanned_file_content)) (this is used for licenses like Nordic BSD which are not technically permissive licenses according
to SPDX but which are still OK for us)
"""
spdx_find_result = SPDX_LICENSE_REGEX.findall(scanned_file_content)
if len(spdx_find_result) == 1:
if spdx_find_result[0] == "LicenseRef-Nordic-5-Clause":
return True
if spdx_find_result[0] == "LicenseRef-PBL":
return True
else:
return False
else:
# Either 0 or multiple matches, ignore
return False
def get_file_text(scancode_output_data_file): def get_file_text(scancode_output_data_file):
@ -94,6 +110,7 @@ def get_file_text(scancode_output_data_file):
except UnicodeDecodeError: except UnicodeDecodeError:
userlog.warning("Unable to decode file text in: %s" % file_path) userlog.warning("Unable to decode file text in: %s" % file_path)
# Ignore files that cannot be decoded # Ignore files that cannot be decoded
return None
def license_check(scancode_output_path): def license_check(scancode_output_path):
@ -135,7 +152,10 @@ def license_check(scancode_output_path):
if not has_permissive_text_in_scancode_output(scancode_output_data_file['licenses']): if not has_permissive_text_in_scancode_output(scancode_output_data_file['licenses']):
scanned_file_content = get_file_text(scancode_output_data_file) scanned_file_content = get_file_text(scancode_output_data_file)
if not (scanned_file_content and has_binary_license(scanned_file_content)): if (scanned_file_content is None
or has_exempted_spdx_identifier(scanned_file_content)):
continue
else:
scancode_output_data_file['fail_reason'] = MISSING_PERMISSIVE_LICENSE_TEXT scancode_output_data_file['fail_reason'] = MISSING_PERMISSIVE_LICENSE_TEXT
license_offenders.append(scancode_output_data_file) license_offenders.append(scancode_output_data_file)