Merge branch 'feature/update_gdbstub_v6.0' into 'release/v6.0'

gdbstub: backport fixes (v6.0)

See merge request espressif/esp-idf!46200
This commit is contained in:
Jiang Jiang Jian
2026-03-24 10:28:23 +08:00
11 changed files with 500 additions and 300 deletions
@@ -1,12 +1,12 @@
set(srcs "test_app_main.c")
if(CONFIG_IDF_TARGET_ARCH_RISCV)
if(CONFIG_SOC_CPU_HAS_HWLOOP)
if(CONFIG_SOC_CPU_HAS_HWLOOP)
list(APPEND srcs "xesppie_loops.S")
endif()
if(CONFIG_SOC_CPU_HAS_FPU OR CONFIG_SOC_CPU_HAS_PIE)
endif()
if(CONFIG_SOC_CPU_HAS_FPU OR CONFIG_SOC_CPU_HAS_PIE)
list(APPEND srcs "coproc_regs.c")
endif()
if(CONFIG_IDF_TARGET_ARCH_RISCV)
set(ext_comp "riscv")
endif()
endif()
idf_component_register(SRCS ${srcs}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -7,7 +7,9 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#if CONFIG_IDF_TARGET_ARCH_RISCV
#include "riscv/rvruntime-frames.h"
#endif
#include "soc/soc_caps.h"
static SemaphoreHandle_t sem = NULL;
@@ -74,13 +76,27 @@ pie_start:
#if SOC_CPU_HAS_FPU
static void test_fpu(void * arg)
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA
struct {
float f[16];
} fpu_regs_sample, fpu_regs;
#endif
#if CONFIG_IDF_TARGET_ARCH_RISCV
RvFPUSaveArea fpu_regs_sample, fpu_regs;
#endif
uint32_t *ptr = (uint32_t *)&fpu_regs_sample;
for (int i = 0; i < sizeof(fpu_regs_sample)/sizeof(uint32_t); i++) {
ptr[i] = i + (uintptr_t) arg;
ptr[i] = i + (int) arg;
}
/* Set FPU owner to current task by calling an instruction */
#if CONFIG_IDF_TARGET_ARCH_XTENSA
__asm__ volatile ("ssi f0, %0, 0" :: "a" (&fpu_regs));
#endif
#if CONFIG_IDF_TARGET_ARCH_RISCV
__asm__ volatile ("fsw ft0, %0" : "=m" (fpu_regs.ft0));
#endif
fpu_start:
asm volatile ("nop");
@@ -88,6 +104,25 @@ fpu_start:
vTaskDelay(50 / portTICK_PERIOD_MS);
}
#if CONFIG_IDF_TARGET_ARCH_XTENSA
__asm__ volatile ("ssi f0, %0, 0" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f1, %0, 4" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f2, %0, 8" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f3, %0, 12" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f4, %0, 16" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f5, %0, 20" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f6, %0, 24" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f7, %0, 28" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f8, %0, 32" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f9, %0, 36" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f10, %0, 40" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f11, %0, 44" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f12, %0, 48" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f13, %0, 52" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f14, %0, 56" :: "a" (&fpu_regs));
__asm__ volatile ("ssi f15, %0, 60" :: "a" (&fpu_regs));
#endif
#if CONFIG_IDF_TARGET_ARCH_RISCV
__asm__ volatile ("fsw ft0, %0" : "=m" (fpu_regs.ft0));
__asm__ volatile ("fsw ft1, %0" : "=m" (fpu_regs.ft1));
__asm__ volatile ("fsw ft2, %0" : "=m" (fpu_regs.ft2));
@@ -121,6 +156,7 @@ fpu_start:
__asm__ volatile ("fsw ft10, %0" : "=m" (fpu_regs.ft10));
__asm__ volatile ("fsw ft11, %0" : "=m" (fpu_regs.ft11));
__asm__ volatile ("csrr %0, fcsr" : "=r" (fpu_regs.fcsr));
#endif
if (!memcmp(&fpu_regs_sample, &fpu_regs, sizeof(fpu_regs))) {
xSemaphoreGive((SemaphoreHandle_t) sem);
@@ -10,7 +10,7 @@
#include "freertos/task.h"
#include "sdkconfig.h"
#define TEST_COPROCESSOR_REGISTERS (__riscv && (SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE))
#define TEST_COPROCESSOR_REGISTERS (SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE)
#define TEST_HWLOOP_INSTRUCTIONS (__riscv && SOC_CPU_HAS_HWLOOP)
int var_1;
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import os
import os.path as path
@@ -147,7 +147,7 @@ def check_registers_numbers(dut: PanicTestDut) -> None:
r_id += 1
def set_float_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
def set_riscv_float_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
cmd = f'-thread-select {t_id}'
responses = dut.gdb_write(cmd)
assert dut.find_gdb_response('done', 'result', responses) is not None
@@ -157,13 +157,39 @@ def set_float_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
responses = dut.gdb_write(cmd)
assert dut.find_gdb_response('done', 'result', responses) is not None
# Note that it's a gap between the last floating register number and fcsr register number.
cmd = f'-data-write-register-values d 68 {32 + addition}'
# Note that it's a gap between the last floating register number and fcsr register number.
cmd = f'-data-write-register-values d 68 {32 + addition}'
responses = dut.gdb_write(cmd)
assert dut.find_gdb_response('done', 'result', responses) is not None
def set_xtensa_float_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
"""Set Xtensa FPU registers via GDB.
Xtensa FPU register numbers:
- f0-f15: registers 87-102
- fcr: register 103
- fsr: register 104
"""
cmd = f'-thread-select {t_id}'
responses = dut.gdb_write(cmd)
assert dut.find_gdb_response('done', 'result', responses) is not None
if dut.target == 'esp32':
fpu_current_register = 87
elif dut.target == 'esp32s3':
fpu_current_register = 84
else:
raise ValueError(f'Unsupported target: {dut.target}')
for i in range(18): # 16 f* registers + fcr + fsr
cmd = f'-data-write-register-values d {fpu_current_register} {i + addition}'
responses = dut.gdb_write(cmd)
assert dut.find_gdb_response('done', 'result', responses) is not None
fpu_current_register += 1
def set_pie_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
def set_riscv_pie_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
cmd = f'-thread-select {t_id}'
responses = dut.gdb_write(cmd)
assert dut.find_gdb_response('done', 'result', responses) is not None
@@ -212,15 +238,14 @@ def coproc_registers_test(dut: PanicTestDut, regs_type: str, set_registers: Call
- Task coproc owner (direct registers write)
- Other tasks (write registers to task's stack)
"""
coproc_tasks = [f'test_{regs_type}_1', f'test_{regs_type}_2']
found_tasks = [False] * len(coproc_tasks)
found_count = 0
for t in threads:
for index, test in enumerate(coproc_tasks):
if test in t['details']:
set_registers(dut, t['id'], index + 1)
found_tasks[index] = True
for task_num in [1, 2]:
if f'test_{regs_type}_{task_num}' in t['details']:
set_registers(dut, t['id'], task_num)
found_count += 1
assert all(found_tasks)
assert found_count == 2, f'Expected 2 coproc tasks, found {found_count}'
dut_set_variable(dut, f'test_{regs_type}_ready', 1)
@@ -240,28 +265,30 @@ def coproc_registers_test(dut: PanicTestDut, regs_type: str, set_registers: Call
threads = dut_get_threads(dut)
found_tasks = [False] * len(coproc_tasks)
found_count = 0
for t in threads:
for index, test in enumerate(coproc_tasks):
if test in t['details']:
found_tasks[index] = True
for task_num in [1, 2]:
if f'test_{regs_type}_{task_num}' in t['details']:
found_count += 1
assert not any(found_tasks)
assert found_count == 0, f'Expected 0 coproc tasks, found {found_count}'
@pytest.mark.generic
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
@idf_parametrize('target', ['esp32', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_coproc_registers(dut: PanicTestDut) -> None:
start_gdb(dut)
# enable coprocessors registers testing
dut_enable_test(dut, 'coproc_regs')
check_registers_numbers(dut)
coproc_registers_test(dut, 'fpu', set_float_registers)
if dut.target == 'esp32p4':
coproc_registers_test(dut, 'pie', set_pie_registers)
if dut.is_xtensa:
coproc_registers_test(dut, 'fpu', set_xtensa_float_registers)
else:
check_registers_numbers(dut)
coproc_registers_test(dut, 'fpu', set_riscv_float_registers)
if dut.target == 'esp32p4':
coproc_registers_test(dut, 'pie', set_riscv_pie_registers)
@pytest.mark.generic
@@ -341,14 +368,11 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
assert dut.find_gdb_response('done', 'result', responses) is not None
cmd = '-exec-continue'
payload = run_and_break(dut, cmd)
assert payload['reason'] == 'signal-received'
assert payload['reason'] == 'watchpoint-trigger'
assert int(payload['value']['new']) == int(payload['value']['old']) + 2
assert payload['frame']['func'] == 'foo'
assert payload['frame']['line'] == str(get_line_number('var_2--;'))
assert payload['stopped-threads'] == 'all'
# Uncomment this when implement send reason to gdb: GCC-313
#
# assert payload['reason'] == 'watchpoint-trigger'
# assert int(payload['value']['new']) == int(payload['value']['old']) + 1
# assert payload['frame']['line'] == '14'
cmd = '-break-delete 2'
responses = dut.gdb_write(cmd)