chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(lp_debugging_example)
|
||||
@@ -0,0 +1,81 @@
|
||||
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 |
|
||||
| ----------------- | -------- | -------- | -------- | --------- |
|
||||
|
||||
# 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 ESP based development board that integrates an LP core.
|
||||
|
||||
### 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) For convenient debugging, `-O0` compile option for ULP app in its CMakeLists.txt can be added. Beware that this change may cause the built binary to be too large to fit in the available memory.
|
||||
```
|
||||
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 do_crash
|
||||
b do_things
|
||||
commands
|
||||
c
|
||||
end
|
||||
b ulp_lp_core_delay_us
|
||||
commands
|
||||
c
|
||||
end
|
||||
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.hp.cpu0" (Name: esp32c6.hp.cpu0, state: debug-request) 0x4080261c in esp_cpu_wait_for_intr ()
|
||||
at /home/user/projects/esp/esp-idf/components/esp_hw_support/cpu.c:64
|
||||
* 2 Thread 2 "esp32c6.lp.cpu" (Name: esp32c6.lp.cpu, state: breakpoint) do_things (max=1000000000)
|
||||
at /home/user/projects/esp/esp-idf/examples/system/ulp/lp_core/debugging/main/ulp/main.c:22
|
||||
```
|
||||
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, ESP32-C5 or ESP32-P4).
|
||||
|
||||
## 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,22 @@
|
||||
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
|
||||
commands
|
||||
c
|
||||
end
|
||||
b ulp_lp_core_delay_us
|
||||
commands
|
||||
c
|
||||
end
|
||||
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,37 @@
|
||||
|
||||
# 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.22)
|
||||
|
||||
# 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' can be overridden here with '-O0' for this example for convenient debugging.
|
||||
target_compile_options(${ULP_APP_NAME} PRIVATE -fsanitize=undefined -fno-sanitize=shift-base)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${ULP_APP_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ULP_APP_NAME}>
|
||||
$<TARGET_FILE_DIR:${ULP_APP_NAME}>/../../../gdbinit/$<TARGET_FILE_NAME:${ULP_APP_NAME}>
|
||||
)
|
||||
@@ -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,58 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import os.path
|
||||
import time
|
||||
import typing
|
||||
|
||||
import pexpect.fdpexpect
|
||||
import pytest
|
||||
from pytest_embedded_idf import IdfDut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from conftest import OpenOCD
|
||||
|
||||
|
||||
@idf_parametrize('target', ['esp32c5', 'esp32c6', 'esp32p4', 'esp32s31'], indirect=['target'])
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='s31-lpcore not supported in latest OpenOCD release yet')
|
||||
@pytest.mark.usb_serial_jtag
|
||||
def test_lp_core_debugging(openocd_dut: 'OpenOCD', dut: IdfDut) -> None:
|
||||
dut.expect('Do some work on HP core...')
|
||||
|
||||
# Prepare gdbinit file
|
||||
gdb_logfile = os.path.join(dut.logdir, 'gdb.txt')
|
||||
gdbinit_orig = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'gdbinit')
|
||||
gdbinit = os.path.join(dut.logdir, 'gdbinit')
|
||||
with open(gdbinit_orig) as f_r, open(gdbinit, 'w') as f_w:
|
||||
for line in f_r:
|
||||
if line.startswith('add-symbol'):
|
||||
f_w.write(f'add-symbol {dut.app.binary_path}/gdbinit/ulp_debug_example.elf\n')
|
||||
else:
|
||||
f_w.write(line)
|
||||
|
||||
# Launch with gdbinit and check abort function is called
|
||||
with (
|
||||
openocd_dut.run() as oocd,
|
||||
open(gdb_logfile, 'w') as gdb_log,
|
||||
pexpect.spawn(
|
||||
f'idf.py -B {dut.app.binary_path} gdb --batch -x {gdbinit}',
|
||||
timeout=60,
|
||||
logfile=gdb_log,
|
||||
encoding='utf-8',
|
||||
codec_errors='ignore',
|
||||
) as p,
|
||||
):
|
||||
p.expect('hit Breakpoint 2, do_crash')
|
||||
# dont intercept ebreak in abort
|
||||
oocd.write(f'{dut.target}.lp.cpu configure -ebreak exception')
|
||||
# let the panic handler finish
|
||||
oocd.write('resume')
|
||||
time.sleep(2)
|
||||
|
||||
# Validate GDB logs
|
||||
with open(gdb_logfile, encoding='utf-8') as fr: # pylint: disable=protected-access
|
||||
gdb_pexpect_proc = pexpect.fdpexpect.fdspawn(fr.fileno())
|
||||
gdb_pexpect_proc.expect('hit Temporary breakpoint 1, main')
|
||||
gdb_pexpect_proc.expect('hit Breakpoint 3, do_things')
|
||||
gdb_pexpect_proc.expect('hit Breakpoint 4, ulp_lp_core_delay_us')
|
||||
gdb_pexpect_proc.expect('hit Breakpoint 2, do_crash')
|
||||
@@ -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