]> granicus.if.org Git - esp-idf/commitdiff
spi_slave: add valid check for DMA buffers
authormichael <xiaoxufeng@espressif.com>
Tue, 20 Nov 2018 03:13:44 +0000 (11:13 +0800)
committerbot <bot@espressif.com>
Mon, 26 Nov 2018 03:49:26 +0000 (03:49 +0000)
The DMA cannot receive data correctly when the buffer address is not
WORD aligned. Currently we only check whether the buffer is in the DRAM
region.

The DMA always write in WORDs, so the length arguments should also be
multiples of 32 bits.

A check is added to see whether the buffer is WORD aligned and has valid
length.

components/driver/include/driver/spi_slave.h
components/driver/spi_slave.c
components/driver/test/test_spi_slave.c
components/soc/include/soc/soc_memory_layout.h
docs/en/api-reference/peripherals/spi_slave.rst

index e0837bd0b6e50d349b4fde742931c529387c4a1a..522c9fb8ab0cfc6870463b7547b07253264c5ea8 100644 (file)
@@ -73,7 +73,10 @@ struct spi_slave_transaction_t {
     size_t length;                  ///< Total data length, in bits
     size_t trans_len;               ///< Transaction data length, in bits
     const void *tx_buffer;          ///< Pointer to transmit buffer, or NULL for no MOSI phase
-    void *rx_buffer;                ///< Pointer to receive buffer, or NULL for no MISO phase
+    void *rx_buffer;                /**< Pointer to receive buffer, or NULL for no MISO phase.
+                                     * When the DMA is anabled, must start at WORD boundary (``rx_buffer%4==0``),
+                                     * and has length of a multiple of 4 bytes.
+                                     */
     void *user;                     ///< User-defined variable. Can be used to store eg transaction ID.
 };
 
index 363234541462a2a053c76915af44b0d18336748e..31606ac2af07823304df6d77155536b8d7bfc9c8 100644 (file)
@@ -298,8 +298,10 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi
     SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
     SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->tx_buffer==NULL || esp_ptr_dma_capable(trans_desc->tx_buffer),
                        "txdata not in DMA-capable memory", ESP_ERR_INVALID_ARG);
-    SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->rx_buffer==NULL || esp_ptr_dma_capable(trans_desc->rx_buffer),
-                       "rxdata not in DMA-capable memory", ESP_ERR_INVALID_ARG);
+    SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->rx_buffer==NULL ||
+        (esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) &&
+            (trans_desc->length%4==0)),
+        "rxdata not in DMA-capable memory or not WORD aligned", ESP_ERR_INVALID_ARG);
 
     SPI_CHECK(trans_desc->length <= spihost[host]->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
     r = xQueueSend(spihost[host]->trans_queue, (void *)&trans_desc, ticks_to_wait);
index b38eaa1bc5c9c4af00370fb362597bb0c78cc70d..c468973faf41fc86a1734d7571813b968e52ad82 100644 (file)
@@ -78,12 +78,12 @@ static void slave_init()
     TEST_ESP_OK( spi_slave_initialize(VSPI_HOST, &buscfg, &slvcfg, 2) );
 }
 
