]> granicus.if.org Git - esp-idf/commitdiff
esp32: Fixes issues discussed during code review of MR!341
authorAlexey Gerenkov <alexey@espressif.com>
Tue, 10 Jan 2017 11:48:47 +0000 (14:48 +0300)
committerAlexey Gerenkov <alexey@espressif.com>
Thu, 12 Jan 2017 16:38:19 +0000 (19:38 +0300)
The following issues mentioned during MR!341 review were fixed:
1) Core dump test application description
2) Usage of CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH and CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
3) FLASH_GUARD_START macro usage is fixed in flash API
4) Core dump module logging facility
5) cache util functions doc updated
6) interactive delay before print core dump to uart
7) core dump partion support in build system

15 files changed:
components/esp32/Kconfig
components/esp32/core_dump.c
components/esp32/cpu_start.c
components/esp32/panic.c
components/espcoredump/test/component.mk [new file with mode: 0644]
components/espcoredump/test/test_core_dump.c [new file with mode: 0644]
components/freertos/include/freertos/FreeRTOSConfig.h
components/log/include/esp_log.h
components/partition_table/gen_esp32part.py
components/partition_table/partitions_singleapp_coredump.csv
components/partition_table/partitions_two_ota_coredump.csv
components/spi_flash/cache_utils.h
components/spi_flash/flash_ops.c
docs/core_dump.rst
docs/index.rst

index 981158514067daf0c4955e50130ac78a8859cf9f..3bdfd230cbeba8205ca75faf2f52298d297b0376 100644 (file)
@@ -66,12 +66,35 @@ choice ESP32_COREDUMP_TO_FLASH_OR_UART
 
 config ESP32_ENABLE_COREDUMP_TO_FLASH
     bool "Flash"
+    select ESP32_ENABLE_COREDUMP
 config ESP32_ENABLE_COREDUMP_TO_UART
     bool "UART"
+    select ESP32_ENABLE_COREDUMP
 config ESP32_ENABLE_COREDUMP_TO_NONE
     bool "None"
 endchoice
 
+config ESP32_ENABLE_COREDUMP
+    bool
+    default F
+    help
+        Enables/disable core dump module.
+
+config ESP32_CORE_DUMP_UART_DELAY
+    int "Core dump print to UART delay"
+    depends on ESP32_ENABLE_COREDUMP_TO_UART
+    default 0
+    help
+        Config delay (in ms) before printing core dump to UART.
+        Delay can be interrupted by pressing Enter key.
+
+config ESP32_CORE_DUMP_LOG_LEVEL
+    int "Core dump module logging level"
+    depends on ESP32_ENABLE_COREDUMP
+    default 1
+    help
+        Config core dump module logging level (0-5).
+
 # Not implemented and/or needs new silicon rev to work
 config MEMMAP_SPISRAM
     bool "Use external SPI SRAM chip as main memory"
index 13519badb795bdf68be86983f92ad4dedec19001..85301f4dd646da0f2162cbbc90c7c5f42715e7df 100644 (file)
 #include <string.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
+#include "soc/uart_reg.h"
+#include "soc/io_mux_reg.h"
+#include "soc/timer_group_struct.h"
+#include "soc/timer_group_reg.h"
+#include "driver/gpio.h"
 
 #include "esp_panic.h"
 #include "esp_partition.h"
 
-#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
+#if CONFIG_ESP32_ENABLE_COREDUMP
+#define LOG_LOCAL_LEVEL CONFIG_ESP32_CORE_DUMP_LOG_LEVEL
 #include "esp_log.h"
 const static char *TAG = "esp_core_dump";
 
+#define ESP_COREDUMP_LOGE( format, ... )  if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR)   { ets_printf(LOG_FORMAT(E, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); }
+#define ESP_COREDUMP_LOGW( format, ... )  if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN)    { ets_printf(LOG_FORMAT(W, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); }
+#define ESP_COREDUMP_LOGI( format, ... )  if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO)    { ets_printf(LOG_FORMAT(I, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); }
+#define ESP_COREDUMP_LOGD( format, ... )  if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG)   { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); }
+#define ESP_COREDUMP_LOGV( format, ... )  if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { ets_printf(LOG_FORMAT(V, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); }
+
+#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
+#define ESP_COREDUMP_LOG_PROCESS( format, ... )  if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG)   { ets_printf(LOG_FORMAT(D, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); }
+#else
+#define ESP_COREDUMP_LOG_PROCESS( format, ... )  do{/*(__VA_ARGS__);*/}while(0)
+#endif
+
+
 // TODO: allow user to set this in menuconfig or get tasks iteratively
 #define COREDUMP_MAX_TASKS_NUM    32
 
-typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len, int verb);
-typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv, int verb);
-typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv, int verb);
-typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len, int verb);
+typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len);
+typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv);
+typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv);
+typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len);
 
 typedef struct _core_dump_write_config_t
 {
@@ -40,19 +59,17 @@ typedef struct _core_dump_write_config_t
 
 } core_dump_write_config_t;
 
-static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg, int verb)
+static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg)
 {
     union
     {
         uint8_t    data8[12];
         uint32_t   data32[3];
     } rom_data;
-    //const esp_partition_t *core_part;
     esp_err_t err;
     TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM];
     UBaseType_t tcb_sz, task_num;
     uint32_t data_len = 0, i, len;
-    //size_t off;
 
     task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz);
     // take TCB padding into account, actual TCB size will be stored in header
@@ -63,22 +80,21 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
     // header + tasknum*(tcb + stack start/end + tcb addr)
     data_len = 3*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *));
     for (i = 0; i < task_num; i++) {
-        if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) {
+        if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
             // set correct stack top for current task
             tasks[i].pxTopOfStack = (StackType_t *)frame;
-            if (verb)
-                ets_printf("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1);
+            ESP_COREDUMP_LOG_PROCESS("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x", frame->exit, frame->pc, frame->ps, frame->a0, frame->a1);
         }
         else {
-            if (verb) {
-                XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack;
-                if (task_frame->exit == 0) {
-                    ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1);
-                }
-                else {
-                    XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack;
-                    ets_printf("Task EXIT/PC/PS/A0/SP %x %x %x %x %x\r\n", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1);
-                }
+            XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack;
+            if (task_frame->exit == 0) {
+                ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1);
+            }
+            else {
+#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
+                XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack;
+#endif
+                ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1);
             }
         }
 #if( portSTACK_GROWTH < 0 )
@@ -86,9 +102,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
 #else
         len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack;
 #endif
-        if (verb) {
-            ets_printf("Stack len = %lu (%x %x)\r\n", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack);
-        }
+        ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack);
         // take stack padding into account
         if (len % sizeof(uint32_t))
             len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t);
@@ -97,22 +111,20 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
 
     // prepare write
     if (write_cfg->prepare) {
-        err = write_cfg->prepare(write_cfg->priv, &data_len, verb);
+        err = write_cfg->prepare(write_cfg->priv, &data_len);
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to prepare core dump (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err);
             return;
         }
     }
 
-    if (verb) {
-        ets_printf("Core dump len = %lu\r\n", data_len);
-    }
+    ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu", data_len);
 
     // write start
     if (write_cfg->start) {
-        err = write_cfg->start(write_cfg->priv, verb);
+        err = write_cfg->start(write_cfg->priv);
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to start core dump (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err);
             return;
         }
     }
@@ -121,30 +133,28 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
     rom_data.data32[0] = data_len;
     rom_data.data32[1] = task_num;
     rom_data.data32[2] = tcb_sz;
-    err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb);
+    err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t));
     if (err != ESP_OK) {
-        ets_printf("ERROR: Failed to write core dump header (%d)!\r\n", err);
+        ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err);
         return;
     }
 
     // write tasks
     for (i = 0; i < task_num; i++) {
-        if (verb) {
-            ets_printf("Dump task %x\r\n", tasks[i].pxTCB);
-        }
+        ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].pxTCB);
         // save TCB address, stack base and stack top addr
         rom_data.data32[0] = (uint32_t)tasks[i].pxTCB;
         rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack;
         rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack;
-        err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t), verb);
+        err = write_cfg->write(write_cfg->priv, &rom_data, 3*sizeof(uint32_t));
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to write task header (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err);
             return;
         }
         // save TCB
-        err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz, verb);
+        err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz);
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to write TCB (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err);
             return;
         }
         // save task stack
@@ -156,18 +166,18 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
                 tasks[i].pxEndOfStack,
                 (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack
 #endif
-                , verb);
+                );
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to write task stack (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err);
             return;
         }
     }
 
     // write end
     if (write_cfg->end) {
-        err = write_cfg->end(write_cfg->priv, verb);
+        err = write_cfg->end(write_cfg->priv);
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to end core dump (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err);
             return;
         }
     }
@@ -202,7 +212,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint
     data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t);
     err = spi_flash_write(off, data, data_len);
     if (err != ESP_OK) {
-        ets_printf("ERROR: Failed to write data to flash (%d)!\r\n", err);
+        ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err);
         return 0;
     }
 
@@ -214,7 +224,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint
             rom_data.data8[k] = *(data + data_len + k);
         err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t));
         if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to finish write data to flash (%d)!\r\n", err);
+            ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err);
             return 0;
         }
         data_len += sizeof(uint32_t);
@@ -223,7 +233,7 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint
     return data_len;
 }
 
-static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len, int verb)
+static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len)
 {
     esp_err_t err;
     uint32_t sec_num;
@@ -231,7 +241,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le
 
     // add space for 2 magics. TODO: change to CRC
     if ((*data_len + 2*sizeof(uint32_t)) > s_core_part_size) {
-        ets_printf("ERROR: Not enough space to save core dump!\r\n");
+        ESP_COREDUMP_LOGE("Not enough space to save core dump!");
         return ESP_ERR_NO_MEM;
     }
     *data_len += 2*sizeof(uint32_t);
@@ -243,7 +253,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le
         sec_num++;
     err = spi_flash_erase_range(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE);
     if (err != ESP_OK) {
-        ets_printf("ERROR: Failed to erase flash (%d)!\r\n", err);
+        ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err);
         return err;
     }
 
@@ -257,7 +267,7 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr
 
     err = spi_flash_write(s_core_part_start + wr_data->off, &data32, sizeof(uint32_t));
     if (err != ESP_OK) {
-        ets_printf("ERROR: Failed to write to flash (%d)!\r\n", err);
+        ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err);
         return err;
     }
     wr_data->off += sizeof(uint32_t);
@@ -265,16 +275,17 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr
     return err;
 }
 
-static esp_err_t esp_core_dump_flash_write_start(void *priv, int verb)
+static esp_err_t esp_core_dump_flash_write_start(void *priv)
 {
     core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
     // save magic 1
     return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START);
 }
 
-static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb)
+static esp_err_t esp_core_dump_flash_write_end(void *priv)
 {
     core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
+#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
     uint32_t i;
     union
     {
@@ -282,27 +293,24 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv, int verb)
         uint32_t   data32[4];
     } rom_data;
 
-    if (verb) {
-        // TEST READ START
-        esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data));
-        if (err != ESP_OK) {
-            ets_printf("ERROR: Failed to read flash (%d)!\r\n", err);
-            return err;
-        }
-        else {
-            ets_printf("Data from flash:\r\n");
-            for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
-                ets_printf("%x\r\n", rom_data.data32[i]);
-            }
+    esp_err_t err = spi_flash_read(s_core_part_start + 0, &rom_data, sizeof(rom_data));
+    if (err != ESP_OK) {
+        ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err);
+        return err;
+    }
+    else {
+        ESP_COREDUMP_LOG_PROCESS("Data from flash:");
+        for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
+            ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]);
         }
-        // TEST READ END
     }
+#endif
 
     // save magic 2
     return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END);
 }
 
-static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len, int verb)
+static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len)
 {
     esp_err_t err = ESP_OK;
     core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
@@ -330,9 +338,9 @@ void esp_core_dump_to_flash(XtExcFrame *frame)
     wr_cfg.write = esp_core_dump_flash_write_data;
     wr_cfg.priv = &wr_data;
 
-    ets_printf("Save core dump to flash...\r\n");
-    esp_core_dump_write(frame, &wr_cfg, 0);
-    ets_printf("Core dump has been saved to flash.\r\n");
+    ESP_COREDUMP_LOGI("Save core dump to flash...");
+    esp_core_dump_write(frame, &wr_cfg);
+    ESP_COREDUMP_LOGI("Core dump has been saved to flash.");
 }
 #endif
 
@@ -362,27 +370,26 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8
     dst[j++] = '\0';
 }
 
-static esp_err_t esp_core_dump_uart_write_start(void *priv, int verb)
+static esp_err_t esp_core_dump_uart_write_start(void *priv)
 {
     esp_err_t err = ESP_OK;
     ets_printf("================= CORE DUMP START =================\r\n");
     return err;
 }
 
-static esp_err_t esp_core_dump_uart_write_end(void *priv, int verb)
+static esp_err_t esp_core_dump_uart_write_end(void *priv)
 {
     esp_err_t err = ESP_OK;
     ets_printf("================= CORE DUMP END =================\r\n");
     return err;
 }
 
-static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len, int verb)
+static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len)
 {
     esp_err_t err = ESP_OK;
     char buf[64 + 4], *addr = data;
     char *end = addr + data_len;
 
-
     while (addr < end) {
         size_t len = end - addr;
         if (len > 48) len = 48;
@@ -397,9 +404,21 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t
     return err;
 }
 
+static int esp_core_dump_uart_get_char() {
+    int i;
+    uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT;
+    if (reg)
+        i = READ_PERI_REG(UART_FIFO_REG(0));
+    else
+        i = -1;
+    return i;
+}
+
 void esp_core_dump_to_uart(XtExcFrame *frame)
 {
     core_dump_write_config_t wr_cfg;
+    uint32_t tm_end, tm_cur;
+    int ch;
 
     wr_cfg.prepare = NULL;
     wr_cfg.start = esp_core_dump_uart_write_start;
@@ -407,9 +426,27 @@ void esp_core_dump_to_uart(XtExcFrame *frame)
     wr_cfg.write = esp_core_dump_uart_write_data;
     wr_cfg.priv = NULL;
 
-    ets_printf("Print core dump to uart...\r\n");
-    esp_core_dump_write(frame, &wr_cfg, 0);
-    ets_printf("Core dump has been written to uart.\r\n");
+    //Make sure txd/rxd are enabled
+    gpio_pullup_dis(1);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD);
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
+
+    ESP_COREDUMP_LOGI("Press Enter to print core dump to UART...");
+    tm_end = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000) + CONFIG_ESP32_CORE_DUMP_UART_DELAY;
+    ch = esp_core_dump_uart_get_char();
+    while (!(ch == '\n' || ch == '\r')) {
+        tm_cur = xthal_get_ccount() / (XT_CLOCK_FREQ / 1000);
+        if (tm_cur >= tm_end)
+            break;
+        /* Feed the Cerberus. */
+        TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
+        TIMERG0.wdt_feed = 1;
+        TIMERG0.wdt_wprotect = 0;
+        ch = esp_core_dump_uart_get_char();
+    }
+    ESP_COREDUMP_LOGI("Print core dump to uart...");
+    esp_core_dump_write(frame, &wr_cfg);
+    ESP_COREDUMP_LOGI("Core dump has been written to uart.");
 }
 #endif
 
index 2ae4260bd5bc48d145e08c164e621c54515bbade..5ae68fc64374451a52856eb71207cd0cf4d95365 100644 (file)
@@ -217,7 +217,7 @@ void start_cpu0_default(void)
     }
 #endif
 
-#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
+#if CONFIG_ESP32_ENABLE_COREDUMP
     esp_core_dump_init();
 #endif
 
index 09ce520dede7640b8879db0a24cfe96c2e553ca5..3db09370ac36c50d4f96bed7d528b715042c3bf5 100644 (file)
@@ -162,39 +162,39 @@ void panicHandler(XtExcFrame *frame)
         reason = reasons[regs[20]];
     }
     haltOtherCore();
-    esp_panicPutStr("Guru Meditation Error: Core ");
-    esp_panicPutDec(xPortGetCoreID());
-    esp_panicPutStr(" panic'ed (");
+    panicPutStr("Guru Meditation Error: Core ");
+    panicPutDec(xPortGetCoreID());
+    panicPutStr(" panic'ed (");
     if (!abort_called) {
-        esp_panicPutStr(reason);
-        esp_panicPutStr(")\r\n");
+        panicPutStr(reason);
+        panicPutStr(")\r\n");
             if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) {
                 int debugRsn;
                 asm("rsr.debugcause %0":"=r"(debugRsn));
-                esp_panicPutStr("Debug exception reason: ");
-                if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) esp_panicPutStr("SingleStep ");
-                if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) esp_panicPutStr("HwBreakpoint ");
+                panicPutStr("Debug exception reason: ");
+                if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep ");
+                if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint ");
                 if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) {
                     //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK
                     //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the
                     //debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0.
                     if (debugRsn&(1<<8)) {
 #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
-                        esp_panicPutStr("Stack canary watchpoint triggered ");
+                        panicPutStr("Stack canary watchpoint triggered ");
 #else
-                        esp_panicPutStr("Watchpoint 1 triggered ");
+                        panicPutStr("Watchpoint 1 triggered ");
 #endif
                     } else {
-                        esp_panicPutStr("Watchpoint 0 triggered ");
+                        panicPutStr("Watchpoint 0 triggered ");
                     }
                 }
