]> granicus.if.org Git - esp-idf/commitdiff
feat(spi_master): fine tune the timing of SPI
authormichael <xiaoxufeng@espressif.com>
Thu, 28 Sep 2017 12:19:18 +0000 (20:19 +0800)
committerbot <bot@espressif.com>
Wed, 6 Jun 2018 06:08:39 +0000 (06:08 +0000)
13 files changed:
components/driver/include/driver/spi_master.h
components/driver/spi_master.c
components/soc/esp32/include/driver/spi_pins.h [new file with mode: 0644]
docs/_static/diagrams/spi_master/miso_timing_waveform.rst [new file with mode: 0644]
docs/_static/diagrams/spi_master/miso_timing_waveform_async.rst [new file with mode: 0644]
docs/_static/diagrams/spi_master/spi_master_freq_tv.plt [new file with mode: 0644]
docs/_static/diagrams/spi_master/spi_timing.pptx [new file with mode: 0644]
docs/_static/diagrams/spi_master/tv.csv [new file with mode: 0644]
docs/_static/miso_timing_waveform.png [new file with mode: 0644]
docs/_static/miso_timing_waveform_async.png [new file with mode: 0644]
docs/_static/spi_master_freq_tv.png [new file with mode: 0644]
docs/_static/spi_miso.png [new file with mode: 0644]
docs/en/api-reference/peripherals/spi_master.rst

index 232ad36eb7ed4c1a470b0b01ca85262dc0c2e43a..a882336941d1b6f0f82b318372ce181933af9833 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2010-2018 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 
 #include "driver/spi_common.h"
 
+/** SPI master clock is divided by 80MHz apb clock. Below defines are example frequencies, and are accurate. Be free to specify a random frequency, it will be rounded to closest frequency (to macros below if above 8MHz).
+  * 8MHz
+  */
+#define SPI_MASTER_FREQ_8M      (APB_CLK_FREQ/10)
+#define SPI_MASTER_FREQ_9M      (APB_CLK_FREQ/9)    ///< 8.89MHz
+#define SPI_MASTER_FREQ_10M     (APB_CLK_FREQ/8)    ///< 10MHz
+#define SPI_MASTER_FREQ_11M     (APB_CLK_FREQ/7)    ///< 11.43MHz
+#define SPI_MASTER_FREQ_13M     (APB_CLK_FREQ/6)    ///< 13.33MHz
+#define SPI_MASTER_FREQ_16M     (APB_CLK_FREQ/5)    ///< 16MHz
+#define SPI_MASTER_FREQ_20M     (APB_CLK_FREQ/4)    ///< 20MHz
+#define SPI_MASTER_FREQ_26M     (APB_CLK_FREQ/3)    ///< 26.67MHz
+#define SPI_MASTER_FREQ_40M     (APB_CLK_FREQ/2)    ///< 40MHz
+#define SPI_MASTER_FREQ_80M     (APB_CLK_FREQ/1)    ///< 80MHz
 
 #ifdef __cplusplus
 extern "C"
@@ -40,7 +53,7 @@ extern "C"
   *     - In full-duplex mode, however, the hardware cannot use dummy bits, so there is no way to prevent data being read from getting corrupted.
   *       Set this flag to confirm that you're going to work with output only, or read without dummy bits at your own risk.
   */
-#define SPI_DEVICE_NO_DUMMY                (1<<6)  
+#define SPI_DEVICE_NO_DUMMY                (1<<6)
 
 
 typedef struct spi_transaction_t spi_transaction_t;
@@ -57,7 +70,12 @@ typedef struct {
     uint8_t duty_cycle_pos;         ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.
     uint8_t cs_ena_pretrans;        ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.
     uint8_t cs_ena_posttrans;       ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)
-    int clock_speed_hz;             ///< Clock speed, in Hz
+    int clock_speed_hz;             ///< Clock speed, divisors of 80MHz, in Hz. See ``SPI_MASTER_FREQ_*``.
+    int input_delay_ns;             /**< Maximum data valid time of slave. The time required between SCLK and MISO 
+        valid, including the possible clock delay from slave to master. The driver uses this value to give an extra 
+        delay before the MISO is ready on the line. Leave at 0 unless you know you need a delay. For better timing 
+        performance at high frequency (over 8MHz), it's suggest to have the right value.
+        */
     int spics_io_num;               ///< CS GPIO pin for this device, or -1 if not used
     uint32_t flags;                 ///< Bitwise OR of SPI_DEVICE_* flags
     int queue_size;                 ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same time
@@ -87,9 +105,9 @@ struct spi_transaction_t {
                                       */
     uint64_t addr;                  /**< Address data, of which the length is set in the ``address_bits`` of spi_device_interface_config_t.
                                       *
-                                      *  <b>NOTE: this field, used to be "address" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF3.0.</b> 
+                                      *  <b>NOTE: this field, used to be "address" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF3.0.</b>
                                       *
-                                      *  Example: write 0x123400 and address_bits=24 to send address of 0x12, 0x34, 0x00 (in previous version, you may have to write 0x12340000).  
+                                      *  Example: write 0x123400 and address_bits=24 to send address of 0x12, 0x34, 0x00 (in previous version, you may have to write 0x12340000).
                                       */
     size_t length;                  ///< Total data length, in bits
     size_t rxlength;                ///< Total data length received, should be not greater than ``length`` in full-duplex mode (0 defaults this to the value of ``length``).
@@ -125,14 +143,14 @@ typedef struct spi_device_t* spi_device_handle_t;  ///< Handle for a device on a
  * @param host SPI peripheral that controls this bus
  * @param bus_config Pointer to a spi_bus_config_t struct specifying how the host should be initialized
  * @param dma_chan Either channel 1 or 2, or 0 in the case when no DMA is required. Selecting a DMA channel
- *                 for a SPI bus allows transfers on the bus to have sizes only limited by the amount of 
+ *                 for a SPI bus allows transfers on the bus to have sizes only limited by the amount of
  *                 internal memory. Selecting no DMA channel (by passing the value 0) limits the amount of
  *                 bytes transfered to a maximum of 32.
  *
- * @warning If a DMA channel is selected, any transmit and receive buffer used should be allocated in 
+ * @warning If a DMA channel is selected, any transmit and receive buffer used should be allocated in
  *          DMA-capable memory.
  *
- * @return 
+ * @return
  *         - ESP_ERR_INVALID_ARG   if configuration is invalid
  *         - ESP_ERR_INVALID_STATE if host already is in use
  *         - ESP_ERR_NO_MEM        if out of memory
@@ -146,7 +164,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
  * @warning In order for this to succeed, all devices have to be removed first.
  *
  * @param host SPI peripheral to free
- * @return 
+ * @return
  *         - ESP_ERR_INVALID_ARG   if parameter is invalid
  *         - ESP_ERR_INVALID_STATE if not all devices on the bus are freed
  *         - ESP_OK                on success
@@ -166,12 +184,12 @@ esp_err_t spi_bus_free(spi_host_device_t host);
  * @param host SPI peripheral to allocate device on
  * @param dev_config SPI interface protocol config for the device
  * @param handle Pointer to variable to hold the device handle
- * @return 
+ * @return
  *         - ESP_ERR_INVALID_ARG   if parameter is invalid
  *         - ESP_ERR_NOT_FOUND     if host doesn't have any free CS slots
  *         - ESP_ERR_NO_MEM        if out of memory
  *         - ESP_OK                on success
- */ 
+ */
 esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_config_t *dev_config, spi_device_handle_t *handle);
 
 
