forked from espressif/esp-idf
Merge branch 'feature/riscv-gdbstub-runtime' into 'master'
esp_gdbstub: implement runtime gdbstub for riscv Closes IDF-5621 See merge request espressif/esp-idf!23110
This commit is contained in:
@@ -92,6 +92,12 @@ test_certificate_bundle_on_host:
|
|||||||
- cd components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/
|
- cd components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/
|
||||||
- ./test_gen_crt_bundle.py
|
- ./test_gen_crt_bundle.py
|
||||||
|
|
||||||
|
test_gdbstub_on_host:
|
||||||
|
extends: .host_test_template
|
||||||
|
script:
|
||||||
|
- cd components/esp_gdbstub/test_gdbstub_host
|
||||||
|
- make test
|
||||||
|
|
||||||
|
|
||||||
test_idf_py:
|
test_idf_py:
|
||||||
extends: .host_test_template
|
extends: .host_test_template
|
||||||
|
@@ -11,7 +11,8 @@ if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
|||||||
"src/port/xtensa/xt_debugexception.S")
|
"src/port/xtensa/xt_debugexception.S")
|
||||||
list(APPEND priv_includes "src/port/xtensa/include")
|
list(APPEND priv_includes "src/port/xtensa/include")
|
||||||
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
|
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")
|
list(APPEND priv_includes "src/port/riscv/include")
|
||||||
endif()
|
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);
|
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
|
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
||||||
/**
|
/**
|
||||||
* Write registers from the saved frame of a given task to the GDB register file
|
* 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);
|
void esp_gdbstub_flush(void);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
/**
|
/**
|
||||||
* Read a data from fifo and detect start symbol
|
* Read a data from fifo and detect start symbol
|
||||||
* @return 1 if break symbol was detected, or 0 if not
|
* @return 1 if break symbol was detected, or 0 if not
|
||||||
*/
|
*/
|
||||||
int esp_gdbstub_getfifo(void);
|
int esp_gdbstub_getfifo(void);
|
||||||
|
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
|
||||||
/**** GDB packet related functions ****/
|
/**** 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_stall_other_cpus_end(void);
|
||||||
|
|
||||||
void esp_gdbstub_clear_step(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);
|
void esp_gdbstub_trigger_cpu(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
#include "soc/uart_reg.h"
|
#include "soc/uart_reg.h"
|
||||||
#include "soc/periph_defs.h"
|
#include "soc/periph_defs.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
@@ -187,6 +188,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
|
* @breef Handle UART interrupt
|
||||||
*
|
*
|
||||||
@@ -196,24 +216,7 @@ static inline void enable_all_wdts(void)
|
|||||||
*
|
*
|
||||||
* @param curr_regs - actual registers frame
|
* @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)
|
void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame)
|
||||||
{
|
{
|
||||||
temp_regs_frame = regs_frame;
|
temp_regs_frame = regs_frame;
|
||||||
@@ -264,11 +267,9 @@ void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame)
|
|||||||
if (res == -2) {
|
if (res == -2) {
|
||||||
esp_gdbstub_send_str_packet(NULL);
|
esp_gdbstub_send_str_packet(NULL);
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
|
||||||
if (res == GDBSTUB_ST_CONT) {
|
if (res == GDBSTUB_ST_CONT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
/* Resume other core */
|
/* Resume other core */
|
||||||
@@ -356,16 +357,12 @@ void gdbstub_handle_debug_int(esp_gdbstub_frame_t *regs_frame)
|
|||||||
gdb_debug_int = false;
|
gdb_debug_int = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
intr_handle_t intr_handle_;
|
|
||||||
extern void _xt_gdbstub_int(void * );
|
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
|
||||||
/** @brief Init gdbstub
|
/** @brief Init gdbstub
|
||||||
* Init uart interrupt for gdbstub
|
* Init uart interrupt for gdbstub
|
||||||
* */
|
* */
|
||||||
void esp_gdbstub_init(void)
|
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();
|
esp_gdbstub_init_dports();
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
|
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
|
||||||
@@ -474,45 +471,71 @@ static void handle_M_command(const unsigned char *cmd, int len)
|
|||||||
esp_gdbstub_send_end();
|
esp_gdbstub_send_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
void update_breakpoints(void)
|
void update_breakpoints(void)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
|
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
|
for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
|
||||||
if (bp_list[i] != 0) {
|
if (bp_list[i] != 0) {
|
||||||
esp_cpu_set_breakpoint(i, (const void *)bp_list[i]);
|
esp_cpu_set_breakpoint(i, (const void *)bp_list[i]);
|
||||||
} else {
|
} else {
|
||||||
esp_cpu_clear_breakpoint(i);
|
esp_cpu_clear_breakpoint(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < GDB_WP_SIZE; i++) {
|
for (size_t i = 0; i < SOC_CPU_WATCHPOINTS_NUM; i++) {
|
||||||
if (wp_list[i] != 0) {
|
if (wp_list[i] != 0) {
|
||||||
esp_cpu_set_watchpoint(i, (void *)wp_list[i], wp_size[i], wp_access[i]);
|
esp_cpu_set_watchpoint(i, (void *)wp_list[i], wp_size[i], wp_access[i]);
|
||||||
} else {
|
} else {
|
||||||
esp_cpu_clear_watchpoint(i);
|
esp_cpu_clear_watchpoint(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else // CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
|
#if (SOC_CPU_BREAKPOINTS_NUM != SOC_CPU_WATCHPOINTS_NUM)
|
||||||
|
#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 < SOC_CPU_BREAKPOINTS_NUM; b++) {
|
||||||
|
if (bp_list[b] != 0) {
|
||||||
|
esp_cpu_set_breakpoint(i, (const void *)bp_list[b]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t w = 0; w < SOC_CPU_WATCHPOINTS_NUM && i < SOC_CPU_WATCHPOINTS_NUM; w++) {
|
||||||
|
if (wp_list[w] != 0) {
|
||||||
|
esp_cpu_set_watchpoint(i, (void *)wp_list[w], wp_size[w], wp_access[w]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
|
||||||
|
esp_cpu_clear_breakpoint(i);
|
||||||
|
}
|
||||||
|
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
|
||||||
/** Write breakpoint */
|
/** Write breakpoint */
|
||||||
static void handle_Z0_command(const unsigned char *cmd, int len)
|
static void handle_Z0_command(const unsigned char *cmd, int len)
|
||||||
{
|
{
|
||||||
cmd++; /* skip 'Z' */
|
cmd++; /* skip 'Z' */
|
||||||
cmd++; /* skip '0' */
|
cmd++; /* skip '0' */
|
||||||
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
||||||
if (bp_count >= GDB_BP_SIZE) {
|
if (bp_count >= SOC_CPU_BREAKPOINTS_NUM) {
|
||||||
esp_gdbstub_send_str_packet("E02");
|
esp_gdbstub_send_str_packet("E02");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool add_bp = true;
|
bool add_bp = true;
|
||||||
/* Check if bp already exist */
|
/* Check if bp already exist */
|
||||||
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
|
for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
|
||||||
if (bp_list[i] == addr) {
|
if (bp_list[i] == addr) {
|
||||||
add_bp = false;
|
add_bp = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (true == add_bp) {
|
if (true == add_bp) {
|
||||||
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
|
for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
|
||||||
if (bp_list[i] == 0) {
|
if (bp_list[i] == 0) {
|
||||||
bp_list[i] = (uint32_t)addr;
|
bp_list[i] = (uint32_t)addr;
|
||||||
bp_count++;
|
bp_count++;
|
||||||
@@ -531,7 +554,7 @@ static void handle_z0_command(const unsigned char *cmd, int len)
|
|||||||
cmd++; /* skip 'z' */
|
cmd++; /* skip 'z' */
|
||||||
cmd++; /* skip '0' */
|
cmd++; /* skip '0' */
|
||||||
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
||||||
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
|
for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
|
||||||
if (bp_list[i] == addr) {
|
if (bp_list[i] == addr) {
|
||||||
bp_list[i] = 0;
|
bp_list[i] = 0;
|
||||||
bp_count--;
|
bp_count--;
|
||||||
@@ -550,7 +573,7 @@ static void handle_Z2_command(const unsigned char *cmd, int len)
|
|||||||
cmd++;
|
cmd++;
|
||||||
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
|
||||||
|
|
||||||
if (wp_count >= GDB_WP_SIZE) {
|
if (wp_count >= SOC_CPU_WATCHPOINTS_NUM) {
|
||||||
esp_gdbstub_send_str_packet("E02");
|
esp_gdbstub_send_str_packet("E02");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -569,7 +592,7 @@ static void handle_Z3_command(const unsigned char *cmd, int len)
|
|||||||
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
||||||
cmd++;
|
cmd++;
|
||||||
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
|
||||||
if (wp_count >= GDB_WP_SIZE) {
|
if (wp_count >= SOC_CPU_WATCHPOINTS_NUM) {
|
||||||
esp_gdbstub_send_str_packet("E02");
|
esp_gdbstub_send_str_packet("E02");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -588,7 +611,7 @@ static void handle_Z4_command(const unsigned char *cmd, int len)
|
|||||||
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
||||||
cmd++;
|
cmd++;
|
||||||
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
|
||||||
if (wp_count >= GDB_WP_SIZE) {
|
if (wp_count >= SOC_CPU_WATCHPOINTS_NUM) {
|
||||||
esp_gdbstub_send_str_packet("E02");
|
esp_gdbstub_send_str_packet("E02");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -605,7 +628,7 @@ static void handle_zx_command(const unsigned char *cmd, int len)
|
|||||||
cmd++; /* skip 'z' */
|
cmd++; /* skip 'z' */
|
||||||
cmd++; /* skip 'x' */
|
cmd++; /* skip 'x' */
|
||||||
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
||||||
for (size_t i = 0; i < GDB_WP_SIZE; i++) {
|
for (size_t i = 0; i < SOC_CPU_WATCHPOINTS_NUM; i++) {
|
||||||
if (wp_list[i] == addr) {
|
if (wp_list[i] == addr) {
|
||||||
wp_access[i] = 0;
|
wp_access[i] = 0;
|
||||||
wp_list[i] = 0;
|
wp_list[i] = 0;
|
||||||
@@ -626,7 +649,7 @@ static void handle_S_command(const unsigned char *cmd, int len)
|
|||||||
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;
|
step_in_progress = true;
|
||||||
esp_gdbstub_do_step();
|
esp_gdbstub_do_step((esp_gdbstub_frame_t *)temp_regs_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Step ... */
|
/** Step ... */
|
||||||
@@ -888,9 +911,11 @@ static eTaskState get_task_state(size_t index)
|
|||||||
eTaskState result = eReady;
|
eTaskState result = eReady;
|
||||||
TaskHandle_t handle = NULL;
|
TaskHandle_t handle = NULL;
|
||||||
get_task_handle(index, &handle);
|
get_task_handle(index, &handle);
|
||||||
|
#if CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
if (gdb_debug_int == false) {
|
if (gdb_debug_int == false) {
|
||||||
result = eTaskGetState(handle);
|
result = eTaskGetState(handle);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
|
||||||
int esp_gdbstub_getchar()
|
int esp_gdbstub_getchar(void)
|
||||||
{
|
{
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
// retry the read until we succeed
|
// retry the read until we succeed
|
||||||
@@ -39,6 +39,12 @@ void esp_gdbstub_flush(void)
|
|||||||
usb_serial_jtag_ll_txfifo_flush();
|
usb_serial_jtag_ll_txfifo_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
int esp_gdbstub_getfifo(void)
|
||||||
|
{
|
||||||
|
return 0; // TODO: IDF-7264
|
||||||
|
}
|
||||||
|
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
|
||||||
#else // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
#else // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
|
||||||
@@ -98,6 +104,7 @@ void esp_gdbstub_flush(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
int esp_gdbstub_getfifo(void)
|
int esp_gdbstub_getfifo(void)
|
||||||
{
|
{
|
||||||
esp_gdbstub_uart_init();
|
esp_gdbstub_uart_init();
|
||||||
@@ -115,5 +122,5 @@ int esp_gdbstub_getfifo(void)
|
|||||||
uart_ll_clr_intsts_mask(gdb_uart, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
|
uart_ll_clr_intsts_mask(gdb_uart, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
|
||||||
return doDebug;
|
return doDebug;
|
||||||
}
|
}
|
||||||
|
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
@@ -7,8 +7,12 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp_gdbstub.h"
|
#include "esp_gdbstub.h"
|
||||||
#include "esp_gdbstub_common.h"
|
#include "esp_gdbstub_common.h"
|
||||||
|
#include "esp_cpu.h"
|
||||||
|
#include "rv_decode.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
extern volatile esp_gdbstub_frame_t *temp_regs_frame;
|
||||||
|
|
||||||
static inline void init_regfile(esp_gdbstub_gdb_regfile_t *dst)
|
static inline void init_regfile(esp_gdbstub_gdb_regfile_t *dst)
|
||||||
{
|
{
|
||||||
memset(dst, 0, sizeof(*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);
|
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 */
|
/* Represents FreeRTOS TCB structure */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -32,6 +36,7 @@ typedef struct {
|
|||||||
/* Other members aren't needed */
|
/* Other members aren't needed */
|
||||||
} dummy_tcb_t;
|
} dummy_tcb_t;
|
||||||
|
|
||||||
|
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
||||||
|
|
||||||
void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst)
|
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
|
||||||
|
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS || CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
|
||||||
int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
|
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(esp_gdbstub_frame_t *frame)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void esp_gdbstub_do_step()
|
|
||||||
{
|
{
|
||||||
|
#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)
|
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)
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,17 +20,6 @@ typedef struct {
|
|||||||
uint32_t pc;
|
uint32_t pc;
|
||||||
} esp_gdbstub_gdb_regfile_t;
|
} esp_gdbstub_gdb_regfile_t;
|
||||||
|
|
||||||
|
|
||||||
// Amount of HW breakpoints used in GDB
|
|
||||||
#ifndef GDB_BP_SIZE
|
|
||||||
#define GDB_BP_SIZE 2
|
|
||||||
#endif // GDB_BP_SIZE
|
|
||||||
|
|
||||||
// Amount of HW watchpoints used in GDB
|
|
||||||
#ifndef GDB_WP_SIZE
|
|
||||||
#define GDB_WP_SIZE 2
|
|
||||||
#endif // GDB_WP_SIZE
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
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"
|
.section .iram1, "ax"
|
||||||
|
|
||||||
.global gdbstub_handle_uart_int
|
.global gdbstub_handle_uart_int
|
||||||
.global _xt_gdbstub_int
|
.global esp_gdbstub_int
|
||||||
|
.type esp_gdbstub_int, @function
|
||||||
.align 4
|
.align 4
|
||||||
|
|
||||||
_xt_gdbstub_int:
|
esp_gdbstub_int:
|
||||||
|
|
||||||
/* Allocate exception frame and save minimal context. */
|
/* Allocate exception frame and save minimal context. */
|
||||||
mov a0, sp
|
mov a0, sp
|
||||||
|
@@ -139,7 +139,7 @@ static bool stall_started = false;
|
|||||||
/** @brief GDB stall other CPU
|
/** @brief GDB stall other CPU
|
||||||
* 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 CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
if (stall_started == false) {
|
if (stall_started == false) {
|
||||||
@@ -152,7 +152,7 @@ void esp_gdbstub_stall_other_cpus_start()
|
|||||||
/** @brief GDB end stall other CPU
|
/** @brief GDB end stall other CPU
|
||||||
* 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 CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
if (stall_started == true) {
|
if (stall_started == true) {
|
||||||
@@ -174,7 +174,7 @@ void esp_gdbstub_clear_step(void)
|
|||||||
/** @brief GDB do step
|
/** @brief GDB do step
|
||||||
* GDB do one 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
|
// 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
|
// application will hang because it will try to step uart interrupt. That's why we have to set ICOUNTLEVEL=1
|
||||||
|
@@ -85,17 +85,6 @@ typedef struct {
|
|||||||
|
|
||||||
} esp_gdbstub_gdb_regfile_t;
|
} esp_gdbstub_gdb_regfile_t;
|
||||||
|
|
||||||
|
|
||||||
// Amount of HW breakpoints used in GDB
|
|
||||||
#ifndef GDB_BP_SIZE
|
|
||||||
#define GDB_BP_SIZE 2
|
|
||||||
#endif // GDB_BP_SIZE
|
|
||||||
|
|
||||||
// Amount of HW watchpoints used in GDB
|
|
||||||
#ifndef GDB_WP_SIZE
|
|
||||||
#define GDB_WP_SIZE 2
|
|
||||||
#endif // GDB_WP_SIZE
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
9
components/esp_gdbstub/test_gdbstub_host/Makefile
Normal file
9
components/esp_gdbstub/test_gdbstub_host/Makefile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
TOPTARGETS := all clean test coverage_report
|
||||||
|
|
||||||
|
SUBDIRS := $(wildcard */.)
|
||||||
|
|
||||||
|
$(TOPTARGETS): $(SUBDIRS)
|
||||||
|
$(SUBDIRS):
|
||||||
|
$(MAKE) -C $@ $(MAKECMDGOALS)
|
||||||
|
|
||||||
|
.PHONY: $(TOPTARGETS) $(SUBDIRS)
|
66
components/esp_gdbstub/test_gdbstub_host/rv_decode/Makefile
Normal file
66
components/esp_gdbstub/test_gdbstub_host/rv_decode/Makefile
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
TEST_PROGRAM=test_gdbstub_rv
|
||||||
|
GDBSTUB_SRC_DIR=../..
|
||||||
|
all: $(TEST_PROGRAM)
|
||||||
|
|
||||||
|
SOURCE_FILES = \
|
||||||
|
$(addprefix $(GDBSTUB_SRC_DIR)/src/, \
|
||||||
|
port/riscv/rv_decode.c \
|
||||||
|
) \
|
||||||
|
test_rv_decode.cpp \
|
||||||
|
main.cpp
|
||||||
|
|
||||||
|
INCLUDE_FLAGS = -I./include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/private_include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/src/port/riscv/include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/../../tools/catch \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/../esp_hw_support/include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/../soc/esp32c3/include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/../esp_common/include \
|
||||||
|
-I$(GDBSTUB_SRC_DIR)/../riscv/include
|
||||||
|
CPPFLAGS += $(INCLUDE_FLAGS) -D__riscv_c -Wall -Werror -g --coverage
|
||||||
|
CFLAGS += $(INCLUDE_FLAGS) -D__riscv_c -Wall -Werror -g --coverage
|
||||||
|
LDFLAGS += -lstdc++ --coverage
|
||||||
|
|
||||||
|
ifeq ($(CC),clang)
|
||||||
|
CFLAGS += -fsanitize=address
|
||||||
|
CXXFLAGS += -fsanitize=address
|
||||||
|
LDFLAGS += -fsanitize=address
|
||||||
|
endif
|
||||||
|
|
||||||
|
OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))
|
||||||
|
|
||||||
|
COVERAGE_FILES = $(OBJ_FILES:.o=.gc*)
|
||||||
|
|
||||||
|
$(TEST_PROGRAM): $(OBJ_FILES)
|
||||||
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
$(OUTPUT_DIR):
|
||||||
|
mkdir -p $(OUTPUT_DIR)
|
||||||
|
|
||||||
|
test: $(TEST_PROGRAM)
|
||||||
|
./$(TEST_PROGRAM) -d yes exclude:[long]
|
||||||
|
|
||||||
|
long-test: $(TEST_PROGRAM)
|
||||||
|
./$(TEST_PROGRAM) -d yes
|
||||||
|
|
||||||
|
$(COVERAGE_FILES): $(TEST_PROGRAM) long-test
|
||||||
|
|
||||||
|
coverage.info: $(COVERAGE_FILES)
|
||||||
|
find $(GDBSTUB_SRC_DIR)/src/ -name "*.gcno" -exec gcov -r -pb {} +
|
||||||
|
lcov --capture --directory $(GDBSTUB_SRC_DIR)/src --output-file coverage.info
|
||||||
|
|
||||||
|
coverage_report: coverage.info
|
||||||
|
genhtml coverage.info --output-directory coverage_report
|
||||||
|
@echo "Coverage report is in coverage_report/index.html"
|
||||||
|
|
||||||
|
clean-coverage:
|
||||||
|
rm -f $(COVERAGE_FILES) *.gcov
|
||||||
|
rm -rf coverage_report/
|
||||||
|
rm -f coverage.info
|
||||||
|
|
||||||
|
clean: clean-coverage
|
||||||
|
rm -f $(OBJ_FILES) $(TEST_PROGRAM)
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: clean clean-coverage all test long-test
|
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include "catch.hpp"
|
@@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "esp_gdbstub_common.h"
|
||||||
|
#include "rv_decode.h"
|
||||||
|
|
||||||
|
esp_gdbstub_frame_t regs;
|
||||||
|
esp_gdbstub_frame_t *temp_regs_frame = ®s;
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct inst_list_s {
|
||||||
|
uint32_t inst;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct inst_list_s rv32i_nojump[] = {
|
||||||
|
{0x31011d37, "lui"},
|
||||||
|
{0x00000917, "auipc"},
|
||||||
|
{0x0b1c0e03, "lb"},
|
||||||
|
{0xc8aecc03, "lbu"},
|
||||||
|
{0x07271103, "lh"},
|
||||||
|
{0x02525403, "lhu"},
|
||||||
|
{0x00392903, "lw"},
|
||||||
|
{0x000000a3, "sb"},
|
||||||
|
{0x00009da3, "sh"},
|
||||||
|
{0x0000a423, "sw"},
|
||||||
|
{0xd3060d13, "addi"},
|
||||||
|
{0x000cad13, "slti"},
|
||||||
|
{0x00003d13, "sltiu"},
|
||||||
|
{0x0001ce13, "xori"},
|
||||||
|
{0x00006013, "ori"},
|
||||||
|
{0x00007c13, "andi"},
|
||||||
|
{0x00099913, "slli"},
|
||||||
|
{0x00005e13, "srli"},
|
||||||
|
{0x4107d313, "srai"},
|
||||||
|
{0x0000fe33, "and"},
|
||||||
|
{0x401e0933, "sub"},
|
||||||
|
{0x000012b3, "sll"},
|
||||||
|
{0x0036a033, "slt"},
|
||||||
|
{0x00853433, "sltu"},
|
||||||
|
{0x00004033, "xor"},
|
||||||
|
{0x00005133, "srl"},
|
||||||
|
{0x40e7d7b3, "sra"},
|
||||||
|
{0x000062b3, "or"},
|
||||||
|
{0x000071b3, "and"},
|
||||||
|
{0x0000000f, "fence"},
|
||||||
|
{0x0000100f, "fence.i"},
|
||||||
|
{0x00002ef3, "csrr"},
|
||||||
|
{0x010312f3, "csrrw"},
|
||||||
|
{0x00005473, "csrrwi"},
|
||||||
|
{0x0001b073, "csrc"},
|
||||||
|
{0x01007073, "csrci"},
|
||||||
|
{0x000078f3, "csrrci"},
|
||||||
|
{0x0002a073, "csrs"},
|
||||||
|
{0x0000e073, "csrsi"},
|
||||||
|
{0x000225f3, "csrrs"}};
|
||||||
|
|
||||||
|
struct inst_list_s rv32m_nojump[] = {
|
||||||
|
{0x025a0133, "mul"},
|
||||||
|
{0x02031233, "mulh"},
|
||||||
|
{0x02f437b3, "mulhu"},
|
||||||
|
{0x03012e33, "mulhsu"},
|
||||||
|
{0x02a6c6b3, "div"},
|
||||||
|
{0x02f5d533, "divu"},
|
||||||
|
{0x02a47433, "remu"},
|
||||||
|
{0x0324e4b3, "rem"}};
|
||||||
|
|
||||||
|
struct inst_list_s rv32a_nojump[] = {
|
||||||
|
{0x100523af, "lr.w"},
|
||||||
|
{0x19d2ae2f, "sc.w"},
|
||||||
|
{0x0c07202f, "amoswap.w"},
|
||||||
|
{0x0000222f, "amoadd.w"},
|
||||||
|
{0x2072a2af, "amoxor.w"},
|
||||||
|
{0x6072a2af, "amoand.w"},
|
||||||
|
{0x4072a2af, "amoor.w"},
|
||||||
|
{0x801c262f, "amomin.w"}};
|
||||||
|
|
||||||
|
struct inst_list_s rv32c_nojump[] = {
|
||||||
|
{0x8522, "c.mv"},
|
||||||
|
{0x4200, "c.lw"},
|
||||||
|
{0x4501, "c.li"},
|
||||||
|
{0x6605, "c.lui"},
|
||||||
|
{0x8e65, "c.and"},
|
||||||
|
{0x985d, "c.andi"},
|
||||||
|
{0x8c9d, "c.sub"},
|
||||||
|
{0x8f41, "c.or"},
|
||||||
|
{0x8da5, "c.xor"},
|
||||||
|
{0x100a, "c.slli"},
|
||||||
|
{0x0002, "c.slli64"},
|
||||||
|
{0x8011, "c.srli"},
|
||||||
|
{0x8081, "c.srli64"},
|
||||||
|
{0x9459, "c.srai"},
|
||||||
|
{0x8481, "c.srai64"}};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("decode rv32i instructions")
|
||||||
|
{
|
||||||
|
uintptr_t pc;
|
||||||
|
uint32_t inst;
|
||||||
|
uintptr_t inst_addr = (uintptr_t)&inst;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(rv32i_nojump)/sizeof(rv32i_nojump[0]); i++) {
|
||||||
|
inst = rv32i_nojump[i].inst;
|
||||||
|
DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name);
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* beq positive offset */
|
||||||
|
/* 420147bc: 0af50b63 beq a0,a5,42014872 */
|
||||||
|
regs.a0 = regs.a5 = 0x777;
|
||||||
|
inst = 0x0af50b63;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42014872 - 0x420147bc));
|
||||||
|
regs.a0 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
|
||||||
|
/* beq negative offset */
|
||||||
|
/* 40383b48: fae789e3 beq a5,a4,40383afa */
|
||||||
|
regs.a4 = regs.a5 = 0x777;
|
||||||
|
inst = 0xfae789e3;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x40383afa - 0x40383b48));
|
||||||
|
regs.a4 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
|
||||||
|
/* bne positive offset */
|
||||||
|
/* 42014798: 10f51163 bne a0,a5,4201489a */
|
||||||
|
regs.a0 = regs.a5 = 0x777;
|
||||||
|
inst = 0x10f51163;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a0 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4201489a - 0x42014798));
|
||||||
|
|
||||||
|
/* bne negative offset */
|
||||||
|
/* 4200b7e0: fed711e3 bne a4,a3,4200b7c2 */
|
||||||
|
regs.a3 = regs.a4 = 0x777;
|
||||||
|
inst = 0xfed711e3;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a3 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200b7c2 - 0x4200b7e0));
|
||||||
|
|
||||||
|
/* blt positive offset */
|
||||||
|
/* 420117a0: 04f54763 blt a0,a5,420117ee */
|
||||||
|
regs.a0 = regs.a5 = 0x777;
|
||||||
|
inst = 0x04f54763;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a0++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a0 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420117ee - 0x420117a0));
|
||||||
|
|
||||||
|
/* blt negative offset */
|
||||||
|
/* 42014448: fcd5c0e3 blt a1,a3,42014408 */
|
||||||
|
regs.a1 = regs.a3 = 0x777;
|
||||||
|
inst = 0xfcd5c0e3;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a1++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a1 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42014408 - 0x42014448));
|
||||||
|
|
||||||
|
/* bltu positive offset */
|
||||||
|
/* 42010dfa: 00f56a63 bltu a0,a5,42010e0e */
|
||||||
|
regs.a0 = regs.a5 = 0x777;
|
||||||
|
inst = 0x00f56a63;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a0++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.a0 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42010e0e - 0x42010dfa));
|
||||||
|
|
||||||
|
/* bltu negative offset */
|
||||||
|
/* 4200137c: f79be5e3 bltu s7,s9,420012e6 */
|
||||||
|
regs.s7 = regs.s9 = 0x777;
|
||||||
|
inst = 0xf79be5e3;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.s7++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
regs.s7 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420012e6 - 0x4200137c));
|
||||||
|
|
||||||
|
/* bge positive offset */
|
||||||
|
/* 4200cf88: 02f55763 bge a0,a5,4200cfb6 */
|
||||||
|
regs.a0 = regs.a5 = 0x777;
|
||||||
|
inst = 0x02f55763;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200cfb6 - 0x4200cf88));
|
||||||
|
regs.a0++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200cfb6 - 0x4200cf88));
|
||||||
|
regs.a0 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
|
||||||
|
/* bge negative offset */
|
||||||
|
/* 420001bc: fe87d6e3 bge a5,s0,420001a8 */
|
||||||
|
regs.a5 = regs.s0 = 0x777;
|
||||||
|
inst = 0xfe87d6e3;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420001a8 - 0x420001bc));
|
||||||
|
regs.a5++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420001a8 - 0x420001bc));
|
||||||
|
regs.a5 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
|
||||||
|
/* bgeu positive offset */
|
||||||
|
/* 40383552: 02f57d63 bgeu a0,a5,4038358c */
|
||||||
|
regs.a0 = regs.a5 = 0x777;
|
||||||
|
inst = 0x02f57d63;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4038358c - 0x40383552));
|
||||||
|
regs.a0++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4038358c - 0x40383552));
|
||||||
|
regs.a0 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
|
||||||
|
/* bgeu negative offset */
|
||||||
|
/* 42001178: fae7fbe3 bgeu a5,a4,4200112e */
|
||||||
|
regs.a5 = regs.a4 = 0x777;
|
||||||
|
inst = 0xfae7fbe3;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200112e - 0x42001178));
|
||||||
|
regs.a5++;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200112e - 0x42001178));
|
||||||
|
regs.a5 = 0x111;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
|
||||||
|
/* jal positive offset */
|
||||||
|
/* 4200f872: 016010ef jal ra,42010888 */
|
||||||
|
inst = 0x016010ef;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42010888 - 0x4200f872));
|
||||||
|
|
||||||
|
/* jal negative offset */
|
||||||
|
/* 42015c94: e31ff0ef jal ra,42015ac4 */
|
||||||
|
inst = 0xe31ff0ef;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42015ac4 - 0x42015c94));
|
||||||
|
|
||||||
|
/* jalr positive offset */
|
||||||
|
/* 4200fee0: 47c080e7 jalr 1148(ra) # 40000358 */
|
||||||
|
regs.ra = 0x40000358 - 1148;
|
||||||
|
inst = 0x47c080e7;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == 0x40000358);
|
||||||
|
|
||||||
|
/* jalr negative offset */
|
||||||
|
/* 4038779e: 8a6080e7 jalr -1882(ra) # 40000040 */
|
||||||
|
regs.ra = 0x40000040 - (-1882);
|
||||||
|
inst = 0x8a6080e7;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == 0x40000040);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("decode rv32c instructions")
|
||||||
|
{
|
||||||
|
uintptr_t pc;
|
||||||
|
uint32_t inst;
|
||||||
|
uintptr_t inst_addr = (uintptr_t)&inst;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(rv32c_nojump)/sizeof(rv32c_nojump[0]); i++) {
|
||||||
|
inst = rv32c_nojump[i].inst;
|
||||||
|
DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name);
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* beqz positive offset */
|
||||||
|
/* 4200db50: c119 beqz a0,4200db56 */
|
||||||
|
regs.a0 = 0;
|
||||||
|
inst = 0xc119;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200db56 - 0x4200db50));
|
||||||
|
regs.a0 = 0xff;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2);
|
||||||
|
|
||||||
|
/* beqz negative offset */
|
||||||
|
/* 42014886: d96d beqz a0,42014878 */
|
||||||
|
regs.a0 = 0;
|
||||||
|
inst = 0xd96d;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42014878 - 0x42014886));
|
||||||
|
regs.a0 = 0xff;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2);
|
||||||
|
|
||||||
|
/* bnez positive offset */
|
||||||
|
/* 4200ee80: e165 bnez a0,4200ef60 */
|
||||||
|
regs.a0 = 0xff;
|
||||||
|
inst = 0xe165;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200ef60 - 0x4200ee80));
|
||||||
|
regs.a0 = 0;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2);
|
||||||
|
|
||||||
|
/* bnez negative offset */
|
||||||
|
/* 4200f472: ff29 bnez a4,4200f3cc */
|
||||||
|
regs.a4 = 0xff;
|
||||||
|
inst = 0xff29;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200f3cc - 0x4200f472));
|
||||||
|
regs.a4 = 0;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2);
|
||||||
|
|
||||||
|
/* jal positive offset */
|
||||||
|
/* 4200ffb8: 2d5d jal 4201066e */
|
||||||
|
inst = 0x2d5d;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4201066e - 0x4200ffb8));
|
||||||
|
|
||||||
|
/* jal negative offset */
|
||||||
|
/* 42005588: 24cd jal 4200586a */
|
||||||
|
inst = 0x24cd;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200586a - 0x42005588));
|
||||||
|
|
||||||
|
/* 42010366: 9782 jalr a5 */
|
||||||
|
regs.a5 = 0x88888888;
|
||||||
|
inst = 0x9782;
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == 0x88888888);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("decode rv32a instructions")
|
||||||
|
{
|
||||||
|
uintptr_t pc;
|
||||||
|
uint32_t inst;
|
||||||
|
uintptr_t inst_addr = (uintptr_t)&inst;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(rv32a_nojump)/sizeof(rv32a_nojump[0]); i++) {
|
||||||
|
inst = rv32i_nojump[i].inst;
|
||||||
|
DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name);
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("decode rv32m instructions")
|
||||||
|
{
|
||||||
|
uintptr_t pc;
|
||||||
|
uint32_t inst;
|
||||||
|
uintptr_t inst_addr = (uintptr_t)&inst;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(rv32m_nojump)/sizeof(rv32m_nojump[0]); i++) {
|
||||||
|
inst = rv32i_nojump[i].inst;
|
||||||
|
DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name);
|
||||||
|
CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4);
|
||||||
|
}
|
||||||
|
}
|
@@ -137,10 +137,6 @@ extern "C" {
|
|||||||
Trigger Module register fields (Debug specification)
|
Trigger Module register fields (Debug specification)
|
||||||
********************************************************/
|
********************************************************/
|
||||||
|
|
||||||
/* tcontrol CSRs not recognized by toolchain currently */
|
|
||||||
#define CSR_TCONTROL 0x7a5
|
|
||||||
#define CSR_TDATA1 0x7a1
|
|
||||||
|
|
||||||
#define TCONTROL_MTE (1<<3) /*R/W, Current M mode trigger enable bit*/
|
#define TCONTROL_MTE (1<<3) /*R/W, Current M mode trigger enable bit*/
|
||||||
#define TCONTROL_MPTE (1<<7) /*R/W, Previous M mode trigger enable bit*/
|
#define TCONTROL_MPTE (1<<7) /*R/W, Previous M mode trigger enable bit*/
|
||||||
|
|
||||||
@@ -152,6 +148,7 @@ extern "C" {
|
|||||||
#define TDATA1_MATCH (1<<7)
|
#define TDATA1_MATCH (1<<7)
|
||||||
#define TDATA1_MATCH_V (0xF) /*R/W,Address match type :0 : Exact byte match 1 : NAPOT range match */
|
#define TDATA1_MATCH_V (0xF) /*R/W,Address match type :0 : Exact byte match 1 : NAPOT range match */
|
||||||
#define TDATA1_MATCH_S (7)
|
#define TDATA1_MATCH_S (7)
|
||||||
|
#define TDATA1_HIT_S (20)
|
||||||
|
|
||||||
|
|
||||||
/* RISC-V CSR macros
|
/* RISC-V CSR macros
|
||||||
|
@@ -132,49 +132,66 @@ FORCE_INLINE_ATTR void rv_utils_set_breakpoint(int bp_num, uint32_t bp_addr)
|
|||||||
/* The code bellow sets breakpoint which will trigger `Breakpoint` exception
|
/* The code bellow sets breakpoint which will trigger `Breakpoint` exception
|
||||||
* instead transfering control to debugger. */
|
* instead transfering control to debugger. */
|
||||||
RV_WRITE_CSR(tselect, bp_num);
|
RV_WRITE_CSR(tselect, bp_num);
|
||||||
RV_SET_CSR(CSR_TCONTROL, TCONTROL_MTE);
|
RV_WRITE_CSR(tcontrol, TCONTROL_MPTE | TCONTROL_MTE);
|
||||||
RV_SET_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE);
|
RV_WRITE_CSR(tdata1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE);
|
||||||
RV_WRITE_CSR(tdata2, bp_addr);
|
RV_WRITE_CSR(tdata2, bp_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR void rv_utils_set_watchpoint(int wp_num,
|
||||||
|
uint32_t wp_addr,
|
||||||
|
size_t size,
|
||||||
|
bool on_read,
|
||||||
|
bool on_write)
|
||||||
|
{
|
||||||
|
RV_WRITE_CSR(tselect, wp_num);
|
||||||
|
RV_WRITE_CSR(tcontrol, TCONTROL_MPTE | TCONTROL_MTE);
|
||||||
|
RV_WRITE_CSR(tdata1, TDATA1_USER |
|
||||||
|
TDATA1_MACHINE |
|
||||||
|
TDATA1_MATCH |
|
||||||
|
(on_read ? TDATA1_LOAD : 0) |
|
||||||
|
(on_write ? TDATA1_STORE : 0));
|
||||||
|
/* From RISC-V Debug Specification:
|
||||||
|
* NAPOT (Naturally Aligned Power-Of-Two):
|
||||||
|
* Matches when the top M bits of any compare value match the top M bits of tdata2.
|
||||||
|
* M is XLEN − 1 minus the index of the least-significant bit containing 0 in tdata2.
|
||||||
|
*
|
||||||
|
* Note: Expectng that size is number power of 2
|
||||||
|
*
|
||||||
|
* Examples for understanding how to calculate NAPOT:
|
||||||
|
*
|
||||||
|
* nnnn...nnnn0 2-byte NAPOT range
|
||||||
|
* nnnn...nnn01 4-byte NAPOT range
|
||||||
|
* nnnn...nn011 8-byte NAPOT range
|
||||||
|
* nnnn...n0111 16-byte NAPOT range
|
||||||
|
* nnnn...01111 32-byte NAPOT range
|
||||||
|
* * where n are bits from original address
|
||||||
|
*/
|
||||||
|
const uint32_t half_size = size >> 1;
|
||||||
|
uint32_t napot = wp_addr;
|
||||||
|
napot &= ~half_size; /* set the least-significant bit with zero */
|
||||||
|
napot |= half_size - 1; /* fill all bits with ones after least-significant bit */
|
||||||
|
RV_WRITE_CSR(tdata2, napot);
|
||||||
|
}
|
||||||
|
|
||||||
FORCE_INLINE_ATTR void rv_utils_clear_breakpoint(int bp_num)
|
FORCE_INLINE_ATTR void rv_utils_clear_breakpoint(int bp_num)
|
||||||
{
|
{
|
||||||
RV_WRITE_CSR(tselect, bp_num);
|
RV_WRITE_CSR(tselect, bp_num);
|
||||||
RV_CLEAR_CSR(CSR_TCONTROL, TCONTROL_MTE);
|
/* tdata1 is a WARL(write any read legal) register
|
||||||
RV_CLEAR_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE);
|
* We can just write 0 to it
|
||||||
}
|
*/
|
||||||
|
RV_WRITE_CSR(tdata1, 0);
|
||||||
FORCE_INLINE_ATTR void rv_utils_set_watchpoint(int wp_num,
|
|
||||||
uint32_t wp_addr,
|
|
||||||
size_t size,
|
|
||||||
bool on_read,
|
|
||||||
bool on_write)
|
|
||||||
{
|
|
||||||
RV_WRITE_CSR(tselect, wp_num);
|
|
||||||
RV_SET_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE);
|
|
||||||
RV_SET_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE);
|
|
||||||
RV_SET_CSR_FIELD(CSR_TDATA1, (long unsigned int) TDATA1_MATCH, 1);
|
|
||||||
|
|
||||||
// add 0 in napot encoding
|
|
||||||
uint32_t addr_napot;
|
|
||||||
addr_napot = ((uint32_t) wp_addr) | ((size >> 1) - 1);
|
|
||||||
if (on_read) {
|
|
||||||
RV_SET_CSR(CSR_TDATA1, TDATA1_LOAD);
|
|
||||||
}
|
|
||||||
if (on_write) {
|
|
||||||
RV_SET_CSR(CSR_TDATA1, TDATA1_STORE);
|
|
||||||
}
|
|
||||||
RV_WRITE_CSR(tdata2, addr_napot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE_ATTR void rv_utils_clear_watchpoint(int wp_num)
|
FORCE_INLINE_ATTR void rv_utils_clear_watchpoint(int wp_num)
|
||||||
{
|
{
|
||||||
RV_WRITE_CSR(tselect, wp_num);
|
/* riscv have the same registers for breakpoints and watchpoints */
|
||||||
RV_CLEAR_CSR(CSR_TCONTROL, TCONTROL_MTE);
|
rv_utils_clear_breakpoint(wp_num);
|
||||||
RV_CLEAR_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE);
|
}
|
||||||
RV_CLEAR_CSR_FIELD(CSR_TDATA1, (long unsigned int) TDATA1_MATCH);
|
|
||||||
RV_CLEAR_CSR(CSR_TDATA1, TDATA1_MACHINE);
|
FORCE_INLINE_ATTR bool rv_utils_is_trigger_fired(int id)
|
||||||
RV_CLEAR_CSR(CSR_TDATA1, TDATA1_LOAD | TDATA1_STORE | TDATA1_EXECUTE);
|
{
|
||||||
|
RV_WRITE_CSR(tselect, id);
|
||||||
|
return (RV_READ_CSR(tdata1) >> TDATA1_HIT_S) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------- Debugger -------------------------
|
// ---------------------- Debugger -------------------------
|
||||||
|
@@ -102,6 +102,9 @@
|
|||||||
.global rtos_int_enter
|
.global rtos_int_enter
|
||||||
.global rtos_int_exit
|
.global rtos_int_exit
|
||||||
.global _global_interrupt_handler
|
.global _global_interrupt_handler
|
||||||
|
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
.global gdbstub_handle_debug_int
|
||||||
|
#endif
|
||||||
|
|
||||||
.section .exception_vectors.text
|
.section .exception_vectors.text
|
||||||
/* This is the vector table. MTVEC points here.
|
/* This is the vector table. MTVEC points here.
|
||||||
@@ -170,16 +173,27 @@ _panic_handler:
|
|||||||
* have an pseudo excause */
|
* have an pseudo excause */
|
||||||
mv a0, sp
|
mv a0, sp
|
||||||
csrr a1, mcause
|
csrr a1, mcause
|
||||||
|
|
||||||
/* Branches instructions don't accept immediates values, so use t1 to
|
/* Branches instructions don't accept immediates values, so use t1 to
|
||||||
* store our comparator */
|
* store our comparator */
|
||||||
li t0, 0x80000000
|
li t0, 0x80000000
|
||||||
bgeu a1, t0, _call_panic_handler
|
bgeu a1, t0, _call_panic_handler
|
||||||
sw a1, RV_STK_MCAUSE(sp)
|
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 */
|
/* exception_from_panic never returns */
|
||||||
jal panic_from_exception
|
jal panic_from_exception
|
||||||
/* We arrive here if the exception handler has returned. */
|
/* We arrive here if the exception handler has returned. */
|
||||||
j _return_from_exception
|
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:
|
_call_panic_handler:
|
||||||
/* Remove highest bit from mcause (a1) register and save it in the
|
/* Remove highest bit from mcause (a1) register and save it in the
|
||||||
* structure */
|
* structure */
|
||||||
|
@@ -60,12 +60,6 @@ examples/system/gcov:
|
|||||||
temporary: true
|
temporary: true
|
||||||
reason: lack of runners
|
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:
|
examples/system/heap_task_tracking:
|
||||||
disable:
|
disable:
|
||||||
- if: IDF_TARGET == "esp32c2" or IDF_TARGET == "esp32h2"
|
- 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
|
# 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.
|
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.
|
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
|
### Configure the project
|
||||||
|
|
||||||
|
@@ -130,6 +130,12 @@ tools/test_apps/system/gdb_loadable_elf:
|
|||||||
temporary: true
|
temporary: true
|
||||||
reason: target esp32c6, esp32h2 is not supported yet
|
reason: target esp32c6, esp32h2 is not supported yet
|
||||||
|
|
||||||
|
tools/test_apps/system/gdbstub_runtime:
|
||||||
|
disable_test:
|
||||||
|
- if: IDF_TARGET in ["esp32c2", "esp32h2"]
|
||||||
|
temporary: true
|
||||||
|
reason: resolve IDF-7264
|
||||||
|
|
||||||
tools/test_apps/system/longjmp_test:
|
tools/test_apps/system/longjmp_test:
|
||||||
enable:
|
enable:
|
||||||
- if: IDF_TARGET in ["esp32", "esp32s2", "esp32s3"]
|
- if: IDF_TARGET in ["esp32", "esp32s2", "esp32s3"]
|
||||||
|
8
tools/test_apps/system/gdbstub_runtime/CMakeLists.txt
Normal file
8
tools/test_apps/system/gdbstub_runtime/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(test_gdbstub_runtime)
|
2
tools/test_apps/system/gdbstub_runtime/README.md
Normal file
2
tools/test_apps/system/gdbstub_runtime/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
|
26
tools/test_apps/system/gdbstub_runtime/conftest.py
Normal file
26
tools/test_apps/system/gdbstub_runtime/conftest.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# pylint: disable=W0621 # redefined-outer-name
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from _pytest.fixtures import FixtureRequest
|
||||||
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
|
||||||
|
sys.path.append(os.path.expandvars(os.path.join('$IDF_PATH', 'tools', 'test_apps', 'system', 'panic')))
|
||||||
|
from test_panic_util import PanicTestDut # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:
|
||||||
|
mp = MonkeyPatch()
|
||||||
|
request.addfinalizer(mp.undo)
|
||||||
|
return mp
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module', autouse=True)
|
||||||
|
def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None:
|
||||||
|
monkeypatch_module.setattr('pytest_embedded_idf.IdfDut', PanicTestDut)
|
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "test_app_main.c"
|
||||||
|
INCLUDE_DIRS ""
|
||||||
|
REQUIRES esp_gdbstub)
|
26
tools/test_apps/system/gdbstub_runtime/main/test_app_main.c
Normal file
26
tools/test_apps/system/gdbstub_runtime/main/test_app_main.c
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int var_1;
|
||||||
|
int var_2;
|
||||||
|
|
||||||
|
void foo(void)
|
||||||
|
{
|
||||||
|
var_2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
printf("tested app is runnig.\n");
|
||||||
|
while(1) {
|
||||||
|
var_1++;
|
||||||
|
if (var_1 % 10 == 0) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
tools/test_apps/system/gdbstub_runtime/pytest_runtime.py
Normal file
114
tools/test_apps/system/gdbstub_runtime/pytest_runtime.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
sys.path.append(os.path.expandvars(os.path.join('$IDF_PATH', 'tools', 'test_apps', 'system', 'panic')))
|
||||||
|
from test_panic_util import PanicTestDut # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.supported_targets
|
||||||
|
@pytest.mark.temp_skip_ci(targets=['esp32c2', 'esp32h2'], reason='resolve IDF-7264')
|
||||||
|
@pytest.mark.generic
|
||||||
|
def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||||
|
dut.expect_exact('tested app is runnig.')
|
||||||
|
dut.write(b'\x03') # send Ctrl-C
|
||||||
|
dut.start_gdb()
|
||||||
|
|
||||||
|
# Test breakpoint
|
||||||
|
cmd = '-break-insert --source test_app_main.c --line 23'
|
||||||
|
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||||
|
assert response is not None
|
||||||
|
cmd = '-exec-continue'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
|
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||||
|
# does not stoped on breakpoint yet
|
||||||
|
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||||
|
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||||
|
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||||
|
assert payload['reason'] == 'breakpoint-hit'
|
||||||
|
assert payload['bkptno'] == '1'
|
||||||
|
assert payload['frame']['func'] == 'app_main'
|
||||||
|
assert payload['frame']['line'] == '23'
|
||||||
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
# Test step command
|
||||||
|
cmd = '-exec-step'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
|
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||||
|
# does not stoped on breakpoint yet
|
||||||
|
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||||
|
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||||
|
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||||
|
assert payload['reason'] == 'end-stepping-range'
|
||||||
|
assert payload['frame']['func'] == 'foo'
|
||||||
|
assert payload['frame']['line'] == '14'
|
||||||
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
# Test finish command
|
||||||
|
cmd = '-exec-finish'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
|
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||||
|
# does not stoped on breakpoint yet
|
||||||
|
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||||
|
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||||
|
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||||
|
assert payload['reason'] == 'function-finished'
|
||||||
|
assert payload['frame']['line'] == '23'
|
||||||
|
assert payload['frame']['func'] == 'app_main'
|
||||||
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
# Test next command
|
||||||
|
cmd = '-exec-next'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
|
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||||
|
# does not stoped on breakpoint yet
|
||||||
|
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||||
|
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||||
|
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||||
|
assert payload['reason'] == 'end-stepping-range'
|
||||||
|
assert payload['frame']['line'] == '21'
|
||||||
|
assert payload['frame']['func'] == 'app_main'
|
||||||
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
# test delete breakpoint
|
||||||
|
cmd = '-break-delete 1'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
cmd = '-exec-continue'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
|
assert dut.find_gdb_response('running', 'notify', responses) is not None
|
||||||
|
|
||||||
|
# test ctrl-c
|
||||||
|
responses = dut.gdbmi.send_signal_to_gdb(2)
|
||||||
|
# assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||||
|
# ?? No response? check we stopped
|
||||||
|
dut.gdb_backtrace()
|
||||||
|
|
||||||
|
# test watchpoint
|
||||||
|
cmd = '-break-watch var_2'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
cmd = '-exec-continue'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
|
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||||
|
# does not stoped on breakpoint yet
|
||||||
|
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||||
|
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||||
|
assert payload['reason'] == 'signal-received'
|
||||||
|
assert payload['frame']['func'] == 'foo'
|
||||||
|
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'
|
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME=y
|
||||||
|
CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
|
@@ -160,20 +160,21 @@ class PanicTestDut(IdfDut):
|
|||||||
Runs GDB and connects it to the "serial" port of the DUT.
|
Runs GDB and connects it to the "serial" port of the DUT.
|
||||||
After this, the DUT expect methods can no longer be used to capture output.
|
After this, the DUT expect methods can no longer be used to capture output.
|
||||||
"""
|
"""
|
||||||
|
gdb_args = ['--nx', '--quiet', '--interpreter=mi2']
|
||||||
if self.is_xtensa:
|
if self.is_xtensa:
|
||||||
gdb_path = f'xtensa-{self.target}-elf-gdb'
|
gdb_path = 'xtensa-esp-elf-gdb-no-python' # TODO: GCC-311
|
||||||
|
gdb_args = [f'--mcpu={self.target}'] + gdb_args
|
||||||
else:
|
else:
|
||||||
gdb_path = 'riscv32-esp-elf-gdb'
|
gdb_path = 'riscv32-esp-elf-gdb-no-python' # TODO: GCC-311
|
||||||
try:
|
try:
|
||||||
from pygdbmi.constants import GdbTimeoutError
|
from pygdbmi.constants import GdbTimeoutError
|
||||||
default_gdb_args = ['--nx', '--quiet', '--interpreter=mi2']
|
gdb_command = [gdb_path] + gdb_args
|
||||||
gdb_command = [gdb_path] + default_gdb_args
|
|
||||||
self.gdbmi = GdbController(command=gdb_command)
|
self.gdbmi = GdbController(command=gdb_command)
|
||||||
pygdbmi_logger = attach_logger()
|
pygdbmi_logger = attach_logger()
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# fallback for pygdbmi<0.10.0.0.
|
# fallback for pygdbmi<0.10.0.0.
|
||||||
from pygdbmi.gdbcontroller import GdbTimeoutError
|
from pygdbmi.gdbcontroller import GdbTimeoutError
|
||||||
self.gdbmi = GdbController(gdb_path=gdb_path)
|
self.gdbmi = GdbController(gdb_path=gdb_path, gdb_args=gdb_args)
|
||||||
pygdbmi_logger = self.gdbmi.logger
|
pygdbmi_logger = self.gdbmi.logger
|
||||||
|
|
||||||
# pygdbmi logs to console by default, make it log to a file instead
|
# pygdbmi logs to console by default, make it log to a file instead
|
||||||
|
Reference in New Issue
Block a user