]> granicus.if.org Git - esp-idf/commitdiff
spi_flash: move cache operations into separate file
authorIvan Grokhotkov <ivan@espressif.com>
Wed, 19 Oct 2016 09:08:05 +0000 (17:08 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Thu, 27 Oct 2016 09:57:29 +0000 (17:57 +0800)
components/spi_flash/README.rst [new file with mode: 0644]
components/spi_flash/cache_utils.c [moved from components/spi_flash/esp_spi_flash.c with 53% similarity]
components/spi_flash/cache_utils.h [new file with mode: 0644]
components/spi_flash/flash_ops.c [new file with mode: 0644]

diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst
new file mode 100644 (file)
index 0000000..22f98cf
--- /dev/null
@@ -0,0 +1,33 @@
+Driver for SPI flash read/write/erase operations
+================================================
+
+Implementation notes
+--------------------
+
+In order to perform some flash operations, we need to make sure both CPUs
+are not running any code from flash for the duration of the flash operation.
+In a single-core setup this is easy: we disable interrupts/scheduler and do
+the flash operation. In the dual-core setup this is slightly more complicated.
+We need to make sure that the other CPU doesn't run any code from flash.
+
+
+When SPI flash API is called on CPU A (can be PRO or APP), we start
+spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API
+wakes up high priority task on CPU B and tells it to execute given function,
+in this case spi_flash_op_block_func. This function disables cache on CPU B and
+signals that cache is disabled by setting s_flash_op_can_start flag.
+Then the task on CPU A disables cache as well, and proceeds to execute flash
+operation.
+
+While flash operation is running, interrupts can still run on CPU B.
+We assume that all interrupt code is placed into RAM.
+
+Once flash operation is complete, function on CPU A sets another flag,
+s_flash_op_complete, to let the task on CPU B know that it can re-enable
+cache and release the CPU. Then the function on CPU A re-enables the cache on
+CPU A as well and returns control to the calling code.
+
+Additionally, all API functions are protected with a mutex (s_flash_op_mutex).
+
+In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply
+disable both caches, no inter-CPU communication takes place.
similarity index 53%
rename from components/spi_flash/esp_spi_flash.c
rename to components/spi_flash/cache_utils.c
index d702f3b81541b43dc0ad361f0ca087dc7204488f..6ae47bdb3e203c31d98873a7776524bc325062ef 100644 (file)
@@ -3,7 +3,7 @@
 // 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
 #include "esp_spi_flash.h"
 #include "esp_log.h"
 
-/*
-    Driver for SPI flash read/write/erase operations
-
-    In order to perform some flash operations, we need to make sure both CPUs
-    are not running any code from flash for the duration of the flash operation.
-    In a single-core setup this is easy: we disable interrupts/scheduler and do
-    the flash operation. In the dual-core setup this is slightly more complicated.
-    We need to make sure that the other CPU doesn't run any code from flash.
-
-
-    When SPI flash API is called on CPU A (can be PRO or APP), we start
-    spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API
-    wakes up high priority task on CPU B and tells it to execute given function,
-    in this case spi_flash_op_block_func. This function disables cache on CPU B and
-    signals that cache is disabled by setting s_flash_op_can_start flag.
-    Then the task on CPU A disables cache as well, and proceeds to execute flash
-    operation.
-
-    While flash operation is running, interrupts can still run on CPU B.
-    We assume that all interrupt code is placed into RAM.
-
-    Once flash operation is complete, function on CPU A sets another flag,
-    s_flash_op_complete, to let the task on CPU B know that it can re-enable
-    cache and release the CPU. Then the function on CPU A re-enables the cache on
-    CPU A as well and returns control to the calling code.
-
-    Additionally, all API functions are protected with a mutex (s_flash_op_mutex).
 
-    In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply
-    disable both caches, no inter-CPU communication takes place.
-*/
-
-static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc);
 static void IRAM_ATTR spi_flash_disable_cache(uint32_t cpuid, uint32_t* saved_state);
 static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state);
 
@@ -72,25 +40,23 @@ static uint32_t s_flash_op_cache_state[2];
 static SemaphoreHandle_t s_flash_op_mutex;
 static bool s_flash_op_can_start = false;
 static bool s_flash_op_complete = false;