@@ -179,7 +197,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
  * @brief Remove a device from the SPI bus
  *
  * @param handle Device handle to free
- * @return 
+ * @return
  *         - ESP_ERR_INVALID_ARG   if parameter is invalid
  *         - ESP_ERR_INVALID_STATE if device already is freed
  *         - ESP_OK                on success
@@ -206,18 +224,18 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
 /**
  * @brief Get the result of a SPI transaction queued earlier
  *
- * This routine will wait until a transaction to the given device (queued earlier with 
+ * This routine will wait until a transaction to the given device (queued earlier with
  * spi_device_queue_trans) has succesfully completed. It will then return the description of the
- * completed transaction so software can inspect the result and e.g. free the memory or 
+ * completed transaction so software can inspect the result and e.g. free the memory or
  * re-use the buffers.
  *
  * @param handle Device handle obtained using spi_host_add_dev
- * @param trans_desc Pointer to variable able to contain a pointer to the description of the transaction 
-        that is executed. The descriptor should not be modified until the descriptor is returned by 
+ * @param trans_desc Pointer to variable able to contain a pointer to the description of the transaction
+        that is executed. The descriptor should not be modified until the descriptor is returned by
         spi_device_get_trans_result.
  * @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time
                         out.
- * @return 
+ * @return
  *         - ESP_ERR_INVALID_ARG   if parameter is invalid
  *         - ESP_ERR_TIMEOUT       if there was no completed transaction before ticks_to_wait expired
  *         - ESP_OK                on success
@@ -253,6 +271,30 @@ esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *tra
  */
 int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t* reg_o);
 
+/**
+  * @brief Calculate the timing settings of specified frequency and settings.
+  *
+  * @param gpio_is_used True if using GPIO matrix, or False if native pins are used.
+  * @param input_delay_ns Input delay from SCLK launch edge to MISO data valid.
+  * @param eff_clk Effective clock frequency (in Hz) from spi_cal_clock.
+  * @param dummy_o Address of dummy bits used output. Set to NULL if not needed.
+  * @param cycles_remain_o Address of cycles remaining (after dummy bits are used) output.
+  *         - -1 If too many cycles remaining, suggest to compensate half a clock.
+  *         - 0 If no remaining cycles or dummy bits are not used.
+  *         - positive value: cycles suggest to compensate.
+  * @note If **dummy_o* is not zero, it means dummy bits should be applied in half duplex mode, and full duplex mode may not work.
+  */
+void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o);
+
+/**
+  * @brief Get the frequency limit of current configurations.
+  *         SPI master working at this limit is OK, while above the limit, full duplex mode and DMA will not work,
+  *         and dummy bits will be aplied in the half duplex mode.
+  * @param gpio_is_used True if using GPIO matrix, or False if native pins are used.
+  * @param input_delay_ns Input delay from SCLK launch edge to MISO data valid.
+  * @return Frequency limit of current configurations.
+  */
+int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns);
 
 #ifdef __cplusplus
 }
index 4e6a51a5e54e50cddf476c7dc92818b8df3676fb..e070ba9c6603061816a626f7d736b319145df8e1 100644 (file)
@@ -21,13 +21,13 @@ is a combination of SPI port and CS pin, plus some information about the specifi
 
 The essence of the interface to a device is a set of queues; one per device. The idea is that to send something to a SPI
 device, you allocate a transaction descriptor. It contains some information about the transfer like the lenghth, address,
-command etc, plus pointers to transmit and receive buffer. The address of this block gets pushed into the transmit queue. 
-The SPI driver does its magic, and sends and retrieves the data eventually. The data gets written to the receive buffers, 
+command etc, plus pointers to transmit and receive buffer. The address of this block gets pushed into the transmit queue.
+The SPI driver does its magic, and sends and retrieves the data eventually. The data gets written to the receive buffers,
 if needed the transaction descriptor is modified to indicate returned parameters and the entire thing goes into the return
 queue, where whatever software initiated the transaction can retrieve it.
 
-The entire thing is run from the SPI interrupt handler. If SPI is done transmitting/receiving but nothing is in the queue, 
-it will not clear the SPI interrupt but just disable it. This way, when a new thing is sent, pushing the packet into the send 
+The entire thing is run from the SPI interrupt handler. If SPI is done transmitting/receiving but nothing is in the queue,
+it will not clear the SPI interrupt but just disable it. This way, when a new thing is sent, pushing the packet into the send
 queue and re-enabling the interrupt will trigger the interrupt again, which can then take care of the sending.
 */
 
@@ -68,8 +68,8 @@ typedef typeof(SPI1.clock) spi_clock_reg_t;
 
 
 /// struct to hold private transaction data (like tx and rx buffer for DMA).