-TEST_CASE("test slave startup","[spi]")
+TEST_CASE("test slave send unaligned","[spi]")
 {
-    uint8_t master_txbuf[320]=MASTER_SEND;
-    uint8_t master_rxbuf[320];
-    uint8_t slave_txbuf[320]=SLAVE_SEND;
-    uint8_t slave_rxbuf[320];
+    WORD_ALIGNED_ATTR uint8_t master_txbuf[320]=MASTER_SEND;
+    WORD_ALIGNED_ATTR uint8_t master_rxbuf[320];
+    WORD_ALIGNED_ATTR uint8_t slave_txbuf[320]=SLAVE_SEND;
+    WORD_ALIGNED_ATTR uint8_t slave_rxbuf[320];
 
     spi_device_handle_t spi;
     //initial master
@@ -97,13 +97,13 @@ TEST_CASE("test slave startup","[spi]")
     int_connect( PIN_NUM_CS,    HSPICS0_OUT_IDX, VSPICS0_IN_IDX );
     int_connect( PIN_NUM_CLK,   HSPICLK_OUT_IDX, VSPICLK_IN_IDX );
 
-    for ( int i = 0; i < 3; i ++ ) {
+    for ( int i = 0; i < 4; i ++ ) {
         //slave send
         spi_slave_transaction_t slave_t;
         spi_slave_transaction_t* out;
         memset(&slave_t, 0, sizeof(spi_slave_transaction_t));
         slave_t.length=8*32;
-        slave_t.tx_buffer=slave_txbuf+2*i;
+        slave_t.tx_buffer=slave_txbuf+i;
         slave_t.rx_buffer=slave_rxbuf;
         TEST_ESP_OK( spi_slave_queue_trans( VSPI_HOST, &slave_t, portMAX_DELAY ) );
 
index 68c87623c3d73e3ef37c073670a620cb5d5276fe..ddc0fa1b21a0f9cad5380e90dc5f798efd02fc2b 100644 (file)
@@ -144,6 +144,11 @@ inline static bool IRAM_ATTR esp_ptr_dma_capable(const void *p)
     return (intptr_t)p >= SOC_DMA_LOW && (intptr_t)p < SOC_DMA_HIGH;
 }
 
+inline static bool IRAM_ATTR esp_ptr_word_aligned(const void *p)
+{
+    return ((intptr_t)p) % 4 == 0;
+}
+
 inline static bool IRAM_ATTR esp_ptr_executable(const void *p)
 {
     intptr_t ip = (intptr_t) p;
index 727338ffa1f21ded45b9b731c2954508d2bfda15..494ed09991fa2a453f9f52e18fa8172700fa122b 100644 (file)
@@ -7,7 +7,7 @@ Overview
 The ESP32 has four SPI peripheral devices, called SPI0, SPI1, HSPI and VSPI. SPI0 is entirely dedicated to
 the flash cache the ESP32 uses to map the SPI flash device it is connected to into memory. SPI1 is
 connected to the same hardware lines as SPI0 and is used to write to the flash chip. HSPI and VSPI
-are free to use, and with the spi_slave driver, these can be used as a SPI slave, driven from a 
+are free to use, and with the spi_slave driver, these can be used as a SPI slave, driven from a
 connected SPI master.
 
 The spi_slave driver
@@ -21,9 +21,9 @@ Terminology
 
 The spi_slave driver uses the following terms:
 
-* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of HSPI or VSPI. 
+* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of HSPI or VSPI.
 * Bus: The SPI bus, common to all SPI devices connected to a master. In general the bus consists of the
-  miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these 
+  miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these
   signals in parallel. Each  SPI slave is also connected to one CS signal.
 
   - miso - Also known as q, this is the output of the serial stream from the ESP32 to the SPI master
@@ -50,15 +50,15 @@ the master makes CS high again.
 Using the spi_slave driver
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-- Initialize a SPI peripheral as a slave by calling ``spi_slave_initialize``. Make sure to set the 
+- Initialize a SPI peripheral as a slave by calling ``spi_slave_initialize``. Make sure to set the
   correct IO pins in the ``bus_config`` struct. Take care to set signals that are not needed to -1.
   A DMA channel (either 1 or 2) must be given if transactions will be larger than 32 bytes, if not
   the dma_chan parameter may be 0.
 
-- To set up a transaction, fill one or more spi_transaction_t structure with any transaction 
+- To set up a transaction, fill one or more spi_transaction_t structure with any transaction
   parameters you need. Either queue all transactions by calling ``spi_slave_queue_trans``, later
   quering the result using ``spi_slave_get_trans_result``, or handle all requests synchroneously
-  by feeding them into ``spi_slave_transmit``. The latter two  functions will block until the 
+  by feeding them into ``spi_slave_transmit``. The latter two  functions will block until the
   master has initiated and finished a transaction, causing the queued data to be sent and received.
 
 - Optional: to unload the SPI slave driver, call ``spi_slave_free``.
@@ -69,7 +69,7 @@ Transaction data and master/slave length mismatches
 
 Normally, data to be transferred to or from a device will be read from or written to a chunk of memory
 indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure. The SPI driver
-may decide to use DMA for transfers, so these buffers should be allocated in DMA-capable memory using 
+may decide to use DMA for transfers, so these buffers should be allocated in DMA-capable memory using
 ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``.
 
 The amount of data written to the buffers is limited by the ``length`` member of the transaction structure:
@@ -78,13 +78,23 @@ length of the SPI transaction; this is determined by the master as it drives the
 transferred can be read from the ``trans_len`` member of the ``spi_slave_transaction_t`` structure after transaction.
 In case the length of the transmission is larger than the buffer length, only the start of the transmission
 will be sent and received, and the ``trans_len`` is set to ``length`` instead of the actual length. It's recommended to
-set ``length`` longer than the maximum length expected if the ``trans_len`` is required.  In case the transmission 
+set ``length`` longer than the maximum length expected if the ``trans_len`` is required.  In case the transmission
 length is shorter than the buffer length, only data up to the length of the buffer will be exchanged.
 
-Warning: Due to a design peculiarity in the ESP32, if the amount of bytes sent by the master or the length 
-of the transmission queues in the slave driver, in bytes, is not both larger than eight and dividable by 
+Warning: Due to a design peculiarity in the ESP32, if the amount of bytes sent by the master or the length
+of the transmission queues in the slave driver, in bytes, is not both larger than eight and dividable by
 four, the SPI hardware can fail to write the last one to seven bytes to the receive buffer.
 
+Restrictions and Known issues
+-------------------------------
+
+1. If the DMA is enabled, the rx buffer should be WORD aligned, i.e. Start
+   from the boundary of 32-bit and have length of multiples of 4 bytes. Or the
+   DMA may write incorrectly or out of the boundary.The driver will check for
+   this.
+
+   Also, master should write lengths which are a multiple of 4 bytes. Data
+   longer than that will be discarded.
 
 Application Example
 -------------------