mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp_gdbstup: implement runtime gdbstub for riscv
This commit is contained in:
parent
36588c4b35
commit
9322c2b82d
@ -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()
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
11
components/esp_gdbstub/src/port/riscv/include/rv_decode.h
Normal file
11
components/esp_gdbstub/src/port/riscv/include/rv_decode.h
Normal 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);
|
158
components/esp_gdbstub/src/port/riscv/rv_decode.c
Normal file
158
components/esp_gdbstub/src/port/riscv/rv_decode.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user