-typedef struct {        
-    spi_transaction_t   *trans; 
+typedef struct {
+    spi_transaction_t   *trans;
     uint32_t *buffer_to_send;   //equals to tx_data, if SPI_TRANS_USE_RXDATA is applied; otherwise if original buffer wasn't in DMA-capable memory, this gets the address of a temporary buffer that is;
                                 //otherwise sets to the original buffer or NULL if no buffer is assigned.
     uint32_t *buffer_to_rcv;    // similar to buffer_to_send
@@ -96,6 +96,7 @@ typedef struct {
     spi_clock_reg_t reg;
     int eff_clk;
     int dummy_num;
+    int miso_delay;
 } clock_config_t;
 
 struct spi_device_t {
@@ -110,9 +111,9 @@ static spi_host_t *spihost[3];
 
 
 static const char *SPI_TAG = "spi_master";
-#define SPI_CHECK(a, str, ret_val) \
+#define SPI_CHECK(a, str, ret_val, ...) \
     if (!(a)) { \
-        ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
+        ESP_LOGE(SPI_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
         return (ret_val); \
     }
 
@@ -177,7 +178,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
             goto cleanup;
         }
     }
-    
+
     err = esp_intr_alloc(spicommon_irqsource_for_host(host), ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void*)spihost[host], &spihost[host]->intr);
     if (err != ESP_OK) {
         ret = err;
@@ -207,7 +208,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
     spihost[host]->hw->slave.wr_sta_inten=0;
 
     //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
-    //disabled.  This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling 
+    //disabled.  This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
     //any transactions that are queued.
     spihost[host]->hw->slave.trans_inten=1;
     spihost[host]->hw->slave.trans_done=1;
@@ -256,14 +257,38 @@ esp_err_t spi_bus_free(spi_host_device_t host)
     return ESP_OK;
 }
 
-static inline uint32_t spi_dummy_limit(bool gpio_is_used)
+void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o)
 {
-    const int apbclk=APB_CLK_FREQ;
-    if (!gpio_is_used) {
-        return apbclk;  //dummy bit workaround is not used when native pins are used
+    const int apbclk_kHz = APB_CLK_FREQ/1000;
+    const int apbclk_n = APB_CLK_FREQ/eff_clk;
+    const int gpio_delay_ns=(gpio_is_used?25:0);
+
+    //calculate how many apb clocks a period has, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
+    int apb_period_n = (1 + input_delay_ns + gpio_delay_ns)*apbclk_kHz/1000/1000;
+    int dummy_required = apb_period_n/apbclk_n;
+
+    int miso_delay = 0;
+    if (dummy_required > 0) {
+        //due to the clock delay between master and slave, there's a range in which data is random
+        //give MISO a delay if needed to make sure we sample at the time MISO is stable
+        miso_delay = (dummy_required+1)*apbclk_n-apb_period_n-1;
     } else {
-        return apbclk/2;  //the dummy bit workaround is used when freq is 40MHz and GPIO matrix is used.
+        //if the dummy is not required, maybe we should also delay half a SPI clock if the data comes too early
+        if (apb_period_n*4 <= apbclk_n) miso_delay = -1;
     }
+    if (dummy_o!=NULL) *dummy_o = dummy_required;
+    if (cycles_remain_o!=NULL) *cycles_remain_o = miso_delay;
+    ESP_LOGD(SPI_TAG,"eff: %d, limit: %dk(/%d), %d dummy, %d delay", eff_clk/1000, apbclk_kHz/(apb_period_n+1), apb_period_n, dummy_required, miso_delay);
+}
+
+int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)
+{
+    const int apbclk_kHz = APB_CLK_FREQ/1000;
+    const int gpio_delay_ns=(gpio_is_used?25:0);
+
+    //calculate how many apb clocks a period has, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
+    int apb_period_n = (1 + input_delay_ns + gpio_delay_ns)*apbclk_kHz/1000/1000;
+    return APB_CLK_FREQ/(apb_period_n+1);
 }
 
 /*
@@ -276,6 +301,9 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
     int apbclk=APB_CLK_FREQ;
     int eff_clk;
     int duty_cycle;
+    int dummy_required;
+    int miso_delay;
+
     spi_clock_reg_t clk_reg;
     SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
     SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE);
@@ -288,18 +316,23 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
     SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND);
     //The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
     //duplex mode does absolutely nothing on the ESP32.
-    SPI_CHECK(dev_config->cs_ena_pretrans==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG);
-    
-    //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
+    SPI_CHECK(dev_config->cs_ena_pretrans <= 1 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay > 1 incompatible with full-duplex", ESP_ERR_INVALID_ARG);
+    SPI_CHECK( dev_config->cs_ena_pretrans != 1 || (dev_config->address_bits == 0 && dev_config->command_bits == 0) ||
+        (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "In full-duplex mode, only support cs pretrans delay = 1 and without address_bits and command_bits", ESP_ERR_INVALID_ARG);
+
     duty_cycle = (dev_config->duty_cycle_pos==0? 128: dev_config->duty_cycle_pos);
-    eff_clk = spi_cal_clock(apbclk, dev_config->clock_speed_hz, duty_cycle, (uint32_t*)&clk_reg);    
-    uint32_t dummy_limit = spi_dummy_limit(!(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS));
-    SPI_CHECK( dev_config->flags & SPI_DEVICE_HALFDUPLEX || (eff_clk/1000/1000) < (dummy_limit/1000/1000) ||
+    eff_clk = spi_cal_clock(apbclk, dev_config->clock_speed_hz, duty_cycle, (uint32_t*)&clk_reg);
+    int freq_limit = spi_get_freq_limit(!(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS), dev_config->input_delay_ns);
+    //GPIO matrix can only change data at 80Mhz rate, which only allows 40MHz SPI clock.
+    SPI_CHECK(eff_clk <= 40*1000*1000 || spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS, "80MHz only supported on native pins", ESP_ERR_INVALID_ARG);
+    //Speed >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
+    spi_get_timing(!(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS), dev_config->input_delay_ns, eff_clk, &dummy_required, &miso_delay);
+    SPI_CHECK( dev_config->flags & SPI_DEVICE_HALFDUPLEX || dummy_required == 0 ||
             dev_config->flags & SPI_DEVICE_NO_DUMMY,
-"When GPIO matrix is used in full-duplex mode at frequency > 26MHz, device cannot read correct data.\n\
+"When GPIO matrix is used in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\
 Please note the SPI can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\
-Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.", 
-            ESP_ERR_INVALID_ARG );
+Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.",
+            ESP_ERR_INVALID_ARG, freq_limit/1000./1000 );
 
     //Allocate memory for device
     spi_device_t *dev=malloc(sizeof(spi_device_t));
@@ -310,17 +343,18 @@ Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output dat
     //Allocate queues, set defaults
     dev->trans_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv));
     dev->ret_queue=xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv));
-    if (!dev->trans_queue || !dev->ret_queue) goto nomem;    
+    if (!dev->trans_queue || !dev->ret_queue) goto nomem;
     dev->host=spihost[host];
 
     //We want to save a copy of the dev config in the dev struct.
     memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
     dev->cfg.duty_cycle_pos = duty_cycle;
-    // TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired.    
+    // TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired.
     dev->clk_cfg= (clock_config_t) {
         .eff_clk = eff_clk,
-        .dummy_num = (dev->clk_cfg.eff_clk >= dummy_limit? 1: 0),
+        .dummy_num = dummy_required,
         .reg = clk_reg,
+        .miso_delay = miso_delay,
     };
 
     //Set CS pin, CS options
@@ -338,6 +372,8 @@ Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output dat
     } else {
         spihost[host]->hw->pin.master_cs_pol &= (1<<freecs);
     }
+    spihost[host]->hw->ctrl2.mosi_delay_mode = 0;
+    spihost[host]->hw->ctrl2.mosi_delay_num = 0;
     *handle=dev;
     ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host, freecs, dev->clk_cfg.eff_clk/1000);
     return ESP_OK;
@@ -460,7 +496,7 @@ static void IRAM_ATTR spi_intr(void *arg)
     /*------------ deal with the in-flight transaction -----------------*/
     if (host->cur_cs != NO_CS) {
         spi_transaction_t *cur_trans = host->cur_trans_buf.trans;
-        //Okay, transaction is done. 
+        //Okay, transaction is done.
         if (host->cur_trans_buf.buffer_to_rcv && host->dma_chan == 0 ) {
             //Need to copy from SPI regs to result buffer.
             for (int x=0; x < cur_trans->rxlength; x+=32) {
@@ -474,7 +510,7 @@ static void IRAM_ATTR spi_intr(void *arg)
         //Call post-transaction callback, if any
         if (host->device[host->cur_cs]->cfg.post_cb) host->device[host->cur_cs]->cfg.post_cb(cur_trans);
         //Return transaction descriptor.
-        xQueueSendFromISR(host->device[host->cur_cs]->ret_queue, &host->cur_trans_buf, &do_yield); 
+        xQueueSendFromISR(host->device[host->cur_cs]->ret_queue, &host->cur_trans_buf, &do_yield);
         host->cur_cs = NO_CS;
     }
     //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
@@ -505,46 +541,27 @@ static void IRAM_ATTR spi_intr(void *arg)
         host->cur_cs=i;
         //We should be done with the transmission.
         assert(host->hw->cmd.usr == 0);
-        
+
         //Reconfigure according to device settings, but only if we change CSses.
         if (i!=host->prev_cs) {
-            const int apbclk=APB_CLK_FREQ;
-            int effclk=dev->clk_cfg.eff_clk;
             spi_set_clock(host->hw, dev->clk_cfg.reg);
             //Configure bit order
             host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0;
             host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0;
-            
-            //Configure polarity
-            //SPI iface needs to be configured for a delay in some cases.
-            int nodelay=0;
-            if ((host->flags&SPICOMMON_BUSFLAG_NATIVE_PINS)!=0) {
-                if (effclk >= apbclk/2) {
-                    nodelay=1;
-                }
-            } else {
-                uint32_t delay_limit = apbclk/4;
-                if (effclk >= delay_limit) {
-                    nodelay=1;
-                }
-            }
 
+            //Configure polarity
             if (dev->cfg.mode==0) {
                 host->hw->pin.ck_idle_edge=0;
                 host->hw->user.ck_out_edge=0;
-                host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
             } else if (dev->cfg.mode==1) {
                 host->hw->pin.ck_idle_edge=0;
                 host->hw->user.ck_out_edge=1;
-                host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
             } else if (dev->cfg.mode==2) {
                 host->hw->pin.ck_idle_edge=1;
                 host->hw->user.ck_out_edge=1;
-                host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
             } else if (dev->cfg.mode==3) {
                 host->hw->pin.ck_idle_edge=1;
                 host->hw->user.ck_out_edge=0;
-                host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
             }
             //Configure misc stuff
             host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1;
@@ -552,8 +569,11 @@ static void IRAM_ATTR spi_intr(void *arg)
 
             host->hw->ctrl2.setup_time=dev->cfg.cs_ena_pretrans-1;
             host->hw->user.cs_setup=dev->cfg.cs_ena_pretrans?1:0;
-            host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans-1;
-            host->hw->user.cs_hold=(dev->cfg.cs_ena_posttrans)?1:0;
+            //set hold_time to 0 will not actually append delay to CS
+            //set it to 1 since we do need at least one clock of hold time in most cases
+            host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans;
+            if ( host->hw->ctrl2.hold_time == 0 ) host->hw->ctrl2.hold_time = 1;
+            host->hw->user.cs_hold=1;
 
             //Configure CS pin
             host->hw->pin.cs0_dis=(i==0)?0:1;
@@ -607,13 +627,14 @@ static void IRAM_ATTR spi_intr(void *arg)
                 extra_dummy=dev->clk_cfg.dummy_num;
             }
         } else {
-            //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon 
+            //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
             if (host->dma_chan != 0 ) {
                 host->hw->dma_in_link.addr=0;
                 host->hw->dma_in_link.start=1;
             }
         }
 
+
         if (trans_buf->buffer_to_send) {
             if (host->dma_chan == 0) {
                 //Need to copy data to registers manually
@@ -634,10 +655,31 @@ static void IRAM_ATTR spi_intr(void *arg)
             }
         }
 
+        //SPI iface needs to be configured for a delay in some cases.
         //configure dummy bits
         host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0;
         host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1;
 
+        int miso_long_delay = 0;
+        if (dev->clk_cfg.miso_delay<0) {
+            //if the data comes too late, delay half a SPI clock to improve reading
+            miso_long_delay = 1;
+            host->hw->ctrl2.miso_delay_num = 0;
+        } else {
+            //if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing
+            host->hw->ctrl2.miso_delay_num = (extra_dummy? dev->clk_cfg.miso_delay: 0);
+        }
+
+        if (dev->cfg.mode==0) {
+            host->hw->ctrl2.miso_delay_mode=miso_long_delay?2:0;
+        } else if (dev->cfg.mode==1) {
+            host->hw->ctrl2.miso_delay_mode=miso_long_delay?1:0;
+        } else if (dev->cfg.mode==2) {
+            host->hw->ctrl2.miso_delay_mode=miso_long_delay?1:0;
+        } else if (dev->cfg.mode==3) {
+            host->hw->ctrl2.miso_delay_mode=miso_long_delay?2:0;
+        }
+
         host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1;
         if ( dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ) {
             host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1;
@@ -667,8 +709,8 @@ static void IRAM_ATTR spi_intr(void *arg)
         // output command will be sent from bit 7 to 0 of command_value, and then bit 15 to 8 of the same register field.
         uint16_t command = trans->cmd << (16-cmdlen);    //shift to MSB
         host->hw->user2.usr_command_value = (command>>8)|(command<<8);  //swap the first and second byte
-        // shift the address to MSB of addr (and maybe slv_wr_status) register. 
-        // output address will be sent from MSB to LSB of addr register, then comes the MSB to LSB of slv_wr_status register. 
+        // shift the address to MSB of addr (and maybe slv_wr_status) register.
+        // output address will be sent from MSB to LSB of addr register, then comes the MSB to LSB of slv_wr_status register.
         if (addrlen>32) {
             host->hw->addr = trans->addr >> (addrlen- 32);
             host->hw->slv_wr_status = trans->addr << (64 - addrlen);
@@ -693,13 +735,13 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
     esp_err_t ret = ESP_OK;
     BaseType_t r;
     SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
-    //check transmission length 
+    //check transmission length
     SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 ||trans_desc->rxlength <= 32, "rxdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG);
     SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG);
     SPI_CHECK(trans_desc->length <= handle->host->max_transfer_sz*8, "txdata transfer > host maximum", ESP_ERR_INVALID_ARG);
     SPI_CHECK(trans_desc->rxlength <= handle->host->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG);
     SPI_CHECK((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG);
-    //check working mode    
+    //check working mode
     SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG);
     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) || handle->host->dma_chan == 0 || !(trans_desc->flags & SPI_TRANS_USE_RXDATA || trans_desc->rx_buffer != NULL)
@@ -717,7 +759,7 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
     // rx memory assign
     if ( trans_desc->flags & SPI_TRANS_USE_RXDATA ) {
         trans_buf.buffer_to_rcv = (uint32_t*)&trans_desc->rx_data[0];
-    } else { 
+    } else {
         //if not use RXDATA neither rx_buffer, buffer_to_rcv assigned to NULL
         trans_buf.buffer_to_rcv = trans_desc->rx_buffer;
     }
@@ -730,12 +772,12 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
             goto clean_up;
         }
     }