-#endif //CONFIG_FREERTOS_UNICORE
-
-#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
-static const char* TAG = "spi_flash";
-static spi_flash_counters_t s_flash_stats;
 
-#define COUNTER_START()     uint32_t ts_begin = xthal_get_ccount()
-#define COUNTER_STOP(counter)  do{ s_flash_stats.counter.count++; s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); } while(0)
-#define COUNTER_ADD_BYTES(counter, size) do { s_flash_stats.counter.bytes += size; } while (0)
-#else
-#define COUNTER_START()
-#define COUNTER_STOP(counter)
-#define COUNTER_ADD_BYTES(counter, size)
+void spi_flash_init_lock()
+{
+    s_flash_op_mutex = xSemaphoreCreateMutex();
+}
 
-#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
+void spi_flash_op_lock()
+{
+    xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY);
+}
 
-#ifndef CONFIG_FREERTOS_UNICORE
+void spi_flash_op_unlock()
+{
+    xSemaphoreGive(s_flash_op_mutex);
+}
 
-static void IRAM_ATTR spi_flash_op_block_func(void* arg)
+void IRAM_ATTR spi_flash_op_block_func(void* arg)
 {
     // Disable scheduler on this CPU
     vTaskSuspendAll();
@@ -108,19 +74,9 @@ static void IRAM_ATTR spi_flash_op_block_func(void* arg)
     xTaskResumeAll();
 }
 
-void spi_flash_init()
+void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
 {
-    s_flash_op_mutex = xSemaphoreCreateMutex();
-
-#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
-    spi_flash_reset_counters();
-#endif
-}
-
-static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
-{
-    // Take the API lock
-    xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY);
+    spi_flash_op_lock();
 
     const uint32_t cpuid = xPortGetCoreID();
     const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
@@ -152,7 +108,7 @@ static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
     spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]);
 }
 
-static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
+void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
 {
     const uint32_t cpuid = xPortGetCoreID();
     const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
@@ -173,98 +129,45 @@ static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
         xTaskResumeAll();
     }
     // Release API lock
-    xSemaphoreGive(s_flash_op_mutex);
+    spi_flash_op_unlock();
 }
 
-#else  // CONFIG_FREERTOS_UNICORE
+#else // CONFIG_FREERTOS_UNICORE
 
-void spi_flash_init()
+void spi_flash_init_lock()
 {
-#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
-    spi_flash_reset_counters();
-#endif
 }
 
-static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
+void spi_flash_op_lock()
 {
     vTaskSuspendAll();
-    spi_flash_disable_cache(0, &s_flash_op_cache_state[0]);
 }
 
-static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
+void spi_flash_op_unlock()
 {
-    spi_flash_restore_cache(0, s_flash_op_cache_state[0]);
     xTaskResumeAll();
 }
 
-#endif // CONFIG_FREERTOS_UNICORE
 
-
-SpiFlashOpResult IRAM_ATTR spi_flash_unlock()
+void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
 {
-    static bool unlocked = false;
-    if (!unlocked) {
-        SpiFlashOpResult rc = SPIUnlock();
-        if (rc != SPI_FLASH_RESULT_OK) {
-            return rc;
-        }
-        unlocked = true;
-    }
-    return SPI_FLASH_RESULT_OK;
+    spi_flash_op_lock();
+    spi_flash_disable_cache(0, &s_flash_op_cache_state[0]);
 }
 
-esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec)
+void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
 {
-    COUNTER_START();
-    spi_flash_disable_interrupts_caches_and_other_cpu();
-    SpiFlashOpResult rc;
-    rc = spi_flash_unlock();
-    if (rc == SPI_FLASH_RESULT_OK) {
-        rc = SPIEraseSector(sec);
-    }
-    spi_flash_enable_interrupts_caches_and_other_cpu();
-    COUNTER_STOP(erase);
-    return spi_flash_translate_rc(rc);
+    spi_flash_restore_cache(0, s_flash_op_cache_state[0]);
+    spi_flash_op_unlock();
 }
 
-esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size)
-{
-    COUNTER_START();
-    spi_flash_disable_interrupts_caches_and_other_cpu();
-    SpiFlashOpResult rc;
-    rc = spi_flash_unlock();
-    if (rc == SPI_FLASH_RESULT_OK) {
-        rc = SPIWrite(dest_addr, src, (int32_t) size);
-        COUNTER_ADD_BYTES(write, size);
-    }
-    spi_flash_enable_interrupts_caches_and_other_cpu();
-    COUNTER_STOP(write);
-    return spi_flash_translate_rc(rc);
-}
+#endif // CONFIG_FREERTOS_UNICORE
 
-esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size)
-{
-    COUNTER_START();
-    spi_flash_disable_interrupts_caches_and_other_cpu();
-    SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size);
-    COUNTER_ADD_BYTES(read, size);
-    spi_flash_enable_interrupts_caches_and_other_cpu();
-    COUNTER_STOP(read);
-    return spi_flash_translate_rc(rc);
-}
-
-static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc)
-{
-    switch (rc) {
-    case SPI_FLASH_RESULT_OK:
-        return ESP_OK;
-    case SPI_FLASH_RESULT_TIMEOUT:
-        return ESP_ERR_FLASH_OP_TIMEOUT;
-    case SPI_FLASH_RESULT_ERR:
-    default:
-        return ESP_ERR_FLASH_OP_FAIL;
-    }
-}
+/**
+ * The following two functions are replacements for Cache_Read_Disable and Cache_Read_Enable
+ * function in ROM. They are used to work around a bug where Cache_Read_Disable requires a call to
+ * Cache_Flush before Cache_Read_Enable, even if cached data was not modified.
+ */
 
 static const uint32_t cache_mask  = DPORT_APP_CACHE_MASK_OPSDRAM | DPORT_APP_CACHE_MASK_DROM0 |
         DPORT_APP_CACHE_MASK_DRAM1 | DPORT_APP_CACHE_MASK_IROM0 |
@@ -300,29 +203,3 @@ static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_sta
     }
 }
 