-                if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) esp_panicPutStr("BREAK instr ");
-                if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) esp_panicPutStr("BREAKN instr ");
-                if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) esp_panicPutStr("DebugIntr ");
-                esp_panicPutStr("\r\n");
+                if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr ");
+                if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr ");
+                if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr ");
+                panicPutStr("\r\n");
             }
         } else {
-            esp_panicPutStr("abort)\r\n");
+            panicPutStr("abort)\r\n");
         }
 
     if (esp_cpu_in_ocd_debug_mode()) {
diff --git a/components/espcoredump/test/component.mk b/components/espcoredump/test/component.mk
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/components/espcoredump/test/test_core_dump.c b/components/espcoredump/test/test_core_dump.c
new file mode 100644 (file)
index 0000000..41137f9
--- /dev/null
@@ -0,0 +1,105 @@
+/* Application For Core Dumps Generation
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_system.h"
+#include "nvs_flash.h"
+
+
+// task crash indicators
+#define TCI_NULL_PTR    0x1
+#define TCI_UNALIGN_PTR 0x2
+#define TCI_FAIL_ASSERT 0x4
+
+volatile unsigned long crash_flags = TCI_UNALIGN_PTR;
+
+void bad_ptr_func()
+{
+    unsigned long *ptr = (unsigned long *)0;
+    volatile int cnt;
+    int i = 0;
+
+    for (i = 0; i < 1000; i++) {
+        cnt++;
+    }
+
+    if(crash_flags & TCI_NULL_PTR) {
+        printf("Write to bad address 0x%lx.\n", (unsigned long)ptr);
+        *ptr = 0xDEADBEEF;
+    }
+}
+
+void bad_ptr_task(void *pvParameter)
+{
+    printf("Task 'bad_ptr_task' start.\n");
+    while (1) {
+        vTaskDelay(1000 / portTICK_RATE_MS);
+        printf("Task 'bad_ptr_task' run.\n");
+        bad_ptr_func();
+    }
+    fflush(stdout);
+}
+
+void recur_func()
+{
+    static int rec_cnt;
+    unsigned short *ptr = (unsigned short *)0x5;
+    volatile int cnt;
+    int i = 0;
+
+    if (rec_cnt++ > 2) {
+        return;
+    }
+    for (i = 0; i < 4; i++) {
+        cnt++;
+        if(i == 2) {
+            recur_func();
+            break;
+        }
+    }
+
+    if(crash_flags & TCI_UNALIGN_PTR) {
+        printf("Write to unaligned address 0x%lx.\n", (unsigned long)ptr);
+        *ptr = 0xDEAD;
+    }
+}
+
+void unaligned_ptr_task(void *pvParameter)
+{
+    printf("Task 'unaligned_ptr_task' start.\n");
+    while (1) {
+        vTaskDelay(1000 / portTICK_RATE_MS);
+        printf("Task 'unaligned_ptr_task' run.\n");
+        recur_func();
+    }
+    fflush(stdout);
+}
+
+void failed_assert_task(void *pvParameter)
+{
+    printf("Task 'failed_assert_task' start.\n");
+    while (1) {
+        vTaskDelay(1000 / portTICK_RATE_MS);
+        printf("Task 'failed_assert_task' run.\n");
+        if(crash_flags & TCI_FAIL_ASSERT) {
+            printf("Assert.\n");
+            assert(0);
+        }
+    }
+    fflush(stdout);
+}
+
+void app_main()
+{
+    nvs_flash_init();
+    xTaskCreate(&bad_ptr_task, "bad_ptr_task", 2048, NULL, 5, NULL);
+    xTaskCreatePinnedToCore(&unaligned_ptr_task, "unaligned_ptr_task", 2048, NULL, 7, NULL, 1);
+    xTaskCreatePinnedToCore(&failed_assert_task, "failed_assert_task", 2048, NULL, 10, NULL, 0);
+}
index 7c9d8fd972b25e681e735eb9297d20f16ed04ab6..b2fc077bc3035d92171f59a5725148e98996867a 100644 (file)
 #define configXT_BOARD                      1   /* Board mode */
 #define configXT_SIMULATOR                                     0
 
-#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH | CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
+#if CONFIG_ESP32_ENABLE_COREDUMP
 #define configENABLE_TASK_SNAPSHOT                     1
 #endif
 
index 33bc10b42afa67ca9e76bb14f2c7da01506c4724..6fda8d6054ea299d98eed9094d4862fb68f90e06 100644 (file)
@@ -75,6 +75,16 @@ void esp_log_set_vprintf(vprintf_like_t func);
  */
 uint32_t esp_log_timestamp(void);
 
+/**
+ * @brief Function which returns timestamp to be used in log output
+ *
+ * This function uses HW cycle counter and does not depend on OS,
+ * so it can be safely used after application crash.
+ *
+ * @return timestamp, in milliseconds
+ */
+uint32_t esp_log_early_timestamp(void);
+
 /**
  * @brief Write message into the log
  *
index a4412ad45b80c98f66c13f1846512450970d7df0..04912048850ad34e189c1116f2a705791dffb91a 100755 (executable)
@@ -127,6 +127,7 @@ class PartitionDefinition(object):
             "ota" : 0x00,
             "phy" : 0x01,
             "nvs" : 0x02,
+            "coredump" : 0x03,
             "esphttpd" : 0x80,
             "fat" : 0x81,
             "spiffs" : 0x82,
index 96719a425a09601fb97bc03a3e20a15209f459d0..a9f12c0fd3d044beaec7a5603c5debbd282d37cd 100644 (file)
@@ -3,4 +3,4 @@
 nvs,      data, nvs,     0x9000,  0x6000
 phy_init, data, phy,     0xf000,  0x1000
 factory,  app,  factory, 0x10000, 1M
-coredump, data, 3,       ,        64K
+coredump, data, coredump,,        64K
index 3d2b34ec9a75cff78a6631ce76efc3a83f1e4e1d..64d70b0d8ebf76412daae9a96aa6602a31a423f5 100644 (file)
@@ -4,6 +4,6 @@ nvs,      data, nvs,     0x9000,  0x4000
 otadata,  data, ota,     0xd000,  0x2000
 phy_init, data, phy,     0xf000,  0x1000
 factory,  0,    0,       0x10000,  1M
-coredump, data, 3,       ,         64K
+coredump, data, coredump,,         64K
 ota_0,    0,    ota_0,   ,         1M
 ota_1,    0,    ota_1,   ,         1M
index a5442d1af7a0c7cc7febdb690250935b99357ad0..598b8fba77884c0a51f31388282ec6cf8ba8cfdf 100644 (file)
@@ -41,13 +41,11 @@ void spi_flash_disable_interrupts_caches_and_other_cpu();
 void spi_flash_enable_interrupts_caches_and_other_cpu();
 
 // Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs.
-// This function is implied to be called from panic handler
-// (when non-current CPU is halted and can not execute code from flash) or when no OS is present.
+// This function is implied to be called when other CPU is not running or running code from IRAM.
 void spi_flash_disable_interrupts_caches_and_other_cpu_no_os();
 
 // Enable cache, enable interrupts on current CPU.
-// This function is implied to be called from panic handler
-// (when non-current CPU is halted and can not execute code from flash) or when no OS is present.
+// This function is implied to be called when other CPU is not running or running code from IRAM.
 void spi_flash_enable_interrupts_caches_no_os();
 
 #endif //ESP_SPI_FLASH_CACHE_UTILS_H
index 831679f437af75e1bb67087ed3482bc860d307c2..fffe487bd156d50c73662c14506d1a401bd8f948 100644 (file)
@@ -58,9 +58,6 @@ static spi_flash_counters_t s_flash_stats;
 
 #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
 
-#define FLASH_GUARD_START(_gp_) do{if((_gp_)) (_gp_)->start();}while(0)
-#define FLASH_GUARD_END(_gp_)   do{if((_gp_)) (_gp_)->end();}while(0)
-
 static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc);
 
 const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = {
@@ -106,6 +103,18 @@ SpiFlashOpResult IRAM_ATTR spi_flash_unlock()
     return SPI_FLASH_RESULT_OK;
 }
 
+static inline void spi_flash_guard_start()
+{
+    if (s_flash_guard_ops)
+        s_flash_guard_ops->start();
+}
+
+static inline void spi_flash_guard_end()
+{
+    if (s_flash_guard_ops)
+        s_flash_guard_ops->end();
+}
+
 esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
 {
     return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
@@ -126,7 +135,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
     size_t end = start + size / SPI_FLASH_SEC_SIZE;
     const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE;
     COUNTER_START();
-    FLASH_GUARD_START(s_flash_guard_ops);
+    spi_flash_guard_start();
     SpiFlashOpResult rc;
     rc = spi_flash_unlock();
     if (rc == SPI_FLASH_RESULT_OK) {
@@ -142,7 +151,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
             }
         }
     }
-    FLASH_GUARD_END(s_flash_guard_ops);
+    spi_flash_guard_end();
     COUNTER_STOP(erase);
     return spi_flash_translate_rc(rc);
 }
@@ -180,9 +189,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
     if (left_size > 0) {
         uint32_t t = 0xffffffff;
         memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size);
-        FLASH_GUARD_START(s_flash_guard_ops);
+        spi_flash_guard_start();
         rc = SPIWrite(left_off, &t, 4);
-        FLASH_GUARD_END(s_flash_guard_ops);
+        spi_flash_guard_end();
         if (rc != SPI_FLASH_RESULT_OK) {
             goto out;
         }
@@ -198,9 +207,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
         bool in_dram = true;
 #endif
         if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) {
-            FLASH_GUARD_START(s_flash_guard_ops);
+            spi_flash_guard_start();
             rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size);
-            FLASH_GUARD_END(s_flash_guard_ops);
+            spi_flash_guard_end();
             if (rc != SPI_FLASH_RESULT_OK) {
                 goto out;
             }
@@ -214,9 +223,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
                 uint32_t t[8];
                 uint32_t write_size = MIN(mid_size, sizeof(t));
                 memcpy(t, srcc + mid_off, write_size);
-                FLASH_GUARD_START(s_flash_guard_ops);
+                spi_flash_guard_start();
                 rc = SPIWrite(dst + mid_off, t, write_size);
-                FLASH_GUARD_END(s_flash_guard_ops);
+                spi_flash_guard_end();
                 if (rc != SPI_FLASH_RESULT_OK) {
                     goto out;
                 }
@@ -229,9 +238,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
     if (right_size > 0) {
         uint32_t t = 0xffffffff;
         memcpy(&t, srcc + right_off, right_size);
-        FLASH_GUARD_START(s_flash_guard_ops);
+        spi_flash_guard_start();
         rc = SPIWrite(dst + right_off, &t, 4);
-        FLASH_GUARD_END(s_flash_guard_ops);
+        spi_flash_guard_end();
         if (rc != SPI_FLASH_RESULT_OK) {
             goto out;
         }
@@ -291,7 +300,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
 
     SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
     COUNTER_START();
-    FLASH_GUARD_START(s_flash_guard_ops);
+    spi_flash_guard_start();
     /* To simplify boundary checks below, we handle small reads separately. */
     if (size < 16) {
         uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */
@@ -365,7 +374,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
         memcpy(dstc + pad_right_off, t, pad_right_size);
     }
 out:
-    FLASH_GUARD_END(s_flash_guard_ops);
+    spi_flash_guard_end();
     COUNTER_STOP(read);
     return spi_flash_translate_rc(rc);
 }
index 0d0624cc8993aeee87462b4b188b942b039efc7a..a8e328996fd2a8c9d361e069f74b33226d5fcfff 100644 (file)
@@ -1,10 +1,10 @@
 ESP32 Core Dump
-================
+===============
 
 Overview
 --------
 
-ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analisys of software state at the moment of failure.
+ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analysis of software state at the moment of failure.
 Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse
 the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks.
 So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash. 
@@ -30,16 +30,16 @@ Save core dump to flash
 When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically 
 allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump 
 as it is shown below::
-  
+
   # Name,   Type, SubType, Offset,  Size
   # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
   nvs,      data, nvs,     0x9000,  0x6000
   phy_init, data, phy,     0xf000,  0x1000
   factory,  app,  factory, 0x10000, 1M
-  coredump, data, 3,       ,        64K
-  
+  coredump, data, coredump,,        64K
+
 There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and 
-sub-type should be 3. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes. 
+sub-type should be 'coredump'. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes.
 This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes.
 
 The example of generic command to analyze core dump from flash is: `espcoredump.py -p </path/to/serial/port> info_corefile </path/to/program/elf/file>`
@@ -55,73 +55,27 @@ or `espcoredump.py -p </path/to/serial/port> dbg_corefile -t b64 -c </path/to/sa
 Base64-encoded body of core dump will be between the following header and footer::
 
  ================= CORE DUMP START =================
- <body of base64-encoded core dump, copy it to file on disk>
+ <body of base64-encoded core dump, save it to file on disk>
  ================= CORE DUMP END ===================
 
-Command Options For 'espcoredump.py'
---------------------------------------------
-
-usage: coredumper [-h] [--chip {auto,esp32}] [--port PORT] [--baud BAUD]
-                  {dbg_corefile,info_corefile} ...
-
-espcoredump.py v0.1-dev - ESP32 Core Dump Utility
-
-positional arguments:
-  {dbg_corefile,info_corefile}
-                        Run coredumper {command} -h for additional help
-    dbg_corefile        Starts GDB debugging session with specified corefile
-    info_corefile       Print core dump info from file
-
-optional arguments:
-  -h, --help            show this help message and exit
-  --chip {auto,esp32}, -c {auto,esp32}
-                        Target chip type
-  --port PORT, -p PORT  Serial port device
-  --baud BAUD, -b BAUD  Serial port baud rate used when flashing/reading
-
-
-usage: coredumper info_corefile [-h] [--gdb GDB] [--core CORE]
-                                [--core-format CORE_FORMAT] [--off OFF]
-                                [--save-core SAVE_CORE] [--print-mem]
-                                prog
-
-positional arguments:
-  prog                  Path to program's ELF binary
-
-optional arguments:
-  -h, --help            show this help message and exit
-  --gdb GDB, -g GDB     Path to gdb
-  --core CORE, -c CORE  Path to core dump file (if skipped core dump will be
-                        read from flash)
-  --core-format CORE_FORMAT, -t CORE_FORMAT
-                        (elf, raw or b64). File specified with "-c" is an ELF
-                        ("elf"), raw (raw) or base64-encoded (b64) binary
-  --off OFF, -o OFF     Ofsset of coredump partition in flash (type "make
-                        partition_table" to see).
-  --save-core SAVE_CORE, -s SAVE_CORE
-                        Save core to file. Othwerwise temporary core file will
-                        be deleted. Does not work with "-c"
-  --print-mem, -m       Print memory dump
-
-
-usage: coredumper dbg_corefile [-h] [--gdb GDB] [--core CORE]
-                               [--core-format CORE_FORMAT] [--off OFF]
-                               [--save-core SAVE_CORE]
-                               prog
-
-positional arguments:
-  prog                  Path to program's ELF binary
-
-optional arguments:
-  -h, --help            show this help message and exit
-  --gdb GDB, -g GDB     Path to gdb
-  --core CORE, -c CORE  Path to core dump file (if skipped core dump will be
-                        read from flash)
-  --core-format CORE_FORMAT, -t CORE_FORMAT
-                        (elf, raw or b64). File specified with "-c" is an ELF
-                        ("elf"), raw (raw) or base64-encoded (b64) binary
-  --off OFF, -o OFF     Ofsset of coredump partition in flash (type "make
-                        partition_table" to see).
-  --save-core SAVE_CORE, -s SAVE_CORE
-                        Save core to file. Othwerwise temporary core file will
-                        be deleted. Ignored with "-c"
+Running 'espcoredump.py'
+------------------------------------
+
+Generic command syntax:
+
+`espcoredump.py [options] command [args]`
+
+:Script Options:
+    * --chip,-c {auto,esp32}. Target chip type. Supported values are `auto` and `esp32`.
+    * --port,-p PORT. Serial port device.
+    * --baud,-b BAUD. Serial port baud rate used when flashing/reading.
+:Commands:
+    * info_corefile. Retrieve core dump and print useful info.
+    * dbg_corefile. Retrieve core dump and start GDB session with it.
+:Command Arguments:
+    * --gdb,-g GDB.                 Path to gdb to use for data retrieval.
+    * --core,-c CORE.               Path to core dump file to use (if skipped core dump will be read from flash).
+    * --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format.
+    * --off,-o OFF.                 Ofsset of coredump partition in flash (type "make partition_table" to see it).
+    * --save-core,-s SAVE_CORE.     Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c".
+    * --print-mem,-m                Print memory dump. Used only with "info_corefile".
index 2d9a62f14b8a2362f05761e2b23902630a23ea48..b994648848bcd80e7f90a530ee6e0e5bfac5e711 100644 (file)
@@ -35,6 +35,7 @@ Contents:
    partition-tables
    build_system
    openocd
+   core_dump
    Flash encryption <security/flash-encryption>
    Secure Boot <security/secure-boot>
    ULP coprocessor <api/ulp.rst>