-    
+
     const uint32_t *txdata;
     // tx memory assign
     if ( trans_desc->flags & SPI_TRANS_USE_TXDATA ) {
         txdata = (uint32_t*)&trans_desc->tx_data[0];
-    } else { 
+    } else {
         //if not use TXDATA neither tx_buffer, tx data assigned to NULL
         txdata = trans_desc->tx_buffer ;
     }
@@ -748,11 +790,11 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
             goto clean_up;
         }
         memcpy( trans_buf.buffer_to_send, txdata, (trans_desc->length+7)/8 );
-    } else { 
+    } else {
         // else use the original buffer (forced-conversion) or assign to NULL
         trans_buf.buffer_to_send = (uint32_t*)txdata;
     }
-    
+
 #ifdef CONFIG_PM_ENABLE
     esp_pm_lock_acquire(handle->host->pm_lock);
 #endif
@@ -772,10 +814,10 @@ clean_up:
     // free malloc-ed buffer (if needed) before return.
     if ( (void*)trans_buf.buffer_to_rcv != trans_desc->rx_buffer && (void*)trans_buf.buffer_to_rcv != &trans_desc->rx_data[0] ) {
         free( trans_buf.buffer_to_rcv );
-    }   
+    }
     if ( (void*)trans_buf.buffer_to_send!= trans_desc->tx_buffer && (void*)trans_buf.buffer_to_send != &trans_desc->tx_data[0] ) {
         free( trans_buf.buffer_to_send );
-    }   
+    }
     assert( ret != ESP_OK );
     return ret;
 }