-#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
-
-static inline void dump_counter(spi_flash_counter_t* counter, const char* name)
-{
-    ESP_LOGI(TAG, "%s  count=%8d  time=%8dms  bytes=%8d\n", name,
-            counter->count, counter->time, counter->bytes);
-}
-
-const spi_flash_counters_t* spi_flash_get_counters()
-{
-    return &s_flash_stats;
-}
-
-void spi_flash_reset_counters()
-{
-    memset(&s_flash_stats, 0, sizeof(s_flash_stats));
-}
-
-void spi_flash_dump_counters()
-{
-    dump_counter(&s_flash_stats.read,  "read ");
-    dump_counter(&s_flash_stats.write, "write");
-    dump_counter(&s_flash_stats.erase, "erase");
-}
-
-#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h
new file mode 100644 (file)
index 0000000..899a31c
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2015-2016 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.
+// 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 ESP_SPI_FLASH_CACHE_UTILS_H
+#define ESP_SPI_FLASH_CACHE_UTILS_H
+
+/**
+ * This header file contains declarations of cache manipulation functions
+ * used both in flash_ops.c and flash_mmap.c.
+ *
+ * These functions are considered internal and are not designed to be called from applications.
+ */
+
+// Init mutex protecting access to spi_flash_* APIs
+void spi_flash_init_lock();
+
+// Take mutex protecting access to spi_flash_* APIs
+void spi_flash_op_lock();
+
+// Release said mutex
+void spi_flash_op_unlock();
+
+// Suspend the scheduler on both CPUs, disable cache.
+// Contrary to its name this doesn't do anything with interrupts, yet.
+// Interrupt disabling capability will be added once we implement
+// interrupt allocation API.
+void spi_flash_disable_interrupts_caches_and_other_cpu();
+
+// Enable cache, enable interrupts (to be added in future), resume scheduler
+void spi_flash_enable_interrupts_caches_and_other_cpu();
+
+
+#endif //ESP_SPI_FLASH_CACHE_UTILS_H
diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c
new file mode 100644 (file)
index 0000000..99e8ef7
--- /dev/null
@@ -0,0 +1,158 @@
+// Copyright 2015-2016 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.
+// 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.
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <freertos/semphr.h>
+#include <rom/spi_flash.h>
+#include <rom/cache.h>
+#include <soc/soc.h>
+#include <soc/dport_reg.h>
+#include "sdkconfig.h"
+#include "esp_ipc.h"
+#include "esp_attr.h"
+#include "esp_spi_flash.h"
+#include "esp_log.h"
+#include "cache_utils.h"
+
+#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
+static const char* TAG = "spi_flash";
+static spi_flash_counters_t s_flash_stats;
+
+#define COUNTER_START()     uint32_t ts_begin = xthal_get_ccount()
+#define COUNTER_STOP(counter)  \
+    do{ \
+        s_flash_stats.counter.count++; \
+        s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \\
+    } while(0)
+
+#define COUNTER_ADD_BYTES(counter, size) \
+    do { \
+        s_flash_stats.counter.bytes += size; \
+    } while (0)
+
+#else
+#define COUNTER_START()
+#define COUNTER_STOP(counter)
+#define COUNTER_ADD_BYTES(counter, size)
+
+#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
+
+static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc);
+
+void spi_flash_init()
+{
+    spi_flash_init_lock();
+#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
+    spi_flash_reset_counters();
+#endif
+}
+
+SpiFlashOpResult IRAM_ATTR spi_flash_unlock()
+{
+    static bool unlocked = false;
+    if (!unlocked) {
+        SpiFlashOpResult rc = SPIUnlock();
+        if (rc != SPI_FLASH_RESULT_OK) {
+            return rc;
+        }
+        unlocked = true;
+    }
+    return SPI_FLASH_RESULT_OK;
+}
+
+esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec)
+{
+    COUNTER_START();
+    spi_flash_disable_interrupts_caches_and_other_cpu();
+    SpiFlashOpResult rc;
+    rc = spi_flash_unlock();
+    if (rc == SPI_FLASH_RESULT_OK) {
+        rc = SPIEraseSector(sec);
+    }
+    spi_flash_enable_interrupts_caches_and_other_cpu();
+    COUNTER_STOP(erase);
+    return spi_flash_translate_rc(rc);
+}
+
+esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size)
+{
+    COUNTER_START();
+    spi_flash_disable_interrupts_caches_and_other_cpu();
+    SpiFlashOpResult rc;
+    rc = spi_flash_unlock();
+    if (rc == SPI_FLASH_RESULT_OK) {
+        rc = SPIWrite(dest_addr, src, (int32_t) size);
+        COUNTER_ADD_BYTES(write, size);
+    }
+    spi_flash_enable_interrupts_caches_and_other_cpu();
+    COUNTER_STOP(write);
+    return spi_flash_translate_rc(rc);
+}
+
+esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size)
+{
+    COUNTER_START();
+    spi_flash_disable_interrupts_caches_and_other_cpu();
+    SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size);
+    COUNTER_ADD_BYTES(read, size);
+    spi_flash_enable_interrupts_caches_and_other_cpu();
+    COUNTER_STOP(read);
+    return spi_flash_translate_rc(rc);
+}
+
+static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc)
+{
+    switch (rc) {
+    case SPI_FLASH_RESULT_OK:
+        return ESP_OK;
+    case SPI_FLASH_RESULT_TIMEOUT:
+        return ESP_ERR_FLASH_OP_TIMEOUT;
+    case SPI_FLASH_RESULT_ERR:
+    default:
+        return ESP_ERR_FLASH_OP_FAIL;
+    }
+}
+
+#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
+
+static inline void dump_counter(spi_flash_counter_t* counter, const char* name)
+{
+    ESP_LOGI(TAG, "%s  count=%8d  time=%8dms  bytes=%8d\n", name,
+            counter->count, counter->time, counter->bytes);
+}
+
+const spi_flash_counters_t* spi_flash_get_counters()
+{
+    return &s_flash_stats;
+}
+
+void spi_flash_reset_counters()
+{
+    memset(&s_flash_stats, 0, sizeof(s_flash_stats));
+}
+
+void spi_flash_dump_counters()
+{
+    dump_counter(&s_flash_stats.read,  "read ");
+    dump_counter(&s_flash_stats.write, "write");
+    dump_counter(&s_flash_stats.erase, "erase");
+}
+
+#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS