Merge branch 'feat/spi_check_length_buffer' into 'master'

spi: add checks for slave buffer valid and master length

See merge request idf/esp-idf!3744
This commit is contained in:
Jeroen Domburg 2018-12-04 11:48:32 +08:00
commit d6ee27e313
6 changed files with 48 additions and 20 deletions

View 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.
};

View File

@ -1103,6 +1103,14 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG);
SPI_CHECK( !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || host->dma_chan == 0 || !(trans_desc->flags & SPI_TRANS_USE_RXDATA || trans_desc->rx_buffer != NULL)
|| !(trans_desc->flags & SPI_TRANS_USE_TXDATA || trans_desc->tx_buffer!=NULL), "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG );
//MOSI phase is skipped only when both tx_buffer and SPI_TRANS_USE_TXDATA are not set.
SPI_CHECK(trans_desc->length != 0 || (trans_desc->tx_buffer == NULL && !(trans_desc->flags & SPI_TRANS_USE_TXDATA)),
"trans tx_buffer should be NULL and SPI_TRANS_USE_TXDATA should be cleared to skip MOSI phase.", ESP_ERR_INVALID_ARG);
//MISO phase is skipped only when both rx_buffer and SPI_TRANS_USE_RXDATA are not set.
//If set rxlength=0 in full_duplex mode, it will be automatically set to length
SPI_CHECK(!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || trans_desc->rxlength != 0 ||
(trans_desc->rx_buffer == NULL && ((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0)),
"trans rx_buffer should be NULL and SPI_TRANS_USE_RXDATA should be cleared to skip MISO phase.", ESP_ERR_INVALID_ARG);
//In Full duplex mode, default rxlength to be the same as length, if not filled in.
// set rxlength to length is ok, even when rx buffer=NULL
if (trans_desc->rxlength==0 && !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX)) {

View 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);

View 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 ) );

View 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;

View File

@ -85,6 +85,16 @@ Warning: Due to a design peculiarity in the ESP32, if the amount of bytes sent b
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
-------------------