@@ -784,12 +826,12 @@ esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transactio
 {
     BaseType_t r;
     spi_trans_priv trans_buf;
-    
+
     SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
     r=xQueueReceive(handle->ret_queue, (void*)&trans_buf, ticks_to_wait);
     if (!r) {
         // The memory occupied by rx and tx DMA buffer destroyed only when receiving from the queue (transaction finished).
-        // If timeout, wait and retry. 
+        // If timeout, wait and retry.
         // Every on-flight transaction request occupies internal memory as DMA buffer if needed.
         return ESP_ERR_TIMEOUT;
     }
@@ -798,12 +840,12 @@ esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transactio
 
     if ( (void*)trans_buf.buffer_to_send != &(*trans_desc)->tx_data[0] && trans_buf.buffer_to_send != (*trans_desc)->tx_buffer ) {
         free( trans_buf.buffer_to_send );
-    }   
+    }
 
     //copy data from temporary DMA-capable buffer back to IRAM buffer and free the temporary one.
     if ( (void*)trans_buf.buffer_to_rcv != &(*trans_desc)->rx_data[0] && trans_buf.buffer_to_rcv != (*trans_desc)->rx_buffer ) {
         if ( (*trans_desc)->flags & SPI_TRANS_USE_RXDATA ) {
-            memcpy( (uint8_t*)&(*trans_desc)->rx_data[0], trans_buf.buffer_to_rcv, ((*trans_desc)->rxlength+7)/8 );   
+            memcpy( (uint8_t*)&(*trans_desc)->rx_data[0], trans_buf.buffer_to_rcv, ((*trans_desc)->rxlength+7)/8 );
         } else {
             memcpy( (*trans_desc)->rx_buffer, trans_buf.buffer_to_rcv, ((*trans_desc)->rxlength+7)/8 );
         }
diff --git a/components/soc/esp32/include/driver/spi_pins.h b/components/soc/esp32/include/driver/spi_pins.h
new file mode 100644 (file)
index 0000000..0073dba
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+\r
+//     http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+\r
+#ifndef _DRIVER_SPI_PINS_H_\r
+#define _DRIVER_SPI_PINS_H_\r
+\r
+#define HSPI_NATIVE_PIN_NUM_MISO 12\r
+#define HSPI_NATIVE_PIN_NUM_MOSI 13\r
+#define HSPI_NATIVE_PIN_NUM_CLK  14\r
+#define HSPI_NATIVE_PIN_NUM_CS   15\r
+\r
+#define VSPI_NATIVE_PIN_NUM_MISO 19\r
+#define VSPI_NATIVE_PIN_NUM_MOSI 23\r
+#define VSPI_NATIVE_PIN_NUM_CLK  18\r
+#define VSPI_NATIVE_PIN_NUM_CS   5\r
+\r
+#endif
\ No newline at end of file
diff --git a/docs/_static/diagrams/spi_master/miso_timing_waveform.rst b/docs/_static/diagrams/spi_master/miso_timing_waveform.rst
new file mode 100644 (file)
index 0000000..ec11ea1
--- /dev/null
@@ -0,0 +1,17 @@
+.. this picture is generated by https://wavedrom.com/, using the sphinx plugin sphinxcontrib-wavedrom\r
+.. due to plugin issue, we cannot place only the picture information in a standalone file, but have to take .. wavedrom:: inside\r
+\r
+.. wavedrom:: \r
+\r
+    { signal: [\r
+      { name: 'SCLK',  wave: 'p...',  node: '.ad...' },\r
+      { name: 'MISO',  wave: 'x3x.',  node: '.b...', phase:-1.8 },\r
+      { name: 'MISO delayed',          wave: 'x3x.',  node: '.c.', phase:-2.4 },\r
+      ],\r
+      edge: [\r
+        'a|->b input delay',\r
+        'b|->c gpio delay',\r
+       'c-|>d setup slack'\r
+      ],\r
+      config: { hscale: 3 }\r
+    }\r
diff --git a/docs/_static/diagrams/spi_master/miso_timing_waveform_async.rst b/docs/_static/diagrams/spi_master/miso_timing_waveform_async.rst
new file mode 100644 (file)
index 0000000..dd3ae8b
--- /dev/null
@@ -0,0 +1,19 @@
+.. this picture is generated by https://wavedrom.com/, using the sphinx plugin sphinxcontrib-wavedrom\r
+.. due to plugin issue, we cannot place only the picture information in a standalone file, but have to take .. wavedrom:: inside\r
+\r
+.. wavedrom:: \r
+\r
+    { signal: [\r
+      { name: 'SCLK',  wave: '0.1....0....1...', node: '..a.........e'},\r
+      { name: 'SLV_CLK',wave: 'p..............',  node: '..b..', phase: -0.5 },  \r
+      { name: 'MISO',  wave: 'x...3.....x....',  node: '....c', phase:-0.5},\r
+      { name: 'MISO delayed',  wave: 'x.......3.....x.',  node: '........d'},\r
+      ],\r
+      edge: [\r
+        'a|->b sample delay',\r
+        'b|->c slave output delay',\r
+       'c|->d gpio delay',\r
+        'd-|>e setup slack',\r
+        'a-|>c input delay'\r
+      ],\r
+    }\r
diff --git a/docs/_static/diagrams/spi_master/spi_master_freq_tv.plt b/docs/_static/diagrams/spi_master/spi_master_freq_tv.plt
new file mode 100644 (file)
index 0000000..b96d3d4
--- /dev/null
@@ -0,0 +1,33 @@
+# this is a GNUPLOT script generating the figure of spi_master_freq_tv.png\r
+\r
+set xlabel "Input delay (ns)"\r
+set xrange [0: 125]\r
+set ylabel "Fmax (MHz)"\r
+set yrange [0: 81]\r
+set xtics 12.5 textcolor rgb "black"\r
+set ytics 10 textcolor rgb "black"\r
+set border 3 lc rgb "gray" lw 2\r
+set grid lt -1 lc rgb "gray" lw 2\r
+set samples 10000\r
+set terminal png size 700,500\r
+set output "plot.png"\r
+\r
+apb = 12.5\r
+\r
+#each line is broken into 10 pieces by the range determined by i\r
+f1(i,x) = (x>= i*apb) && (x < (i+1)*apb) ? 80./(i+1) : 1/0\r
+\r
+set style circle radius graph 0.008\r
+\r
+#solid and empty circles are draw by the coordinates given in the csv\r
+plot [0:125]\\r
+    f1(-1, x) lw 3lc rgb "blue" title "IOMUX",\\r
+    for [i=0:9] f1(i, x) with lines lw 3 lc rgb "blue" notitle,\\r
+    f1(0, x+25) lw 3 lc rgb "red" title "GPIO",\\r
+    for [i=2:11] f1(i, x+25) with lines lw 3 lc rgb "red" notitle, \\r
+    "tv.csv" using 1:2 with circles notitle fill solid fc rgb "blue", \\r
+    "tv.csv" using 1:4 with circles notitle fc rgb "blue",\\r
+    "tv.csv" using 1:3 with circles notitle fill solid fc rgb "red" ,\\r
+    "tv.csv" using 1:5 with circles notitle fc rgb "red" \r
+    \r
+\r
diff --git a/docs/_static/diagrams/spi_master/spi_timing.pptx b/docs/_static/diagrams/spi_master/spi_timing.pptx
new file mode 100644 (file)
index 0000000..2f6666c
Binary files /dev/null and b/docs/_static/diagrams/spi_master/spi_timing.pptx differ
diff --git a/docs/_static/diagrams/spi_master/tv.csv b/docs/_static/diagrams/spi_master/tv.csv
new file mode 100644 (file)
index 0000000..c17ad61
--- /dev/null
@@ -0,0 +1,29 @@
+0      80      26.66666667     #DIV/0! #DIV/0!\r
+12.5   40      20      80      26.66666667\r
+25     26.66666667     16      40      20\r
+37.5   20      13.33333333     26.66666667     16\r
+50     16      11.42857143     20      13.33333333\r
+62.5   13.33333333     10      16      11.42857143\r
+75     11.42857143     8.888888889     13.33333333     10\r
+87.5   10      8       11.42857143     8.888888889\r
+100    8.888888889     7.272727273     10      8\r
+112.5  8       6.666666667     8.888888889     7.272727273\r
+125    7.272727273     6.153846154     8       6.666666667\r
+137.5  6.666666667     5.714285714     7.272727273     6.153846154\r
+150    6.153846154     5.333333333     6.666666667     5.714285714\r
+162.5  5.714285714     5       6.153846154     5.333333333\r
+175    5.333333333     4.705882353     5.714285714     5\r
+187.5  5       4.444444444     5.333333333     4.705882353\r
+200    4.705882353     4.210526316     5       4.444444444\r
+212.5  4.444444444     4       4.705882353     4.210526316\r
+225    4.210526316     3.80952381      4.444444444     4\r
+237.5  4       3.636363636     4.210526316     3.80952381\r
+250    3.80952381      3.47826087      4       3.636363636\r
+262.5  3.636363636     3.333333333     3.80952381      3.47826087\r
+275    3.47826087      3.2     3.636363636     3.333333333\r
+287.5  3.333333333     3.076923077     3.47826087      3.2\r
+300    3.2     2.962962963     3.333333333     3.076923077\r
+312.5  3.076923077     2.857142857     3.2     2.962962963\r
+325    2.962962963     2.75862069      3.076923077     2.857142857\r
+337.5  2.857142857     2.666666667     2.962962963     2.75862069\r
+350    2.75862069      2.580645161     2.857142857     2.666666667\r
diff --git a/docs/_static/miso_timing_waveform.png b/docs/_static/miso_timing_waveform.png
new file mode 100644 (file)
index 0000000..19cc223
Binary files /dev/null and b/docs/_static/miso_timing_waveform.png differ
diff --git a/docs/_static/miso_timing_waveform_async.png b/docs/_static/miso_timing_waveform_async.png
new file mode 100644 (file)
index 0000000..4cf84e2
Binary files /dev/null and b/docs/_static/miso_timing_waveform_async.png differ
diff --git a/docs/_static/spi_master_freq_tv.png b/docs/_static/spi_master_freq_tv.png
new file mode 100644 (file)
index 0000000..d8d888d
Binary files /dev/null and b/docs/_static/spi_master_freq_tv.png differ
diff --git a/docs/_static/spi_miso.png b/docs/_static/spi_miso.png
new file mode 100644 (file)
index 0000000..4580938
Binary files /dev/null and b/docs/_static/spi_miso.png differ
index 05474190d74979f0da2bfe2838a7664944a691d5..a5239c6a001a31524cc4214ea7c074e15d26a51e 100644 (file)
@@ -14,7 +14,7 @@ The spi_master driver
 ^^^^^^^^^^^^^^^^^^^^^
 
 The spi_master driver allows easy communicating with SPI slave devices, even in a multithreaded environment.
-It fully transparently handles DMA transfers to read and write data and automatically takes care of 
+It fully transparently handles DMA transfers to read and write data and automatically takes care of
 multiplexing between different SPI slaves on the same master
 
 Terminology
@@ -22,11 +22,11 @@ Terminology
 
 The spi_master driver uses the following terms:
 
-* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of SPI, HSPI or VSPI. (For 
-  now, only HSPI or VSPI are actually supported in the driver; it will support all 3 peripherals 
+* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of SPI, HSPI or VSPI. (For
+  now, only HSPI or VSPI are actually supported in the driver; it will support all 3 peripherals
   somewhere in the future.)
 * Bus: The SPI bus, common to all SPI devices connected to one host. 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.
 
   - miso - Also known as q, this is the input of the serial stream into the ESP32
@@ -58,33 +58,40 @@ A transaction on the SPI bus consists of five phases, any of which may be skippe
 * The read phase. The slave sends data to the master.
 
 In full duplex mode, the read and write phases are combined, and the SPI host reads and
-writes data simultaneously. The total transaction length is decided by 
+writes data simultaneously. The total transaction length is decided by
 ``command_bits + address_bits + trans_conf.length``, while the ``trans_conf.rx_length``
 only determins length of data received into the buffer.
 
 While in half duplex mode, the host have independent write and read phases. The length of write phase and read phase are
-decided by ``trans_conf.length`` and ``trans_conf.rx_length`` respectively. 
+decided by ``trans_conf.length`` and ``trans_conf.rx_length`` respectively.
 
 The command and address phase are optional in that not every SPI device will need to be sent a command
 and/or address. This is reflected in the device configuration: when the ``command_bits`` or ``address_bits``
 fields are set to zero, no command or address phase is done.
 
 Something similar is true for the read and write phase: not every transaction needs both data to be written
-as well as data to be read. When ``rx_buffer`` is NULL (and SPI_USE_RXDATA) is not set) the read phase 
+as well as data to be read. When ``rx_buffer`` is NULL (and SPI_USE_RXDATA) is not set) the read phase
 is skipped. When ``tx_buffer`` is NULL (and SPI_USE_TXDATA) is not set) the write phase is skipped.
 
