esp_gdbstup: implement runtime gdbstub for riscv

This commit is contained in:
Alexey Lapshin 2023-04-08 00:09:22 +08:00
parent 36588c4b35
commit 9322c2b82d
12 changed files with 355 additions and 55 deletions

View File

@ -11,7 +11,8 @@ if(CONFIG_IDF_TARGET_ARCH_XTENSA)
"src/port/xtensa/xt_debugexception.S")
list(APPEND priv_includes "src/port/xtensa/include")
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
list(APPEND srcs "src/port/riscv/gdbstub_riscv.c")
list(APPEND srcs "src/port/riscv/gdbstub_riscv.c"
"src/port/riscv/rv_decode.c")
list(APPEND priv_includes "src/port/riscv/include")
endif()

View File

@ -74,6 +74,16 @@ int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame);
*/
void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_gdb_regfile_t *dst);
/**
* Signal handler for debugging interrupts of the application.
*/
void esp_gdbstub_int(void *frame);
/**
* Signal handler for transport protocol interrupts.
*/
void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame);
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/**
* Write registers from the saved frame of a given task to the GDB register file
@ -104,11 +114,13 @@ void esp_gdbstub_putchar(int c);
*/
void esp_gdbstub_flush(void);
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/**
* Read a data from fifo and detect start symbol
* @return 1 if break symbol was detected, or 0 if not
*/
int esp_gdbstub_getfifo(void);
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/**** GDB packet related functions ****/
@ -144,7 +156,7 @@ void esp_gdbstub_stall_other_cpus_start(void);
void esp_gdbstub_stall_other_cpus_end(void);
void esp_gdbstub_clear_step(void);
void esp_gdbstub_do_step(void);
void esp_gdbstub_do_step(esp_gdbstub_frame_t *regs_frame);
void esp_gdbstub_trigger_cpu(void);
/**

View File

@ -187,6 +187,25 @@ static inline void enable_all_wdts(void)
}
}
int getActiveTaskNum(void);
int __swrite(struct _reent *, void *, const char *, int);
int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len);
volatile esp_gdbstub_frame_t *temp_regs_frame;
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static int bp_count = 0;
static int wp_count = 0;
static uint32_t bp_list[SOC_CPU_BREAKPOINTS_NUM] = {0};
static uint32_t wp_list[SOC_CPU_WATCHPOINTS_NUM] = {0};
static uint32_t wp_size[SOC_CPU_WATCHPOINTS_NUM] = {0};
static esp_cpu_watchpoint_trigger_t wp_access[SOC_CPU_WATCHPOINTS_NUM] = {0};
static volatile bool step_in_progress = false;
static bool not_send_reason = false;
static bool process_gdb_kill = false;
static bool gdb_debug_int = false;
/**
* @breef Handle UART interrupt
*
@ -196,24 +215,7 @@ static inline void enable_all_wdts(void)
*
* @param curr_regs - actual registers frame
*
*/
static int bp_count = 0;
static int wp_count = 0;
static uint32_t bp_list[GDB_BP_SIZE] = {0};
static uint32_t wp_list[GDB_WP_SIZE] = {0};
static uint32_t wp_size[GDB_WP_SIZE] = {0};
static esp_cpu_watchpoint_trigger_t wp_access[GDB_WP_SIZE] = {0};
static volatile bool step_in_progress = false;
static bool not_send_reason = false;
static bool process_gdb_kill = false;
static bool gdb_debug_int = false;
int getActiveTaskNum(void);
int __swrite(struct _reent *, void *, const char *, int);
int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len);
volatile esp_gdbstub_frame_t *temp_regs_frame;
*/
void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame)
{
temp_regs_frame = regs_frame;
@ -264,11 +266,9 @@ void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame)
if (res == -2) {
esp_gdbstub_send_str_packet(NULL);
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (res == GDBSTUB_ST_CONT) {
break;
}
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
}
{
/* Resume other core */
@ -356,16 +356,12 @@ void gdbstub_handle_debug_int(esp_gdbstub_frame_t *regs_frame)
gdb_debug_int = false;
}
intr_handle_t intr_handle_;
extern void _xt_gdbstub_int(void * );
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/** @brief Init gdbstub
* Init uart interrupt for gdbstub
* */
void esp_gdbstub_init(void)
{
esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, _xt_gdbstub_int, NULL, &intr_handle_);
esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, esp_gdbstub_int, NULL, NULL);
esp_gdbstub_init_dports();
}
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
@ -474,8 +470,10 @@ static void handle_M_command(const unsigned char *cmd, int len)
esp_gdbstub_send_end();
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
void update_breakpoints(void)
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
if (bp_list[i] != 0) {
esp_cpu_set_breakpoint(i, (const void *)bp_list[i]);
@ -490,9 +488,33 @@ void update_breakpoints(void)
esp_cpu_clear_watchpoint(i);
}
}
#else // CONFIG_IDF_TARGET_ARCH_XTENSA
#if (GDB_BP_SIZE != GDB_WP_SIZE)
#error "riscv have a common number of BP and WP"
#endif
/*
* On riscv we have no separated registers for setting BP and WP as we have for xtensa.
* Instead we have common registers which could be configured as BP or WP.
*/
size_t i = 0;
for (size_t b = 0; b < GDB_BP_SIZE; b++) {
if (bp_list[b] != 0) {
esp_cpu_set_breakpoint(i, (const void *)bp_list[b]);
i++;
}
}
for (size_t w = 0; w < GDB_WP_SIZE && i < GDB_WP_SIZE; w++) {
if (wp_list[w] != 0) {
esp_cpu_set_watchpoint(i, (void *)wp_list[w], wp_size[w], wp_access[w]);
i++;
}
}
for (; i < GDB_BP_SIZE; i++) {
esp_cpu_clear_breakpoint(i);
}
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/** Write breakpoint */
static void handle_Z0_command(const unsigned char *cmd, int len)
{
@ -626,7 +648,7 @@ static void handle_S_command(const unsigned char *cmd, int len)
static void handle_s_command(const unsigned char *cmd, int len)
{
step_in_progress = true;
esp_gdbstub_do_step();
esp_gdbstub_do_step((esp_gdbstub_frame_t *)temp_regs_frame);
}
/** Step ... */
@ -888,9 +910,11 @@ static eTaskState get_task_state(size_t index)
eTaskState result = eReady;
TaskHandle_t handle = NULL;
get_task_handle(index, &handle);
#if CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (gdb_debug_int == false) {
result = eTaskGetState(handle);
}
#endif
return result;
}

