feat(ulp): Add LP core debugging support
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(lp_debugging_example)
|
||||
@@ -0,0 +1,77 @@
|
||||
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 |
|
||||
| ----------------- | -------- | -------- | -------- |
|
||||
|
||||
# LP Core Debugging Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates how to debug application running on the LP core.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you should have an ESP32-C6 based development board.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Debugging Session
|
||||
|
||||
1) Run OpenOCD `openocd -f board/esp32c6-lpcore-builtin.cfg`.
|
||||
2) Run GDB `riscv32-esp-elf-gdb -x gdbinit build/lp_debugging_example.elf`
|
||||
3) `gdbinit` file will tell GDB to load debug info and symbols and set a number of breakpoints.
|
||||
4) Type `c` upon hitting every breakpoint.
|
||||
5) Finally LP core application should stop in `abort()`.
|
||||
|
||||
### LP Core Debugging Specifics
|
||||
|
||||
1) Add `-O0` compile option for ULP app in its CMakeLists.txt.
|
||||
```
|
||||
target_compile_options(${ULP_APP_NAME} PRIVATE -O0)
|
||||
```
|
||||
2) LP core supports limited set of HW exceptions, so, for example, writing at address `0x0` will not cause a panic as it would be for the code running on HP core. This can be overcome to some extent by enabling undefined behavior sanitizer for LP core application, so `ubsan` can help to catch some errors. But note that it will increase code size significantly and it can happen that application won't fit into RTC RAM. To enable `ubsan` for ULP app add `-fsanitize=undefined -fno-sanitize=shift-base` compile option to its CMakeLists.txt.
|
||||
```
|
||||
target_compile_options(${ULP_APP_NAME} PRIVATE -fsanitize=undefined -fno-sanitize=shift-base)
|
||||
```
|
||||
3) To be able to debug program running on LP core debug info and symbols need to be loaded to GDB. So there is special GDB command in `gdbinit`:
|
||||
```
|
||||
add-symbol build/esp-idf/main/ulp_debug_example/ulp_debug_example.elf
|
||||
```
|
||||
4) Upon startup LP core application is loaded into RAM, so all SW breakpoints set before that moment will get overwritten. The best moment to set breakpoints for LP core application is to do this when LP core program reaches `main` function. So `gdbinit` file used in this example sets temporary HW breakpoint on `main` and then set a bunch of other breakpoints when it hit.
|
||||
```
|
||||
thb main
|
||||
commands
|
||||
b main
|
||||
b do_crash
|
||||
b do_things
|
||||
b ulp_lp_core_delay_us
|
||||
c
|
||||
end
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
1) Currently debugging is not supported when either HP or LP core enters any sleep mode. So this limits debugging scenarios.
|
||||
2) FreeRTOS support in OpenOCD is disabled when debugging LP core, so you won't be able to see tasks running in the system. Instead there will be two threads representing HP ('esp32c6.cpu0') and LP ('esp32c6.cpu1') cores:
|
||||
```
|
||||
(gdb) info thread
|
||||
Id Target Id Frame
|
||||
1 Thread 1 "esp32c6.cpu0" (Name: esp32c6.cpu0, state: debug-request) 0x40803772 in esp_cpu_wait_for_intr ()
|
||||
at /home/user/projects/esp/esp-idf/components/esp_hw_support/cpu.c:64
|
||||
* 2 Thread 2 "esp32c6.cpu1" (Name: esp32c6.cpu1, state: breakpoint) do_things (max=1000000000)
|
||||
at /home/user/projects/esp/esp-idf/examples/system/ulp/lp_core/debugging/main/lp_core/main.c:21
|
||||
```
|
||||
3) When setting HW breakpoint in GDB it is set on both cores, so the number of available HW breakpoints is limited to the number of them supported by LP core (2 for ESP32-C6).
|
||||
4) OpenOCD flash support is disabled. It does not matter for LP core application because it is run completely from RAM and GDB can use SW breakpoints for it. But if you want to set a breakpoint on function from flash used by the code running on HP core (e.g. `app_main`) you should request to set HW breakpoint explicitly via `hb`/`thb` GDB commands.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
||||
@@ -0,0 +1,16 @@
|
||||
set pagination off
|
||||
target extended-remote :3333
|
||||
|
||||
mon reset halt
|
||||
maintenance flush register-cache
|
||||
|
||||
add-symbol build/esp-idf/main/ulp_debug_example/ulp_debug_example.elf
|
||||
|
||||
thb main
|
||||
commands
|
||||
b do_crash
|
||||
b do_things
|
||||
b ulp_lp_core_delay_us
|
||||
c
|
||||
end
|
||||
c
|
||||
@@ -0,0 +1,8 @@
|
||||
# Set usual component variables
|
||||
set(app_sources "lp_debug_main.c")
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
REQUIRES ulp
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
ulp_add_project("ulp_debug_example" "${CMAKE_SOURCE_DIR}/main/ulp/")
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_err.h"
|
||||
#include "ulp_lp_core.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
extern const uint8_t ulp_build_debug_bin_start[] asm("_binary_ulp_debug_example_bin_start");
|
||||
extern const uint8_t ulp_build_debug_bin_end[] asm("_binary_ulp_debug_example_bin_end");
|
||||
|
||||
static void lp_core_init(void)
|
||||
{
|
||||
/* Set LP core wakeup source as the HP CPU */
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
|
||||
/* Load LP core firmware */
|
||||
ESP_ERROR_CHECK(ulp_lp_core_load_binary(ulp_build_debug_bin_start, (ulp_build_debug_bin_end - ulp_build_debug_bin_start)));
|
||||
|
||||
/* Run LP core */
|
||||
ESP_ERROR_CHECK(ulp_lp_core_run(&cfg));
|
||||
|
||||
printf("LP core loaded with firmware and running successfully\n");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Initializing LP core...\n");
|
||||
/* Load LP Core binary and start the coprocessor */
|
||||
lp_core_init();
|
||||
|
||||
printf("Do some work on HP core...\n");
|
||||
while(1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
# This CMakelists.txt is included in the ULP project
|
||||
# so we have access to the ULP target: ULP_APP_NAME
|
||||
|
||||
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Project/target name is passed from the main project to allow IDF to have a dependency on this target
|
||||
# as well as embed the binary into the main app
|
||||
project(${ULP_APP_NAME})
|
||||
add_executable(${ULP_APP_NAME} main.c)
|
||||
|
||||
# Import the ULP project helper functions
|
||||
include(IDFULPProject)
|
||||
|
||||
# Apply default compile options
|
||||
ulp_apply_default_options(${ULP_APP_NAME})
|
||||
|
||||
# Apply default sources provided by the IDF ULP component
|
||||
ulp_apply_default_sources(${ULP_APP_NAME})
|
||||
|
||||
# Add targets for building the binary, as well as the linkerscript which exports ULP shared variables to the main app
|
||||
ulp_add_build_binary_targets(${ULP_APP_NAME})
|
||||
|
||||
|
||||
# Set custom compile flags
|
||||
# By default ULP sources are compiled with -Os which is set in toolchain file in IDF build system.
|
||||
# These options will appear on command line after default ones effectively overriding them.
|
||||
# Therefore '-Os' will be overridden with '-O0' for this example for convenient debugging.
|
||||
target_compile_options(${ULP_APP_NAME} PRIVATE -O0 -fsanitize=undefined -fno-sanitize=shift-base)
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "ulp_lp_core_utils.h"
|
||||
|
||||
void do_crash(void)
|
||||
{
|
||||
volatile int *p = (int *)0x0;
|
||||
// if ubsan is enabled (-fsanitize=undefined) line below will cause ubsan check failure
|
||||
// and finally app will be stopped in abort()
|
||||
*p = 32;
|
||||
// if ubsan is disabled app will be stopped in abort() call below
|
||||
abort();
|
||||
}
|
||||
|
||||
void do_things(int max)
|
||||
{
|
||||
while (1) {
|
||||
for (int i = 0; i < max; i++) {
|
||||
ulp_lp_core_delay_us(100000);
|
||||
if (i > 0)
|
||||
do_crash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
do_things(1000000000);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# Enable LP Core
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=14000
|
||||
Reference in New Issue
Block a user