-GPIO matrix and native pins
+GPIO matrix and IOMUX
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Most peripheral pins in ESP32 can directly connect to a GPIO, which is called *native pin*. When the peripherals are
-required to work with other pins than the native pins, ESP32 use a *GPIO matrix* to realize this. If one of the pins is
-not native, the driver automatically routes all the signals to the GPIO matrix, which works under 80MHz. The signals are
-sampled and sent to peripherals or the GPIOs. 
+Most peripheral signals in ESP32 can connect directly to a specific GPIO, which is called its IOMUX pin. When a
+peripheral signal is routed to a pin other than its IOMUX pin, ESP32 uses the less direct GPIO matrix to make this
+connection.
 
-When the GPIO matrix is used, signals cannot propogate to the peripherals over 40MHz, and the setup time of MISO is very
-likely violated. Hence the clock frequency limitation is a little lower than the case without GPIO matrix.
+If the driver is configured with all SPI signals set to their specific IOMUX pins (or left unconnected), it will bypass
+the GPIO matrix. If any SPI signal is configured to a pin other than its IOMUx pin, the driver will automatically route
+all the signals via the GPIO Matrix. The GPIO matrix samples all signals at 80MHz and sends them between the GPIO and
+the peripheral.
 
-Native pins for SPI controllers are as below:
+When the GPIO matrix is used, signals faster than 40MHz cannot propagate and the setup time of MISO is more easily
+violated, since the input delay of MISO signal is increased. The maximum clock frequency with GPIO Matrix is 40MHz
+or less, whereas using all IOMUX pins allows 80MHz.
+
+.. note:: More details about influence of input delay on the maximum clock frequency, see :ref:`timing_considerations` below.
+
+IOMUX pins for SPI controllers are as below:
 
 +----------+------+------+
 | Pin Name | HSPI | VSPI |