View File

@ -98,6 +98,7 @@ void esp_gdbstub_flush(void)
}
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
int esp_gdbstub_getfifo(void)
{
esp_gdbstub_uart_init();
@ -115,5 +116,5 @@ int esp_gdbstub_getfifo(void)
uart_ll_clr_intsts_mask(gdb_uart, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
return doDebug;
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG

View File

@ -7,8 +7,12 @@
#include <string.h>
#include "esp_gdbstub.h"
#include "esp_gdbstub_common.h"
#include "esp_cpu.h"
#include "rv_decode.h"
#include "sdkconfig.h"
extern volatile esp_gdbstub_frame_t *temp_regs_frame;
static inline void init_regfile(esp_gdbstub_gdb_regfile_t *dst)
{
memset(dst, 0, sizeof(*dst));
@ -24,7 +28,7 @@ void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_
memcpy(&(dst->x[1]), &frame->ra, sizeof(uint32_t) * 31);
}
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS || CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/* Represents FreeRTOS TCB structure */
typedef struct {
@ -32,6 +36,7 @@ typedef struct {
/* Other members aren't needed */
} dummy_tcb_t;
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst)
{
@ -42,40 +47,117 @@ void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS || CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
{
return 5; // SIGTRAP, see IDF-2490
switch (frame->mcause) {
case 0: /* Instruction address misaligned */
case 1: /* Instruction access fault */
case 2: /* Illegal instruction */
return 4; /* SIGILL */
case 3: /* Breakpoint */
return 5; /* SIGTRAP */
case 4: /* Load address misaligned */
case 5: /* Load access fault */
case 6: /* Store/AMO address misaligned */
case 7: /* Store/AMO access fault */
return 11; /* SIGSEGV */
case 8: /* Environment call from U-mode */
case 9: /* Environment call from S-mode */
// case 10: /* Reserved */
case 11: /* Environment call from M-mode */
return 5; /* SIGTRAP */
case 12: /* Instruction page fault */
case 13: /* Load page fault */
// case 14: /* Reserved */
case 15: /* Store/AMO page fault */
return 11; /* SIGSEGV */
};
return 5; /* SIGTRAP */
}
void _xt_gdbstub_int(void *frame)
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
void esp_gdbstub_int(__attribute__((unused)) void *frame)
{
/* Pointer to saved frame is in pxCurrentTCB
* See rtos_int_enter function
*/
extern void *pxCurrentTCB;
dummy_tcb_t *tcb = pxCurrentTCB;
gdbstub_handle_uart_int((esp_gdbstub_frame_t *)tcb->top_of_stack);
}
void esp_gdbstub_init_dports(void)
{
}
void esp_gdbstub_init_dports()
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#if (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static bool stall_started = false;
#endif
void esp_gdbstub_stall_other_cpus_start(void)
{
#if (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (stall_started == false) {
esp_ipc_isr_stall_other_cpu();
stall_started = true;
}
#endif
}
void esp_gdbstub_stall_other_cpus_start()
void esp_gdbstub_stall_other_cpus_end(void)
{
#if (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (stall_started == true) {
esp_ipc_isr_release_other_cpu();
stall_started = false;
}
#endif
}
void esp_gdbstub_stall_other_cpus_end()
void esp_gdbstub_clear_step(void)
{
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/* Setup triggers again because we removed them in esp_gdbstub_do_step() */
update_breakpoints();
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
}
void esp_gdbstub_clear_step()
{
}
void esp_gdbstub_do_step()
void esp_gdbstub_do_step(esp_gdbstub_frame_t *frame)
{
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
uint32_t pc = (uint32_t) frame->mepc;
uint32_t next_pc = rv_compute_next_pc(frame, pc);
esp_cpu_set_breakpoint(0, (void *) next_pc);
for (size_t i = 1; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
esp_cpu_clear_breakpoint(i);
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
}
void esp_gdbstub_trigger_cpu(void)
{
#if !CONFIG_FREERTOS_UNICORE
if (0 == esp_cpu_get_core_id()) {
esp_crosscore_int_send_gdb_call(1);
} else {
esp_crosscore_int_send_gdb_call(0);
}
#endif
}
void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t value)
{
/* RISC-V base ISA has registers x0-x31 */
if (reg_index == 0) { /* skip zero-wired register */
return;
} else if (reg_index < 32) {
(&frame->mepc)[reg_index] = value;
} else if (reg_index == 32) { /* register 32 is PC */
frame->mepc = value;
}
}

View File

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_gdbstub_common.h"
uintptr_t rv_compute_next_pc(esp_gdbstub_frame_t *frame, uintptr_t inst_addr);

View File

@ -0,0 +1,158 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "rv_decode.h"
static inline uint32_t rv_inst_len(uint32_t inst)
{
#ifdef __riscv_c
if ((inst & 0x3) != 0x3)
return 2; /* 16-bit instructions. */
#endif /* __riscv_c */
return 4; /* 32-bit instructions. */
}
static uint32_t rv_get_register_value(esp_gdbstub_frame_t *frame, uint32_t r_num)
{
r_num &= 0x1F;
if (r_num == 0) { /* zero-wired */
return 0;
}
return (&frame->mepc)[r_num];
}
static uint32_t rv_get_rs1_value(esp_gdbstub_frame_t *frame, uint32_t inst)
{
return rv_get_register_value(frame, inst >> 15);
}
static uint32_t rv_get_rs2_value(esp_gdbstub_frame_t *frame, uint32_t inst)
{
return rv_get_register_value(frame, inst >> 20);
}
static int32_t rv_get_branch_next_inst_offset(esp_gdbstub_frame_t *frame, uint32_t inst)
{
uint32_t funct = (inst >> 12) & 0x7;
uint32_t rs1 = rv_get_rs1_value(frame, inst);
uint32_t rs2 = rv_get_rs2_value(frame, inst);
if ((funct == 0 && rs1 == rs2) || /* beq true */
(funct == 1 && rs1 != rs2) || /* bne true */
(funct == 4 && (int32_t) rs1 < (int32_t) rs2) || /* blt true */
(funct == 5 && (int32_t) rs1 >= (int32_t) rs2) || /* bge true */
(funct == 6 && rs1 < rs2) || /* bltu true */
(funct == 7 && rs1 >= rs2)) { /* bgeu true */
return ((inst >> 8 ) & 0xF ) << 1 | /* imm[4:1] */
((inst >> 25) & 0x3F) << 5 | /* imm[10:5] */
((inst >> 7 ) & 0x1 ) << 11 | /* imm[11] */
((inst >> 31) ? 0xFFFFF000 : 0); /* imm[12] is sign part */;
}
return rv_inst_len(inst); /* branch will not jump. Next instruction will be executed */
}
static int32_t rv_get_jal_next_inst_offset(uint32_t inst)
{
return ((inst >> 21) & 0x3FF) << 1 | /* imm[10:1] */
((inst >> 20) & 0x1 ) << 11 | /* imm[11] */
((inst >> 12) & 0xFF ) << 12 | /* imm[19:12] */
((inst >> 31) ? 0xFFF00000 : 0); /* imm[20] is sign bit */
}
static uint32_t rv_get_jalr_next_inst(esp_gdbstub_frame_t *frame, uint32_t inst)
{
uint32_t rs1 = rv_get_rs1_value(frame, inst);
int32_t imm = ((inst >> 20) & 0xFFF); /* imm[11:0] */
imm |= (imm >> 11) ? 0xFFFFF000 : 0; /* imm[11] is sign bit */
return rs1 + imm;
}
#ifdef __riscv_c /* compressed riscv instruction set */
static uint32_t rv_get_c_rs1_value(esp_gdbstub_frame_t *frame, uint32_t inst)
{
return rv_get_register_value(frame, inst >> 7);
}
static uint32_t rv_get_c_rs2_num(uint32_t inst)
{
return (inst >> 2) & 0x1F;
}
static uint32_t rv_get_c_rd_num(uint32_t inst)
{
return (inst >> 7) & 0x1F;
}
static int32_t rv_get_c_jal_next_inst_offset(uint32_t inst)
{
return ((inst >> 3 ) & 0x7 ) << 1 | /* imm[3:1] */
((inst >> 11) & 0x1 ) << 4 | /* imm[4] */
((inst >> 2 ) & 0x1 ) << 5 | /* imm[5] */
((inst >> 7 ) & 0x1 ) << 6 | /* imm[6] */
((inst >> 6 ) & 0x1 ) << 7 | /* imm[7] */
((inst >> 9 ) & 0x3 ) << 8 | /* imm[9:8] */
((inst >> 8 ) & 0x1 ) << 10 | /* imm[10] */
((inst >> 12) & 0x1 ) << 11 | /* imm[11] */
((inst >> 12) & 0x1 ? 0xFFFFF000 : 0); /* imm[11] is sign part */;
}
static int32_t rv_get_c_branch_next_inst_offset(esp_gdbstub_frame_t *frame,uint32_t inst)
{
const int32_t rs1_value = (&frame->s0)[(inst >> 7) & 7];
const bool is_bnez = (inst >> 13) & 1;
if ((rs1_value == 0 && !is_bnez) ||
(rs1_value != 0 && is_bnez)) {
return ((inst >> 3 ) & 0x3 ) << 1 | /* imm[2:1] */
((inst >> 10) & 0x3 ) << 3 | /* imm[4:3] */
((inst >> 2 ) & 0x1 ) << 5 | /* imm[5] */
((inst >> 5 ) & 0x3 ) << 6 | /* imm[7:6] */
((inst >> 12) & 0x1 ) << 8 | /* imm[8] */
((inst >> 12) & 0x1 ? 0xFFFFFF00 : 0); /* imm[8] is sign part */;
}
return 2;
}
#endif /* __riscv_c */
uintptr_t rv_compute_next_pc(esp_gdbstub_frame_t *frame, uintptr_t inst_addr)
{
const uint32_t inst = *((uint32_t *) inst_addr);
const uint32_t inst_len = rv_inst_len(inst);
if (inst_len == 4) { /* this is 32-bit instruction */
switch (inst & 0x7f) {
case 0x63: /* branch */
return inst_addr + rv_get_branch_next_inst_offset(frame, inst);
case 0x6F: /* jal */
return inst_addr + rv_get_jal_next_inst_offset(inst);
case 0x67: /* jalr */
return rv_get_jalr_next_inst(frame, inst);
}
}
#ifdef __riscv_c /* compressed riscv instruction set */
const uint32_t funct3 = (inst & 0xFFFF) >> 13;
if ((inst & 3) == 1) {
switch (funct3) {
case 1: /* c.jal */
case 5: /* c.j */
return inst_addr + rv_get_c_jal_next_inst_offset(inst);
case 6: /* c.beqz */
case 7: /* c.bnez */
return inst_addr + rv_get_c_branch_next_inst_offset(frame, inst);
}
} else if ((inst & 3) == 2) {
uint32_t rs2 = rv_get_c_rs2_num(inst);
uint32_t rd = rv_get_c_rd_num(inst);
/* c.jr and c.jalr:
*
* They must have funct3 == 0b100, rd!=0 and rs2==0
* See Table 1.6: Instruction listing for RVC, Quadrant 2
* in The RISC-V Compressed Instruction Set Manual
*/
if (funct3 == 4 && rd != 0 && rs2 == 0) {
return rv_get_c_rs1_value(frame, inst);
}
}
#endif /* __riscv_c */
return inst_addr + inst_len;
}

View File

@ -9,10 +9,11 @@
.section .iram1, "ax"
.global gdbstub_handle_uart_int
.global _xt_gdbstub_int
.global esp_gdbstub_int
.type esp_gdbstub_int, @function
.align 4
_xt_gdbstub_int:
esp_gdbstub_int:
/* Allocate exception frame and save minimal context. */
mov a0, sp

View File

@ -139,7 +139,7 @@ static bool stall_started = false;
/** @brief GDB stall other CPU
* GDB stall other CPU
* */
void esp_gdbstub_stall_other_cpus_start()
void esp_gdbstub_stall_other_cpus_start(void)
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (stall_started == false) {
@ -152,7 +152,7 @@ void esp_gdbstub_stall_other_cpus_start()
/** @brief GDB end stall other CPU
* GDB end stall other CPU
* */
void esp_gdbstub_stall_other_cpus_end()
void esp_gdbstub_stall_other_cpus_end(void)
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (stall_started == true) {
@ -174,7 +174,7 @@ void esp_gdbstub_clear_step(void)
/** @brief GDB do step
* GDB do one step
* */
void esp_gdbstub_do_step(void)
void esp_gdbstub_do_step( esp_gdbstub_frame_t *frame)
{
// We have gdbstub uart interrupt, and if we will call step, with ICOUNTLEVEL=2 or higher, from uart interrupt, the
// application will hang because it will try to step uart interrupt. That's why we have to set ICOUNTLEVEL=1

View File

@ -102,6 +102,9 @@
.global rtos_int_enter
.global rtos_int_exit
.global _global_interrupt_handler
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
.global gdbstub_handle_debug_int
#endif
.section .exception_vectors.text
/* This is the vector table. MTVEC points here.
@ -170,16 +173,27 @@ _panic_handler:
* have an pseudo excause */
mv a0, sp
csrr a1, mcause
/* Branches instructions don't accept immediates values, so use t1 to
* store our comparator */
li t0, 0x80000000
bgeu a1, t0, _call_panic_handler
sw a1, RV_STK_MCAUSE(sp)
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
li t0, 3
beq a1, t0, _call_gdbstub_handler
#endif
/* exception_from_panic never returns */
jal panic_from_exception
/* We arrive here if the exception handler has returned. */
j _return_from_exception
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
_call_gdbstub_handler:
call gdbstub_handle_debug_int
j _return_from_exception
#endif
_call_panic_handler:
/* Remove highest bit from mcause (a1) register and save it in the
* structure */

View File

@ -60,12 +60,6 @@ examples/system/gcov:
temporary: true
reason: lack of runners
examples/system/gdbstub:
disable:
- if: IDF_TARGET == "esp32c2" or IDF_TARGET == "esp32h2"
temporary: true
reason: target esp32c2, esp32h2 is not supported yet
examples/system/heap_task_tracking:
disable:
- if: IDF_TARGET == "esp32c2" or IDF_TARGET == "esp32h2"

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# GDBstub example
@ -14,7 +14,9 @@ Upon exit from GDB, the application will continue to work in IDF Monitor as befo
The example can run on any commonly available ESP32 development board.
There are two possible ways to execute gdbstub with GDB: from IDF Monitor and as standalone application.
gdbstub support ESP32, ESP32-S2 and ESP32-S3 chips.
GDBStub is supported for all ESP chips.
NOTE: On chips with an integrated USB Serial/JTAG Controller, it is reasonable to use OpenOCD + GDB for debugging.
### Configure the project