[esp32] Guru Meditation 错误解析及解决方案

esp32 Guru Meditation 错误解决方案

Guru Meditation

  • 本节将对打印在 Guru Meditation Error: Core panic'ed后面括号中的致错原因进行逐一解释。

IllegalInstruction

此 CPU 异常表示当前执行的指令不是有效指令,引起此错误的常见原因包括:

  • FreeRTOS 中的任务函数已返回。在 FreeRTOS 中,如果想终止任务函数,需要调用 vTaskDelete()函数释放当前任务的资源,而不是直接返回。
  • 无法从 SPI Flash 中加载下一条指令,这通常发生在:
    1. 应用程序将 SPI Flash 的引脚重新配置为其它功能(如 GPIO,UART 等等)。有关 SPI Flash 引脚的详细信息,请参阅硬件设计指南和芯片/模组的数据手册。
    2. 某些外部设备意外连接到 SPI Flash 的引脚上,干扰了 ESP32 和 SPI Flash 之间的通信。

InstrFetchProhibited

此 CPU 异常表示 CPU 无法加载指令,因为指令的地址不在 IRAM 或者 IROM 中的有效区域中。

通常这意味着代码中调用了并不指向有效代码块的函数指针。这种情况下,可以查看 PC(程序计数器)寄存器的值并做进一步判断:若为 0 或者其它非法值(即只要不是 0x4xxxxxxx 的情况),则证实确实是该原因。

若PC指针大规模指向无效区域,那么coredump发生的可能原因某个引脚接触不良等原因导致。

详情可以对照下面esp32映射图,确定其 pc 指针位置(并不局限于本节使用)。
[esp32] Guru Meditation 错误解析及解决方案_第1张图片

LoadProhibited, StoreProhibited

当应用程序尝试读取或写入无效的内存位置时,会发生此类 CPU 异常。此类无效内存地址可以在寄存器转储的EXCVADDR 中找到。如果该地址为零,通常意味着应用程序正尝试解引用一个 NULL指针。如果该地址接近于零,则通常意味着应用程序尝试访问某个结构体的成员,但是该结构体的指针为 NULL。如果该地址是其它非法值(不在 0x3fxxxxxx - 0x6xxxxxxx 的范围内),则可能意味着用于访问数据的指针未初始化或者已经损坏。

IntegerDivideByZero

应用程序尝试将整数除以零。

LoadStoreAlignment

应用程序尝试读取/写入的内存位置不符合加载/存储指令对字节对齐大小的要求,例如,32 位加载指令只能访问 4 字节对齐的内存地址,而 16 位加载指令只能访问 2 字节对齐的内存地址。

LoadStoreError

应用程序尝试从仅支持 32 位加载/存储的内存区域执行 8 位或 16 位加载/存储操作,例如,解引用一个指向指令内存区域的 char* 指针就会导致这样的错误。

Unhandled debug exception

这后面通常会再跟一条消息:

Debug exception reason: Stack canary watchpoint triggered (task_name)

此错误表示应用程序写入的位置越过了 task_name 任务堆栈的末尾,请注意,并非每次堆栈溢出都会触发此错误。任务有可能会绕过堆栈金丝雀(stack canary)的位置访问堆栈,在这种情况下,监视点就不会被触发。

Interrupt wdt timeout on CPU0 / CPU1

这表示发生了中断看门狗超时.

Cache disabled but cached memory region accessed

@ 此问题很多人遇到过,特别在这总结一下, 该部分内容感谢不方便透露姓名的张同学提供帮助

该问题原因在于:在 cache 被禁用期间(例如在使用 spi_flash API 读取/写入/擦除/映射 SPI Flash 的时候),发生了 IRAM-Safe 中断并且中断程序访问了 flash 的资源。通常发生在中断处理程序调用了在 flash 中的程序,引用了 flash 中的常量。值得注意的是,当在中断处理程序里使用 double 型变量时,由于 double 型变量操作的实现是软件实现的,所以该实现部分也被存放在了 flash 中(例如强制类型转换操作)。
解决办法就是:

  1. 给在中断中访问的函数加上IRAM_ATTR修饰符
  2. 给在中断中访问的常量加 DRAM_ATTR 修饰符
  3. 不在中断处理程序中使用 double 类型操作
  • 需要注意的是:
    1.该异常只发生在 IRAM-Safe 中断,即使用 ESP_INTR_FLAG_IRAM注册的中断处理程序。
    2.该异常只有在 cache 被禁期间会出现,所以当 cache 未被禁用期间不会出现,换句话说该异常具有随机性。
    3.编译器可能会将一些不会被改变的变量放入 .rodata 段中,哪怕程序员没有加 const 修饰符。对于 ESP32 的应用程序来说,.rodata 意味着该部分位于 flash 中。对于字符串变量,这种情况很容易被察觉,但是对于某些常量,就不是那么好察觉了。例如下面的情况
static timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1};

这里定义了指针数组 TG,该数组元素是两个硬件定时器0,1的寄存器首地址,该指针数组的元素不会被改变,所以会被编译器放入 .rodata 中。此时在定时器中断里操作了 TG,又恰好这个中断发生在 cache 被禁期间,那么 CPU 就会抛出该异常。

  • 出现该问题的调试技巧
  1. CPU 抛出该类型异常时,通常异常位置就是出错位置,需要细看

Guru Meditation Error: Core  1 panic'ed (Cache disabled but cached memory region accessed)
Core 1 register dump:
PC      : 0x4011b808  PS      : 0x00060034  A0      : 0x8008c664  A1      : 0x3ffbeca0 
0x4011b808: __fixunsdfdi at /builds/idf/crosstool-NG/.build/src/gcc-5.2.0/libgcc/config/xtensa/ieee754-df.S:2041
A2      : 0x0099fd28  A3      : 0x00000000  A4      : 0x3ffbf6a0  A5      : 0x3f7909f1 
A6      : 0x00000000  A7      : 0x00013ffc  A8      : 0x80086136  A9      : 0x3ffbec80 
A10     : 0xfffffffc  A11     : 0x40ddd57f  A12     : 0x000007fe  A13     : 0x00000000 
A14     : 0x65580000  A15     : 0x00000000  SAR     : 0x00000014  EXCCAUSE: 0x00000007 
EXCVADDR: 0x00000000  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0x00000000 
Core 1 was running in ISR context:
EPC1    : 0x40086914  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x4011b808
0x40086914: spi_flash_enable_interrupts_caches_and_other_cpu at /home/chenzhengwei/esp/esp-idf/components/spi_flash/cache_utils.c:147
0x4011b808: __fixunsdfdi at /builds/idf/crosstool-NG/.build/src/gcc-5.2.0/libgcc/config/xtensa/ieee754-df.S:2041
Backtrace: 0x4011b808:0x3ffbeca0 0x4008c661:0x3ffbecd0 0x4008c69d:0x3ffbecf0 0x400857e5:0x3ffbed10 0x40086911:0x3ffd0970 0x40086cc9:0x3ffd0990 0x40117789:0x3ffd09e0 0x4011785e:0x3ffd0a10 0x401179ea:0x3ffd0a30 0x40116c51:0x3ffd0aa0 0x40117109:0x3ffd0b20 0x40116729:0x3ffd0b90 0x40105c36:0x3ffd0bd0 0x401057d7:0x3ffd0c10 0x401058ce:0x3ffd0c40 0x4008dfb5:0x3ffd0c90
0x4011b808: __fixunsdfdi at /builds/idf/crosstool-NG/.build/src/gcc-5.2.0/libgcc/config/xtensa/ieee754-df.S:2041
0x4008c661: gpio_isr_loop at /home/chenzhengwei/esp/esp-idf/components/driver/gpio.c:433
0x4008c69d: gpio_intr_service at /home/chenzhengwei/esp/esp-idf/components/driver/gpio.c:433
0x400857e5: _xt_lowint1 at /home/chenzhengwei/esp/esp-idf/components/freertos/xtensa_vectors.S:1154
0x40086911: spi_flash_enable_interrupts_caches_and_other_cpu at /home/chenzhengwei/esp/esp-idf/components/spi_flash/cache_utils.c:147
...

在这个例子中,发生异常的位置在 0x4011b808, 这里是__fixunssfdi 函数的存放位置,且该位置位于片外存储器(见 esp32_technical_reference_manual 的 1.3.3 节),这个函数是用来对浮点数转 64 位无符号整数的操作,意味着中断处理程序里使用了 double 型数据的强制类型转换操作。这在 cache 被关闭时操作是不被允许的。

  1. 如果不知道变量是在 .data/.bss 段还是 .rodata 段,那么可以使用 readelf 命令查看 .elf 中的符号详细信息
# 将 build/empty-project.elf 中的符号表覆盖写到 empty-project.out 文件中
readelf -a build/empty-project.elf > empty-project.out

使用 readelf 命令提取 elf 文件中的符号表,如何查看符号表可参考 C语言变量的存储布局,通过符号表,可以准确的看到每一个变量、函数的存储位置,大小等信息。有了这些信息,就可以检查 IRAM-Safe 中断处理程序里使用变量和函数是否在 flash 中。

你可能感兴趣的:(esp32)