@@ -112,11 +119,11 @@ Using the spi_master driver
 - Initialize a SPI bus by calling ``spi_bus_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.
 
-- Tell the driver about a SPI slave device connected to the bus by calling spi_bus_add_device. 
+- Tell the driver about a SPI slave device connected to the bus by calling spi_bus_add_device.
   Make sure to configure any timing requirements the device has in the ``dev_config`` structure.
   You should now have a handle for the device, to be used when sending it a transaction.
 
-- To interact with the device, fill one or more spi_transaction_t structure with any transaction 
+- To interact with the device, fill one or more spi_transaction_t structure with any transaction
   parameters you need. Either queue all transactions by calling ``spi_device_queue_trans``, later
   quering the result using ``spi_device_get_trans_result``, or handle all requests synchroneously
   by feeding them into ``spi_device_transmit``.
@@ -124,7 +131,7 @@ Using the spi_master driver
 - Optional: to unload the driver for a device, call ``spi_bus_remove_device`` with the device
   handle as an argument
 
-- Optional: to remove the driver for a bus, make sure no more drivers are attached and call 
+- Optional: to remove the driver for a bus, make sure no more drivers are attached and call
   ``spi_bus_free``.
 
 Command and address phases
@@ -135,26 +142,26 @@ During the command and address phases, ``cmd`` and ``addr`` field in the
 same time. The default length of command and address phase are set in the
 ``spi_device_interface_config_t`` and by ``spi_bus_add_device``. When the the
 flag ``SPI_TRANS_VARIABLE_CMD`` and ``SPI_TRANS_VARIABLE_ADDR`` are not set in
-the ``spi_transaction_t``,the driver automatically set the length of these 
+the ``spi_transaction_t``,the driver automatically set the length of these
 phases to the default value as set when the device is initialized respectively.
 
 If the length of command and address phases needs to be variable, declare a
-``spi_transaction_ext_t`` descriptor, set the flag ``SPI_TRANS_VARIABLE_CMD`` 
-or/and ``SPI_TRANS_VARIABLE_ADDR`` in the ``flags`` of ``base`` member and 
-configure the rest part of ``base`` as usual. Then the length of each phases 
+``spi_transaction_ext_t`` descriptor, set the flag ``SPI_TRANS_VARIABLE_CMD``
+or/and ``SPI_TRANS_VARIABLE_ADDR`` in the ``flags`` of ``base`` member and
+configure the rest part of ``base`` as usual. Then the length of each phases
 will be ``command_bits`` and ``address_bits`` set in the ``spi_transaction_ext_t``.
 
 Write and read phases
 ^^^^^^^^^^^^^^^^^^^^^
 
 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. 
+indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure.
 When DMA is enabled for transfers, these buffers are highly recommended to meet the requirements as below:
 
   1. allocated in DMA-capable memory using ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``;
   2. 32-bit aligned (start from the boundary and have length of multiples of 4 bytes).
 
-If these requirements are not satisfied, efficiency of the transaction will suffer due to the allocation and 
+If these requirements are not satisfied, efficiency of the transaction will suffer due to the allocation and
 memcpy of temporary buffers.
 
 .. note::  Half duplex transactions with both read and write phases are not supported when using DMA. See
@@ -179,25 +186,27 @@ speed a lot if small transactions are used.
 
     1. Transaction interval: The interval mainly comes from the cost of FreeRTOS queues and the time switching between
        tasks and the ISR. It also takes time for the software to setup spi peripheral registers as well as copy data to
-       FIFOs, or setup DMA links. Depending on whether the DMA is used, the interval of an one-byte transaction is around 
-       25us typically. 
-            
+       FIFOs, or setup DMA links. Depending on whether the DMA is used, the interval of an one-byte transaction is around
+       25us typically.
+
             1.  The CPU is blocked and switched to other tasks when the
-                transaction is in flight. This save the cpu time but increase the interval. 
+                transaction is in flight. This save the cpu time but increase the interval.
             2.  When the DMA is enabled, it needs about 2us per transaction to setup the linked list. When the master is
                 transferring, it automatically read data from the linked list. If the DMA is not enabled,
                 CPU has to write/read each byte to/from the FIFO by itself. Usually this is faster than 2us, but the
                 transaction length is limited to 32 bytes for both write and read.
-       
+
        Typical transaction interval with one byte data is as below:
 
-       +-----------------------+---------+
-       | Transaction Time (us) | Typical |
-       +=======================+=========+
-       | DMA                   | 24      | 
-       +-----------------------+---------+
-       | No DMA                | 22      |
-       +-----------------------+---------+
+       +--------+------------------+
+       |        | Transaction Time |
+       +========+==================+
+       |        | Typical (us)     |
+       +--------+------------------+
+       | DMA    | 24               |
+       +--------+------------------+
+       | No DMA | 22               |
+       +--------+------------------+
 
     2. SPI clock frequency: Each byte transferred takes 8 times of the clock period *8/fspi*. If the clock frequency is
        too high, some functions may be limited to use. See :ref:`timing_considerations`.
@@ -209,7 +218,7 @@ clock speed:
 +-----------+----------------------+--------------------+------------+-------------+
 | Frequency | Transaction Interval | Transaction Length | Total Time | Total Speed |
 |           |                      |                    |            |             |
-| [MHz]     | [us]                 | [bytes]            | [us]       | [kBps]      |
+| (MHz)     | (us)                 | (bytes)            | (us)       | (kBps)      |
 +===========+======================+====================+============+=============+
 | 8         | 25                   | 1                  | 26         | 38.5        |
 +-----------+----------------------+--------------------+------------+-------------+
@@ -229,9 +238,24 @@ into one transaction if possible to get higher transfer speed.
 
 Timing considerations
 ^^^^^^^^^^^^^^^^^^^^^
