|
|
@@ -0,0 +1,153 @@
|
|
|
+/*
|
|
|
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * In this example, we show you a way to make an ISR-based callback work during Flash operations, when the ISR-based
|
|
|
+ * callback is put in Flash.
|
|
|
+ *
|
|
|
+ * Please read the README.md to know more details about this feature!
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdbool.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include "sdkconfig.h"
|
|
|
+#include "esp_log.h"
|
|
|
+#include "esp_attr.h"
|
|
|
+#include "esp_cpu.h"
|
|
|
+#include "esp_partition.h"
|
|
|
+#include "driver/gptimer.h"
|
|
|
+
|
|
|
+
|
|
|
+#define TIMER_RESOLUTION_HZ (1 * 1000 * 1000) // 1MHz resolution
|
|
|
+#define TIMER_ALARM_PERIOD_S 1 // Alarm period 1s
|
|
|
+
|
|
|
+#if CONFIG_IDF_TARGET_ESP32C3
|
|
|
+#define CPU_FREQ_MHZ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ
|
|
|
+#endif
|
|
|
+
|
|
|
+#define RECORD_TIME_PREPARE() uint32_t __t1, __t2
|
|
|
+#define RECORD_TIME_START() do {__t1 = esp_cpu_get_ccount();} while(0)
|
|
|
+#define RECORD_TIME_END(p_time) do{__t2 = esp_cpu_get_ccount(); p_time = (__t2 - __t1);} while(0)
|
|
|
+#define GET_US_BY_CCOUNT(t) ((double)(t)/CPU_FREQ_MHZ)
|
|
|
+
|
|
|
+const static char *TAG = "Example";
|
|
|
+DRAM_ATTR static uint32_t s_t1;
|
|
|
+DRAM_ATTR static uint32_t s_flash_func_t2;
|
|
|
+DRAM_ATTR static uint32_t s_iram_func_t2;
|
|
|
+
|
|
|
+
|
|
|
+static NOINLINE_ATTR void s_function_in_flash(void)
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * - Here we will have few instructions in .flash.text
|
|
|
+ * - Cache will read from Flash to get the instructions synchronized.
|
|
|
+ * - CPU will execute around 400 times.
|
|
|
+ */
|
|
|
+ for (int i = 0; i < 100; i++) {
|
|
|
+ asm volatile("nop");
|
|
|
+ }
|
|
|
+
|
|
|
+ s_flash_func_t2 = esp_cpu_get_ccount();
|
|
|
+}
|
|
|
+
|
|
|
+static IRAM_ATTR NOINLINE_ATTR void s_funtion_in_iram(void)
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * - Here we will have few instructions in .iram0.text
|
|
|
+ * - CPU will execute around 400 times.
|
|
|
+ */
|
|
|
+ for (int i = 0; i < 100; i++) {
|
|
|
+ asm volatile("nop");
|
|
|
+ }
|
|
|
+
|
|
|
+ s_iram_func_t2 = esp_cpu_get_ccount();
|
|
|
+}
|
|
|
+
|
|
|
+static bool IRAM_ATTR on_gptimer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
|
|
|
+{
|
|
|
+ bool is_flash = *(bool *)user_ctx;
|
|
|
+ s_t1 = esp_cpu_get_ccount();
|
|
|
+
|
|
|
+ if (is_flash) {
|
|
|
+ s_function_in_flash();
|
|
|
+ } else {
|
|
|
+ s_funtion_in_iram();
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static const esp_partition_t *s_get_partition(void)
|
|
|
+{
|
|
|
+ //Find the "storage1" partition defined in `partitions.csv`
|
|
|
+ const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1");
|
|
|
+ if (!result) {
|
|
|
+ ESP_LOGE(TAG, "Can't find the partition, please define it correctly in `partitions.csv`");
|
|
|
+ abort();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+void app_main(void)
|
|
|
+{
|
|
|
+ //Get the partition used for SPI1 erase operation
|
|
|
+ const esp_partition_t *part = s_get_partition();
|
|
|
+ ESP_LOGI(TAG, "found partition '%s' at offset 0x%x with size 0x%x", part->label, part->address, part->size);
|
|
|
+ //Erase whole region
|
|
|
+ ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
|
|
+
|
|
|
+ gptimer_handle_t gptimer = NULL;
|
|
|
+ gptimer_config_t timer_config = {
|
|
|
+ .clk_src = GPTIMER_CLK_SRC_APB,
|
|
|
+ .direction = GPTIMER_COUNT_UP,
|
|
|
+ .resolution_hz = TIMER_RESOLUTION_HZ,
|
|
|
+ };
|
|
|
+ ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
|
|
+
|
|
|
+ gptimer_alarm_config_t alarm_config = {
|
|
|
+ .reload_count = 0,
|
|
|
+ .alarm_count = 1 * 1000 * 1000,
|
|
|
+ .flags.auto_reload_on_alarm = false,
|
|
|
+ };
|
|
|
+ ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
|
|
+
|
|
|
+ gptimer_event_callbacks_t cbs = {
|
|
|
+ .on_alarm = on_gptimer_alarm_cb,
|
|
|
+ };
|
|
|
+ bool is_flash = true;
|
|
|
+ ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, &is_flash));
|
|
|
+
|
|
|
+ ESP_ERROR_CHECK(gptimer_start(gptimer));
|
|
|
+
|
|
|
+ uint32_t erase_time = 0;
|
|
|
+ RECORD_TIME_PREPARE();
|
|
|
+
|
|
|
+ RECORD_TIME_START();
|
|
|
+ ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
|
|
+ RECORD_TIME_END(erase_time);
|
|
|
+
|
|
|
+ ESP_ERROR_CHECK(gptimer_stop(gptimer));
|
|
|
+ ESP_LOGI(TAG, "Flash Driver Erase Operation finishes, duration:\n\t\t%0.2f us", GET_US_BY_CCOUNT(erase_time));
|
|
|
+ ESP_LOGI(TAG, "During Erase, ISR callback function(in flash) response time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_flash_func_t2 - s_t1));
|
|
|
+
|
|
|
+ //Let the timer alarm callback to run code reside in .iram0.text
|
|
|
+ is_flash = false;
|
|
|
+ ESP_ERROR_CHECK(gptimer_set_raw_count(gptimer, 0));
|
|
|
+ ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
|
|
+ ESP_ERROR_CHECK(gptimer_start(gptimer));
|
|
|
+
|
|
|
+ RECORD_TIME_START();
|
|
|
+ //Erase whole region
|
|
|
+ ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
|
|
+ RECORD_TIME_END(erase_time);
|
|
|
+
|
|
|
+ ESP_ERROR_CHECK(gptimer_stop(gptimer));
|
|
|
+ ESP_LOGI(TAG, "Flash Driver Erase Operation finishes, duration:\n\t\t%0.2f us", GET_US_BY_CCOUNT(erase_time));
|
|
|
+ ESP_LOGI(TAG, "During Erase, ISR callback function(in iram) response time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_iram_func_t2 - s_t1));
|
|
|
+
|
|
|
+ ESP_LOGI(TAG, "Finish");
|
|
|
+ ESP_ERROR_CHECK(gptimer_del_timer(gptimer));
|
|
|
+}
|