-Due to the input delay of MISO pin, ESP32 SPI master cannot read data at very high speed. The frequency allowed is
-rather low when the GPIO matrix is used. Currently only frequency not greater than 8.8MHz is fully supported. When the
-frequency is higher, you have to use the native pins or the *dummy bit workaround*.
+
+As shown in the figure below, there is a delay on the MISO signal after SCLK
+launch edge and before it's latched by the internal register. As a result,
+the MISO pin setup time is the limiting factor for SPI clock speed. When the
+delay is too large, setup slack is < 0 and the setup timing requirement is
+violated, leads to the failure of reading correctly.
+
+.. image:: /../_static/spi_miso.png
+
+""" wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
+.. include:: /../_static/miso_timing_waveform.png
+
+The maximum frequency allowed is related to the *input delay* (maximum valid
+time after SCLK on the MISO bus), as well as the usage of GPIO matrix. The
+maximum frequency allowed is reduced to about 33~77% (related to existing
+*input delay*) when the GPIO matrix is used. To work at higher frequency, you
+have to use the IOMUX pins or the *dummy bit workaround*. You can get the
+maximum reading frequency of the master by ``spi_get_freq_limit``.
 
 .. _dummy_bit_workaround:
 
@@ -240,29 +264,97 @@ actually begins. The slave still sees the dummy clocks and gives out data, but t
 phase. This compensates the lack of setup time of MISO required by the host, allowing the host reading at higher
 frequency.
 
-The maximum frequency (in MHz) host can read (or read and write) under different conditions is as below:
-
-+-------------+-------------+-----------+-----------------------------+
-| Frequency Limit           | Dummy Bits| Comments                    | 
-+-------------+-------------+ Used      +                             +
-| GPIO matrix | Native pins | By Driver |                             |
-+=============+=============+===========+=============================+
-| 8.8         | N.M.        | 0         |                             |
-+-------------+-------------+-----------+-----------------------------+
-| N.M.        | N.M.        | 1         | Half Duplex, no DMA allowed |
-+-------------+-------------+-----------+                             +
-| N.M.        | N.M.        | 2         |                             |
-+-------------+-------------+-----------+-----------------------------+
+In the ideal case (the slave is so fast that the input delay is shorter than an apb clock, 12.5ns), the maximum
+frequency host can read (or read and write) under different conditions is as below:
 
-N.M.: Not Measured Yet.
++-------------+-------------+------------+-----------------------------+
+| Frequency Limit (MHz)     | Dummy Bits | Comments                    |
++-------------+-------------+ Used       +                             +
+| GPIO matrix | IOMUX pins  | By Driver  |                             |
++=============+=============+============+=============================+
+| 26.6        | 80          | No         |                             |
++-------------+-------------+------------+-----------------------------+
+| 40          | --          | Yes        | Half Duplex, no DMA allowed |
++-------------+-------------+------------+-----------------------------+
 
 And if the host only writes, the *dummy bit workaround* is not used and the frequency limit is as below:
 
-+-------------+----------------------+
-| GPIO matrix | Native pins          |
-+=============+======================+
-| 40          | 80                   |
-+-------------+----------------------+
++-------------------+------------------+
+| GPIO matrix (MHz) | IOMUX pins (MHz) |
++===================+==================+
+| 40                | 80               |
++-------------------+------------------+
+
+The spi master driver can work even if the *input delay* in the ``spi_device_interface_config_t`` is set to 0. 
+However, setting a accurate value helps to: (1) calculate the frequency limit in full duplex mode, and (2) compensate 
+the timing correctly by dummy bits in half duplex mode. You may find the maximum data valid time after the launch edge 
+of SPI clocks in the AC characteristics chapter of the device specifications, or measure the time on a oscilloscope or 
+logic analyzer.
+
+""" wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
+.. include:: /../_static/miso_timing_waveform_async.png
+
+As shown in the figure above, the input delay is usually:
+
+    *[input delay] = [sample delay] + [slave output delay]*
+
+    1. The sample delay is the maximum random delay due to the
+       asynchronization of SCLK and peripheral clock of the slave. It's usually
+       1 slave peripheral clock if the clock is asynchronize with SCLK, or 0 if
+       the slave just use the SCLK to latch the SCLK and launch MISO data. e.g.
+       for ESP32 slaves, the delay is 12.5ns (1 apb clock), while it is reduced
+       to 0 if the slave is in the same chip as the master.
+
+    2. The slave output delay is the time for the MOSI to be stable after the
+       launch edge. e.g. for ESP32 slaves, the output delay is 37.5ns (3 apb
+       clocks) when IOMUX pins in the slave is used, or 62.5ns (5 apb clocks) if
+       through the GPIO matrix.
+
+Some typical delays are shown in the following table:
+
++--------------------+------------------+
+| Device             | Input delay (ns) |
++====================+==================+
+| Ideal device       |      0           |
++--------------------+------------------+
+| ESP32 slave IOMUX* |      50          |
++--------------------+------------------+
+| ESP32 slave GPIO*  |      75          |
++--------------------+------------------+
+| ESP32 slave is on an independent      |
+| chip, 12.5ns sample delay included.   |
++---------------------------------------+
+
+The MISO path delay(tv), consists of slave *input delay* and master *GPIO matrix delay*, finally determines the 
+frequency limit, above which the full duplex mode will not work, or dummy bits are used in the half duplex mode. The 
+frequency limit is:
+
+    *Freq limit[MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
+
+The figure below shows the relations of frequency limit against the input delay. 2 extra apb clocks should be counted 
+into the MISO delay if the GPIO matrix in the master is used.
+
+.. image:: /../_static/spi_master_freq_tv.png
+
+Corresponding frequency limit for different devices with different *input delay* are shown in the following 
+table:
+
++--------+------------------+----------------------+-------------------+
+| Master | Input delay (ns) | MISO path delay (ns) | Freq. limit (MHz) |
++========+==================+======================+===================+
+| IOMUX  | 0                | 0                    | 80                |
++ (0ns)  +------------------+----------------------+-------------------+
+|        | 50               | 50                   | 16                |
++        +------------------+----------------------+-------------------+
+|        | 75               | 75                   | 11.43             |
++--------+------------------+----------------------+-------------------+
+| GPIO   | 0                | 25                   | 26.67             |
++ (25ns) +------------------+----------------------+-------------------+
+|        | 50               | 75                   | 11.43             |
++        +------------------+----------------------+-------------------+
+|        | 75               | 100                  | 8.89              |
++--------+------------------+----------------------+-------------------+
+
 
 Thread Safety
 -------------
@@ -282,7 +374,7 @@ Known Issues
 
    1. use full-duplex mode instead.
    2. disable the DMA by setting the last parameter to 0 in bus initialization function just as below:
-      ``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);``  
+      ``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);``
 
       this may prohibit you from transmitting and receiving data longer than 32 bytes.
    3. try to use command and address field to replace the write phase.
@@ -293,7 +385,7 @@ Known Issues
 
 Application Example
 -------------------
+
 Display graphics on the 320x240 LCD of WROVER-Kits: :example:`peripherals/spi_master`.