mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
Merge branch 'feature/gdbstub_q_and_xesppie_registers_support' into 'master'
GDBStub: riscv: support f- and q-registers Closes IDF-7279 and IDF-12552 See merge request espressif/esp-idf!37484
This commit is contained in:
@@ -18,7 +18,8 @@ if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
|||||||
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")
|
"src/port/riscv/rv_decode.c"
|
||||||
|
"src/port/riscv/target_xml.c")
|
||||||
list(APPEND priv_includes "src/port/riscv/include")
|
list(APPEND priv_includes "src/port/riscv/include")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -133,6 +133,9 @@ void esp_gdbstub_send_char(char c);
|
|||||||
/** Send a string as part of the packet */
|
/** Send a string as part of the packet */
|
||||||
void esp_gdbstub_send_str(const char *s);
|
void esp_gdbstub_send_str(const char *s);
|
||||||
|
|
||||||
|
/** Send a string of limited length as part of a packet */
|
||||||
|
void esp_gdbstub_send_str_n(const char *c, size_t len);
|
||||||
|
|
||||||
/** Send a hex value as part of the packet */
|
/** Send a hex value as part of the packet */
|
||||||
void esp_gdbstub_send_hex(int val, int bits);
|
void esp_gdbstub_send_hex(int val, int bits);
|
||||||
|
|
||||||
@@ -165,4 +168,4 @@ void esp_gdbstub_trigger_cpu(void);
|
|||||||
* @param reg_index register index, depends on architecture
|
* @param reg_index register index, depends on architecture
|
||||||
* @param value 32 bit data value
|
* @param value 32 bit data value
|
||||||
*/
|
*/
|
||||||
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_ptr);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -24,6 +24,12 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if GDBSTUB_QXFER_FEATURES_ENABLED
|
||||||
|
#define GDBSTUB_QXFER_SUPPORTED_STR ";qXfer:features:read+"
|
||||||
|
#else
|
||||||
|
#define GDBSTUB_QXFER_SUPPORTED_STR ""
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
||||||
static inline int gdb_tid_to_task_index(int tid);
|
static inline int gdb_tid_to_task_index(int tid);
|
||||||
static inline int task_index_to_gdb_tid(int tid);
|
static inline int task_index_to_gdb_tid(int tid);
|
||||||
@@ -34,9 +40,7 @@ static int handle_task_commands(unsigned char *cmd, int len);
|
|||||||
static void esp_gdbstub_send_str_as_hex(const char *str);
|
static void esp_gdbstub_send_str_as_hex(const char *str);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
|
||||||
static void handle_qSupported_command(const unsigned char *cmd, int len);
|
static void handle_qSupported_command(const unsigned char *cmd, int len);
|
||||||
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
|
||||||
|
|
||||||
#if (CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME || CONFIG_ESP_GDBSTUB_SUPPORT_TASKS)
|
#if (CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME || CONFIG_ESP_GDBSTUB_SUPPORT_TASKS)
|
||||||
static bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len);
|
static bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len);
|
||||||
@@ -193,7 +197,9 @@ int getActiveTaskNum(void);
|
|||||||
int __swrite(struct _reent *, void *, const char *, int);
|
int __swrite(struct _reent *, void *, const char *, int);
|
||||||
int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len);
|
int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len);
|
||||||
|
|
||||||
volatile esp_gdbstub_frame_t *temp_regs_frame;
|
volatile esp_gdbstub_frame_t *selected_task_frame; /* related to task that has been chosen via GDB */
|
||||||
|
volatile esp_gdbstub_frame_t *running_task_frame; /* related to task that was interrupted. GDBStub implements all-stop mode,
|
||||||
|
and this frame is needed to continue executing the task that was interrupted. */
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
static int bp_count = 0;
|
static int bp_count = 0;
|
||||||
@@ -220,7 +226,7 @@ static bool gdb_debug_int = false;
|
|||||||
*/
|
*/
|
||||||
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;
|
running_task_frame = selected_task_frame = regs_frame;
|
||||||
not_send_reason = step_in_progress;
|
not_send_reason = step_in_progress;
|
||||||
if (step_in_progress == true) {
|
if (step_in_progress == true) {
|
||||||
esp_gdbstub_send_str_packet("S05");
|
esp_gdbstub_send_str_packet("S05");
|
||||||
@@ -297,7 +303,7 @@ void gdbstub_handle_debug_int(esp_gdbstub_frame_t *regs_frame)
|
|||||||
{
|
{
|
||||||
bp_count = 0;
|
bp_count = 0;
|
||||||
wp_count = 0;
|
wp_count = 0;
|
||||||
temp_regs_frame = regs_frame;
|
running_task_frame = selected_task_frame = regs_frame;
|
||||||
gdb_debug_int = true;
|
gdb_debug_int = true;
|
||||||
not_send_reason = step_in_progress;
|
not_send_reason = step_in_progress;
|
||||||
if (step_in_progress == true) {
|
if (step_in_progress == true) {
|
||||||
@@ -367,6 +373,9 @@ void gdbstub_handle_debug_int(esp_gdbstub_frame_t *regs_frame)
|
|||||||
* */
|
* */
|
||||||
void esp_gdbstub_init(void)
|
void esp_gdbstub_init(void)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
||||||
|
s_scratch.paniced_task_index = GDBSTUB_CUR_TASK_INDEX_UNKNOWN;
|
||||||
|
#endif
|
||||||
esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, esp_gdbstub_int, NULL, NULL);
|
esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, esp_gdbstub_int, NULL, NULL);
|
||||||
esp_gdbstub_init_dports();
|
esp_gdbstub_init_dports();
|
||||||
}
|
}
|
||||||
@@ -654,7 +663,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_frame_t *)temp_regs_frame);
|
esp_gdbstub_do_step((esp_gdbstub_frame_t *)running_task_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Step ... */
|
/** Step ... */
|
||||||
@@ -667,45 +676,87 @@ static void handle_C_command(const unsigned char *cmd, int len)
|
|||||||
/* Set Register ... */
|
/* Set Register ... */
|
||||||
static void handle_P_command(const unsigned char *cmd, int len)
|
static void handle_P_command(const unsigned char *cmd, int len)
|
||||||
{
|
{
|
||||||
uint32_t reg_index = 0;
|
uint32_t reg_index = esp_gdbstub_gethex(&cmd, -1);
|
||||||
if (cmd[1] == '=') {
|
if (*cmd != '=') {
|
||||||
reg_index = esp_gdbstub_gethex(&cmd, 4);
|
esp_gdbstub_send_str_packet("E.unexpected P packet format");
|
||||||
cmd++;
|
|
||||||
} else if (cmd[2] == '=') {
|
|
||||||
reg_index = esp_gdbstub_gethex(&cmd, 8);
|
|
||||||
cmd++;
|
|
||||||
cmd++;
|
|
||||||
} else {
|
|
||||||
esp_gdbstub_send_str_packet("E02");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
|
cmd++; /* skip '=' */
|
||||||
/* The address comes with inverted byte order.*/
|
|
||||||
uint8_t *addr_ptr = (uint8_t *)&addr;
|
|
||||||
uint32_t p_address = 0;
|
|
||||||
uint8_t *p_addr_ptr = (uint8_t *)&p_address;
|
|
||||||
p_addr_ptr[3] = addr_ptr[0];
|
|
||||||
p_addr_ptr[2] = addr_ptr[1];
|
|
||||||
p_addr_ptr[1] = addr_ptr[2];
|
|
||||||
p_addr_ptr[0] = addr_ptr[3];
|
|
||||||
|
|
||||||
esp_gdbstub_set_register((esp_gdbstub_frame_t *)temp_regs_frame, reg_index, p_address);
|
/* In general, we operate with 32-bit sized values here.
|
||||||
|
* However, some registers may be larger. For example, q registers are 128-bit sized. */
|
||||||
|
#if GDBSTUB_MAX_REGISTER_SIZE > 4
|
||||||
|
uint8_t value[GDBSTUB_MAX_REGISTER_SIZE * sizeof(uint32_t)] = {0};
|
||||||
|
uint32_t *value_ptr = (uint32_t *)value;
|
||||||
|
for(int i = 0; i < sizeof(value); i++) {
|
||||||
|
value[i] = (uint8_t) esp_gdbstub_gethex(&cmd, 8);
|
||||||
|
if (*cmd == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
uint32_t value;
|
||||||
|
uint32_t *value_ptr = &value;
|
||||||
|
value = gdbstub_hton(esp_gdbstub_gethex(&cmd, -1));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (*cmd != 0) {
|
||||||
|
esp_gdbstub_send_str_packet("E.unexpected register size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_gdbstub_set_register((esp_gdbstub_frame_t *)selected_task_frame, reg_index, value_ptr);
|
||||||
/* Convert current register file to GDB*/
|
/* Convert current register file to GDB*/
|
||||||
esp_gdbstub_frame_to_regfile((esp_gdbstub_frame_t *)temp_regs_frame, gdb_local_regfile);
|
esp_gdbstub_frame_to_regfile((esp_gdbstub_frame_t *)selected_task_frame, gdb_local_regfile);
|
||||||
/* Sen OK response*/
|
/* Sen OK response*/
|
||||||
esp_gdbstub_send_str_packet("OK");
|
esp_gdbstub_send_str_packet("OK");
|
||||||
}
|
}
|
||||||
|
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
|
||||||
/** qSupported requests the communication with GUI
|
/** qSupported requests the communication with GUI
|
||||||
*/
|
*/
|
||||||
static void handle_qSupported_command(const unsigned char *cmd, int len)
|
static void handle_qSupported_command(const unsigned char *cmd, int len)
|
||||||
{
|
{
|
||||||
esp_gdbstub_send_start();
|
esp_gdbstub_send_start();
|
||||||
esp_gdbstub_send_str("qSupported:multiprocess+;swbreak-;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;no-resumed+");
|
#if CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
|
esp_gdbstub_send_str("qSupported:multiprocess+;swbreak-;hwbreak+;fork-events+;vfork-events+;exec-events+;vContSupported+;no-resumed+" GDBSTUB_QXFER_SUPPORTED_STR);
|
||||||
|
#else
|
||||||
|
esp_gdbstub_send_str("qSupported:multiprocess+" GDBSTUB_QXFER_SUPPORTED_STR);
|
||||||
|
#endif
|
||||||
esp_gdbstub_send_end();
|
esp_gdbstub_send_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
#if GDBSTUB_QXFER_FEATURES_ENABLED
|
||||||
|
static void qXfer_data(const char *ptr, uint32_t size, uint32_t offset, uint32_t length)
|
||||||
|
{
|
||||||
|
if (offset >= size) {
|
||||||
|
/* No data to send. */
|
||||||
|
esp_gdbstub_send_str_packet("l");
|
||||||
|
} else {
|
||||||
|
size_t len = MIN(length, size - offset);
|
||||||
|
esp_gdbstub_send_start();
|
||||||
|
esp_gdbstub_send_char('m');
|
||||||
|
esp_gdbstub_send_str_n(ptr + offset, len);
|
||||||
|
esp_gdbstub_send_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_qXfer_command(const unsigned char *cmd, int len)
|
||||||
|
{
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
const char *target_feature_str = "qXfer:features:read:target.xml:";
|
||||||
|
const int target_feature_str_len = strlen(target_feature_str);
|
||||||
|
if (!command_name_matches(target_feature_str, cmd, target_feature_str_len)) {
|
||||||
|
/* Send empty packet for not supported requests. */
|
||||||
|
esp_gdbstub_send_str_packet(NULL);
|
||||||
|
}
|
||||||
|
cmd += target_feature_str_len;
|
||||||
|
offset = esp_gdbstub_gethex(&cmd, -1);
|
||||||
|
cmd++; /* skip ',' */
|
||||||
|
length = esp_gdbstub_gethex(&cmd, -1);
|
||||||
|
qXfer_data(target_xml, strlen(target_xml), offset, length);
|
||||||
|
}
|
||||||
|
#endif // GDBSTUB_QXFER_FEATURES_ENABLED
|
||||||
|
|
||||||
/** Handle a command received from gdb */
|
/** Handle a command received from gdb */
|
||||||
int esp_gdbstub_handle_command(unsigned char *cmd, int len)
|
int esp_gdbstub_handle_command(unsigned char *cmd, int len)
|
||||||
@@ -780,9 +831,13 @@ int esp_gdbstub_handle_command(unsigned char *cmd, int len)
|
|||||||
handle_P_command(data, len - 1);
|
handle_P_command(data, len - 1);
|
||||||
} else if (cmd[0] == 'c') { //continue execution
|
} else if (cmd[0] == 'c') { //continue execution
|
||||||
return GDBSTUB_ST_CONT;
|
return GDBSTUB_ST_CONT;
|
||||||
|
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||||
} else if (command_name_matches("qSupported", cmd, 10)) {
|
} else if (command_name_matches("qSupported", cmd, 10)) {
|
||||||
handle_qSupported_command(cmd, len);
|
handle_qSupported_command(cmd, len);
|
||||||
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
#if GDBSTUB_QXFER_FEATURES_ENABLED
|
||||||
|
} else if (command_name_matches("qXfer", cmd, 5)) {
|
||||||
|
handle_qXfer_command(cmd, len);
|
||||||
|
#endif // GDBSTUB_QXFER_FEATURES_ENABLED
|
||||||
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
||||||
} else if (s_scratch.state != GDBSTUB_TASK_SUPPORT_DISABLED) {
|
} else if (s_scratch.state != GDBSTUB_TASK_SUPPORT_DISABLED) {
|
||||||
return handle_task_commands(cmd, len);
|
return handle_task_commands(cmd, len);
|
||||||
@@ -964,12 +1019,13 @@ static void set_active_task(size_t index)
|
|||||||
esp_gdbstub_frame_to_regfile(&s_scratch.paniced_frame, &s_scratch.regfile);
|
esp_gdbstub_frame_to_regfile(&s_scratch.paniced_frame, &s_scratch.regfile);
|
||||||
} else {
|
} else {
|
||||||
/* Get the registers from TCB.
|
/* Get the registers from TCB.
|
||||||
* FIXME: for the task currently running on the other CPU, extracting the registers from TCB
|
* TODO: IDF-12550. For the task currently running on the other CPU, extracting the registers from TCB
|
||||||
* isn't valid. Need to use some IPC mechanism to obtain the registers of the other CPU.
|
* isn't valid. Need to use some IPC mechanism to obtain the registers of the other CPU.
|
||||||
*/
|
*/
|
||||||
TaskHandle_t handle = NULL;
|
TaskHandle_t handle = NULL;
|
||||||
get_task_handle(index, &handle);
|
get_task_handle(index, &handle);
|
||||||
if (handle != NULL) {
|
if (handle != NULL) {
|
||||||
|
selected_task_frame = ((StaticTask_t *)handle)->pxDummy1 /* pxTopOfStack */;
|
||||||
esp_gdbstub_tcb_to_regfile(handle, &s_scratch.regfile);
|
esp_gdbstub_tcb_to_regfile(handle, &s_scratch.regfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -33,6 +33,16 @@ void esp_gdbstub_send_char(char c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send a string of limited length as part of a packet
|
||||||
|
void esp_gdbstub_send_str_n(const char *c, size_t len)
|
||||||
|
{
|
||||||
|
while (*c != 0 && len != 0) {
|
||||||
|
esp_gdbstub_send_char(*c);
|
||||||
|
c++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send a string as part of a packet
|
// Send a string as part of a packet
|
||||||
void esp_gdbstub_send_str(const char *c)
|
void esp_gdbstub_send_str(const char *c)
|
||||||
{
|
{
|
||||||
@@ -84,7 +94,7 @@ uint32_t esp_gdbstub_gethex(const unsigned char **ptr, int bits)
|
|||||||
char c;
|
char c;
|
||||||
no = bits / 4;
|
no = bits / 4;
|
||||||
if (bits == -1) {
|
if (bits == -1) {
|
||||||
no = 64;
|
no = 32 / 4;
|
||||||
}
|
}
|
||||||
for (i = 0; i < no; i++) {
|
for (i = 0; i < no; i++) {
|
||||||
c = **ptr;
|
c = **ptr;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -7,12 +7,18 @@
|
|||||||
#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 "soc/soc_caps.h"
|
||||||
#include "esp_cpu.h"
|
#include "esp_cpu.h"
|
||||||
#include "esp_ipc_isr.h"
|
#include "esp_ipc_isr.h"
|
||||||
#include "rv_decode.h"
|
#include "rv_decode.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_private/crosscore_int.h"
|
#include "esp_private/crosscore_int.h"
|
||||||
#include "esp_private/freertos_debug.h"
|
#include "esp_private/freertos_debug.h"
|
||||||
|
#include "freertos/portmacro.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/FreeRTOSConfig.h"
|
||||||
|
|
||||||
|
#define GDBSTUB_CSA_NOT_INITIALIZED ((void *) 0xFFFFFFFF)
|
||||||
|
|
||||||
extern volatile esp_gdbstub_frame_t *temp_regs_frame;
|
extern volatile esp_gdbstub_frame_t *temp_regs_frame;
|
||||||
|
|
||||||
@@ -21,6 +27,183 @@ static inline void init_regfile(esp_gdbstub_gdb_regfile_t *dst)
|
|||||||
memset(dst, 0, sizeof(*dst));
|
memset(dst, 0, sizeof(*dst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
static void esp_gdbstub_fpu_regs_to_regfile (esp_gdbstub_gdb_regfile_t *dst)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: The GDB stub logic executes within an ISR, where coprocessors are disabled.
|
||||||
|
* Therefore, we must enable the coprocessor before reading or writing its registers.
|
||||||
|
*/
|
||||||
|
rv_utils_enable_fpu();
|
||||||
|
__asm__ volatile ("fsw ft0, %0" : "=m" (dst->f.ft0));
|
||||||
|
__asm__ volatile ("fsw ft1, %0" : "=m" (dst->f.ft1));
|
||||||
|
__asm__ volatile ("fsw ft2, %0" : "=m" (dst->f.ft2));
|
||||||
|
__asm__ volatile ("fsw ft3, %0" : "=m" (dst->f.ft3));
|
||||||
|
__asm__ volatile ("fsw ft4, %0" : "=m" (dst->f.ft4));
|
||||||
|
__asm__ volatile ("fsw ft5, %0" : "=m" (dst->f.ft5));
|
||||||
|
__asm__ volatile ("fsw ft6, %0" : "=m" (dst->f.ft6));
|
||||||
|
__asm__ volatile ("fsw ft7, %0" : "=m" (dst->f.ft7));
|
||||||
|
__asm__ volatile ("fsw fs0, %0" : "=m" (dst->f.fs0));
|
||||||
|
__asm__ volatile ("fsw fs1, %0" : "=m" (dst->f.fs1));
|
||||||
|
__asm__ volatile ("fsw fa0, %0" : "=m" (dst->f.fa0));
|
||||||
|
__asm__ volatile ("fsw fa1, %0" : "=m" (dst->f.fa1));
|
||||||
|
__asm__ volatile ("fsw fa2, %0" : "=m" (dst->f.fa2));
|
||||||
|
__asm__ volatile ("fsw fa3, %0" : "=m" (dst->f.fa3));
|
||||||
|
__asm__ volatile ("fsw fa4, %0" : "=m" (dst->f.fa4));
|
||||||
|
__asm__ volatile ("fsw fa5, %0" : "=m" (dst->f.fa5));
|
||||||
|
__asm__ volatile ("fsw fa6, %0" : "=m" (dst->f.fa6));
|
||||||
|
__asm__ volatile ("fsw fa7, %0" : "=m" (dst->f.fa7));
|
||||||
|
__asm__ volatile ("fsw fs2, %0" : "=m" (dst->f.fs2));
|
||||||
|
__asm__ volatile ("fsw fs3, %0" : "=m" (dst->f.fs3));
|
||||||
|
__asm__ volatile ("fsw fs4, %0" : "=m" (dst->f.fs4));
|
||||||
|
__asm__ volatile ("fsw fs5, %0" : "=m" (dst->f.fs5));
|
||||||
|
__asm__ volatile ("fsw fs6, %0" : "=m" (dst->f.fs6));
|
||||||
|
__asm__ volatile ("fsw fs7, %0" : "=m" (dst->f.fs7));
|
||||||
|
__asm__ volatile ("fsw fs8, %0" : "=m" (dst->f.fs8));
|
||||||
|
__asm__ volatile ("fsw fs9, %0" : "=m" (dst->f.fs9));
|
||||||
|
__asm__ volatile ("fsw fs10, %0" : "=m" (dst->f.fs10));
|
||||||
|
__asm__ volatile ("fsw fs11, %0" : "=m" (dst->f.fs11));
|
||||||
|
__asm__ volatile ("fsw ft8, %0" : "=m" (dst->f.ft8));
|
||||||
|
__asm__ volatile ("fsw ft9, %0" : "=m" (dst->f.ft9));
|
||||||
|
__asm__ volatile ("fsw ft10, %0" : "=m" (dst->f.ft10));
|
||||||
|
__asm__ volatile ("fsw ft11, %0" : "=m" (dst->f.ft11));
|
||||||
|
__asm__ volatile ("csrr %0, fcsr" : "=r" (dst->f.fcsr));
|
||||||
|
rv_utils_disable_fpu();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
static void esp_gdbstub_pie_regs_to_regfile (esp_gdbstub_gdb_regfile_t *dst)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: The GDB stub logic executes within an ISR, where coprocessors are disabled.
|
||||||
|
* Therefore, we must enable the coprocessor before reading or writing its registers.
|
||||||
|
*/
|
||||||
|
rv_utils_enable_pie();
|
||||||
|
register void* ptr asm("a5") = &dst->pie;
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q0, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q1, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q2, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q3, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q4, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q5, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q6, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q7, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.l.l.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.l.h.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.h.l.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.h.h.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.ua.state.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.s.xacc.ip %0, 0" :: "r" (ptr));
|
||||||
|
rv_utils_disable_pie();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE
|
||||||
|
const StaticTask_t *esp_gdbstub_find_tcb_by_frame(const esp_gdbstub_frame_t *frame)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Determine which task owns the current frame.
|
||||||
|
* Perform a search across all tasks, as GDBstub may not include task information
|
||||||
|
* if configured with ESP_GDBSTUB_SUPPORT_TASKS disabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TaskIterator_t xTaskIter = {0}; /* Point to the first task list */
|
||||||
|
|
||||||
|
while(xTaskGetNext(&xTaskIter) != -1)
|
||||||
|
{
|
||||||
|
StaticTask_t *tcb = (StaticTask_t *)xTaskIter.pxTaskHandle;
|
||||||
|
if (tcb->pxDummy1 /* pxTopOfStack */ == frame) {
|
||||||
|
return tcb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL; /* Task not found. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *esp_gdbstub_coproc_saved_area(const StaticTask_t *tcb, int coproc, bool is_read) {
|
||||||
|
/*
|
||||||
|
* Coprocessors have lazy register saving mechanism:
|
||||||
|
* 1. Defer saving coprocessor registers until a task actually uses a coprocessor instruction.
|
||||||
|
* This triggers an exception when the instruction is executed.
|
||||||
|
* 2. In the exception handler:
|
||||||
|
* - Enable the coprocessor and designate the task as the new coprocessor owner.
|
||||||
|
* - If another task previously owned the coprocessor, save its registers.
|
||||||
|
* - Restore the coprocessor registers for the new owner before execution resumes.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* To determine whether to read/write coprocessor registers directly or use stack memory:
|
||||||
|
* - Check if the current task is the coprocessor owner:
|
||||||
|
* - Yes: Perform direct read/write operations.
|
||||||
|
* - No: Read/write from the stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
RvCoprocSaveArea* pxPortGetCoprocArea(StaticTask_t* task, bool allocate, int coproc);
|
||||||
|
|
||||||
|
RvCoprocSaveArea* csa;
|
||||||
|
extern StaticTask_t* port_uxCoprocOwner[portNUM_PROCESSORS][SOC_CPU_COPROC_NUM];
|
||||||
|
uint32_t core = rv_utils_get_core_id();
|
||||||
|
uint32_t coproc_bit;
|
||||||
|
if (port_uxCoprocOwner[core][coproc] == tcb) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
csa = pxPortGetCoprocArea((StaticTask_t*)tcb, false, coproc);
|
||||||
|
if (csa->sa_coprocs[coproc]) {
|
||||||
|
return csa->sa_coprocs[coproc];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_read) {
|
||||||
|
/* Don't allocate csa for coprocessor for read. (Just return zeroed registers) */
|
||||||
|
return GDBSTUB_CSA_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore 'csa->sa_enable' flag to read/write from the frame if the coprocessor has not been used by this task yet.
|
||||||
|
* This ensures that the registers are correctly restored when the task's coprocessor context is switched.
|
||||||
|
* That's why true is passed to the allocate parameter of pxPortGetCoprocArea.
|
||||||
|
*/
|
||||||
|
/* TODO: IDF-12550. Provide correct read/write access for coprocessor owned by another CPU.
|
||||||
|
* Accessing registers in stack-frame is not correct in this case.
|
||||||
|
*/
|
||||||
|
csa = pxPortGetCoprocArea((StaticTask_t*)tcb, true, coproc);
|
||||||
|
coproc_bit = 1 << coproc;
|
||||||
|
if (!(csa->sa_enable & coproc_bit)) {
|
||||||
|
uint32_t sa_coproc_size = coproc == 0 ? RV_COPROC0_SIZE : (coproc == 1 ? RV_COPROC1_SIZE : RV_COPROC2_SIZE);
|
||||||
|
/* coproc registers were not saved for this task yet. Initialize with zeroes. */
|
||||||
|
memset(csa->sa_coprocs[coproc], 0, sa_coproc_size);
|
||||||
|
csa->sa_enable |= coproc_bit;
|
||||||
|
}
|
||||||
|
return csa->sa_coprocs[coproc];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_gdbstub_coproc_regs_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_gdb_regfile_t *dst)
|
||||||
|
{
|
||||||
|
const StaticTask_t *tcb = esp_gdbstub_find_tcb_by_frame(frame);
|
||||||
|
void *csa;
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
csa = esp_gdbstub_coproc_saved_area(tcb, FPU_COPROC_IDX, true);
|
||||||
|
if (csa == NULL) {
|
||||||
|
esp_gdbstub_fpu_regs_to_regfile(dst);
|
||||||
|
} else if (csa == GDBSTUB_CSA_NOT_INITIALIZED) {
|
||||||
|
memset(&dst->f, 0, sizeof(dst->f));
|
||||||
|
} else {
|
||||||
|
memcpy(&dst->f, csa, sizeof(dst->f));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
csa = esp_gdbstub_coproc_saved_area(tcb, PIE_COPROC_IDX, true);
|
||||||
|
if (csa == NULL) {
|
||||||
|
esp_gdbstub_pie_regs_to_regfile(dst);
|
||||||
|
} else if (csa == GDBSTUB_CSA_NOT_INITIALIZED) {
|
||||||
|
memset(&dst->pie, 0, sizeof(dst->pie));
|
||||||
|
} else {
|
||||||
|
memcpy(&dst->pie, csa, sizeof(dst->pie));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE */
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
init_regfile(dst);
|
init_regfile(dst);
|
||||||
@@ -29,28 +212,21 @@ void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_
|
|||||||
// We omit register x0 here since it's the zero register and always hard-wired to 0.
|
// We omit register x0 here since it's the zero register and always hard-wired to 0.
|
||||||
// See The RISC-V Instruction Set Manual Volume I: Unprivileged ISA Document Version 20191213 for more details.
|
// See The RISC-V Instruction Set Manual Volume I: Unprivileged ISA Document Version 20191213 for more details.
|
||||||
memcpy(&(dst->x[1]), &frame->ra, sizeof(uint32_t) * 31);
|
memcpy(&(dst->x[1]), &frame->ra, sizeof(uint32_t) * 31);
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE
|
||||||
|
esp_gdbstub_coproc_regs_to_regfile(frame, dst);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS || CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
|
||||||
|
|
||||||
/* Represents FreeRTOS TCB structure */
|
|
||||||
typedef struct {
|
|
||||||
uint8_t *top_of_stack;
|
|
||||||
/* Other members aren't needed */
|
|
||||||
} dummy_tcb_t;
|
|
||||||
|
|
||||||
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
|
#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)
|
||||||
{
|
{
|
||||||
const dummy_tcb_t *dummy_tcb = (const dummy_tcb_t *) tcb;
|
const RvExcFrame *frame = (RvExcFrame *) ((StaticTask_t *) tcb)->pxDummy1 /* top_of_stack */;
|
||||||
|
|
||||||
const RvExcFrame *frame = (RvExcFrame *) dummy_tcb->top_of_stack;
|
|
||||||
esp_gdbstub_frame_to_regfile(frame, dst);
|
esp_gdbstub_frame_to_regfile(frame, 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)
|
||||||
{
|
{
|
||||||
@@ -87,8 +263,8 @@ void esp_gdbstub_int(__attribute__((unused)) void *frame)
|
|||||||
/* Pointer to saved frame is in pxCurrentTCB
|
/* Pointer to saved frame is in pxCurrentTCB
|
||||||
* See rtos_int_enter function
|
* See rtos_int_enter function
|
||||||
*/
|
*/
|
||||||
dummy_tcb_t *tcb = (dummy_tcb_t *)pvTaskGetCurrentTCBForCore(esp_cpu_get_core_id());
|
StaticTask_t *tcb = (StaticTask_t *)pvTaskGetCurrentTCBForCore(esp_cpu_get_core_id());
|
||||||
gdbstub_handle_uart_int((esp_gdbstub_frame_t *)tcb->top_of_stack);
|
gdbstub_handle_uart_int((esp_gdbstub_frame_t *)tcb->pxDummy1 /* top_of_stack */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void esp_gdbstub_init_dports(void)
|
void esp_gdbstub_init_dports(void)
|
||||||
@@ -152,8 +328,168 @@ void esp_gdbstub_trigger_cpu(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t value)
|
#if SOC_CPU_HAS_FPU
|
||||||
|
void esp_gdbstub_set_fpu_register(uint32_t reg_index, uint32_t value)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: The GDB stub logic executes within an ISR, where coprocessors are disabled.
|
||||||
|
* Therefore, we must enable the coprocessor before reading or writing its registers.
|
||||||
|
*/
|
||||||
|
rv_utils_enable_fpu();
|
||||||
|
if (reg_index == 0) {
|
||||||
|
__asm__ volatile ("flw ft0, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 1) {
|
||||||
|
__asm__ volatile ("flw ft1, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 2) {
|
||||||
|
__asm__ volatile ("flw ft2, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 3) {
|
||||||
|
__asm__ volatile ("flw ft3, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 4) {
|
||||||
|
__asm__ volatile ("flw ft4, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 5) {
|
||||||
|
__asm__ volatile ("flw ft5, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 6) {
|
||||||
|
__asm__ volatile ("flw ft6, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 7) {
|
||||||
|
__asm__ volatile ("flw ft7, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 8) {
|
||||||
|
__asm__ volatile ("flw fs0, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 9) {
|
||||||
|
__asm__ volatile ("flw fs1, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 10) {
|
||||||
|
__asm__ volatile ("flw fa0, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 11) {
|
||||||
|
__asm__ volatile ("flw fa1, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 12) {
|
||||||
|
__asm__ volatile ("flw fa2, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 13) {
|
||||||
|
__asm__ volatile ("flw fa3, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 14) {
|
||||||
|
__asm__ volatile ("flw fa4, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 15) {
|
||||||
|
__asm__ volatile ("flw fa5, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 16) {
|
||||||
|
__asm__ volatile ("flw fa6, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 17) {
|
||||||
|
__asm__ volatile ("flw fa7, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 18) {
|
||||||
|
__asm__ volatile ("flw fs2, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 19) {
|
||||||
|
__asm__ volatile ("flw fs3, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 20) {
|
||||||
|
__asm__ volatile ("flw fs4, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 21) {
|
||||||
|
__asm__ volatile ("flw fs5, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 22) {
|
||||||
|
__asm__ volatile ("flw fs6, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 23) {
|
||||||
|
__asm__ volatile ("flw fs7, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 24) {
|
||||||
|
__asm__ volatile ("flw fs8, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 25) {
|
||||||
|
__asm__ volatile ("flw fs9, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 26) {
|
||||||
|
__asm__ volatile ("flw fs10, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 27) {
|
||||||
|
__asm__ volatile ("flw fs11, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 28) {
|
||||||
|
__asm__ volatile ("flw ft8, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 29) {
|
||||||
|
__asm__ volatile ("flw ft9, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 30) {
|
||||||
|
__asm__ volatile ("flw ft10, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 31) {
|
||||||
|
__asm__ volatile ("flw ft11, %0" : "=m" (value));
|
||||||
|
} else if (reg_index == 32) {
|
||||||
|
__asm__ volatile ("csrw fcsr, %0" : : "r" (value));
|
||||||
|
}
|
||||||
|
rv_utils_disable_fpu();
|
||||||
|
}
|
||||||
|
#endif /* SOC_CPU_HAS_FPU */
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
void esp_gdbstub_set_pie_register(uint32_t reg_index, uint32_t *value_ptr)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: The GDB stub logic executes within an ISR, where coprocessors are disabled.
|
||||||
|
* Therefore, we must enable the coprocessor before reading or writing its registers.
|
||||||
|
*/
|
||||||
|
register void *ptr asm("a5") = value_ptr;
|
||||||
|
rv_utils_enable_pie();
|
||||||
|
if (reg_index == 0) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q0, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 1) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q1, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 2) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q2, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 3) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q3, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 4) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q4, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 5) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q5, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 6) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q6, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 7) {
|
||||||
|
__asm__ volatile ("esp.vld.128.ip q7, %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 8) {
|
||||||
|
__asm__ volatile ("esp.ld.qacc.l.l.128.ip %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 9) {
|
||||||
|
__asm__ volatile ("esp.ld.qacc.l.h.128.ip %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 10) {
|
||||||
|
__asm__ volatile ("esp.ld.qacc.h.l.128.ip %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 11) {
|
||||||
|
__asm__ volatile ("esp.ld.qacc.h.h.128.ip %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 12) {
|
||||||
|
__asm__ volatile ("esp.ld.ua.state.ip %0, 0" :: "r" (ptr));
|
||||||
|
} else if (reg_index == 13) {
|
||||||
|
__asm__ volatile ("esp.ld.xacc.ip %0, 0" :: "r" (ptr));
|
||||||
|
}
|
||||||
|
rv_utils_disable_pie();
|
||||||
|
}
|
||||||
|
#endif /* SOC_CPU_HAS_PIE */
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE
|
||||||
|
void esp_gdbstub_set_coproc_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t *value_ptr) {
|
||||||
|
const StaticTask_t *tcb = esp_gdbstub_find_tcb_by_frame(frame);
|
||||||
|
uint32_t *csa; /* points to coprocessor registers data. */
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
if (reg_index >= 33 && reg_index <= 68) {
|
||||||
|
reg_index -= 33;
|
||||||
|
if (reg_index > 32 /* fcsr */) {
|
||||||
|
reg_index = 32;
|
||||||
|
}
|
||||||
|
csa = esp_gdbstub_coproc_saved_area(tcb, FPU_COPROC_IDX, false);
|
||||||
|
if (csa == NULL) {
|
||||||
|
esp_gdbstub_set_fpu_register(reg_index, *value_ptr);
|
||||||
|
} else {
|
||||||
|
csa[reg_index] = *value_ptr;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
if (reg_index >= 4211 && reg_index <= 4224) {
|
||||||
|
reg_index -= 4211;
|
||||||
|
csa = esp_gdbstub_coproc_saved_area(tcb, PIE_COPROC_IDX, false);
|
||||||
|
if (csa == NULL) {
|
||||||
|
esp_gdbstub_set_pie_register(reg_index, value_ptr);
|
||||||
|
} else {
|
||||||
|
if (reg_index < 13) { /* 128-bit registers: q, qacc, ua_state */
|
||||||
|
memcpy(&csa[reg_index * 4], value_ptr, sizeof(uint32_t) * 4);
|
||||||
|
} else { /* 40-bit regiseter: xacc */
|
||||||
|
memcpy(&csa[4 * 13], value_ptr, sizeof(uint8_t) * 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE */
|
||||||
|
|
||||||
|
void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t *value_ptr)
|
||||||
|
{
|
||||||
|
uint32_t value = *value_ptr;
|
||||||
|
|
||||||
/* RISC-V base ISA has registers x0-x31 */
|
/* RISC-V base ISA has registers x0-x31 */
|
||||||
if (reg_index == 0) { /* skip zero-wired register */
|
if (reg_index == 0) { /* skip zero-wired register */
|
||||||
return;
|
return;
|
||||||
@@ -162,4 +498,7 @@ void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, ui
|
|||||||
} else if (reg_index == 32) { /* register 32 is PC */
|
} else if (reg_index == 32) { /* register 32 is PC */
|
||||||
frame->mepc = value;
|
frame->mepc = value;
|
||||||
}
|
}
|
||||||
|
#if SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE
|
||||||
|
esp_gdbstub_set_coproc_register(frame, reg_index, value_ptr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
#include "riscv/rvruntime-frames.h"
|
#include "riscv/rvruntime-frames.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE
|
||||||
|
#define GDBSTUB_QXFER_FEATURES_ENABLED 1
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
#define GDBSTUB_MAX_REGISTER_SIZE 16
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -14,12 +24,31 @@ extern "C" {
|
|||||||
|
|
||||||
typedef RvExcFrame esp_gdbstub_frame_t;
|
typedef RvExcFrame esp_gdbstub_frame_t;
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
static_assert(sizeof(RvFPUSaveArea) == (32 + 1) * sizeof(uint32_t),
|
||||||
|
"Expected 32 float registers + fcsr. Please update gdbstub internals.");
|
||||||
|
#endif
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
static_assert(sizeof(RvPIESaveArea) == ((8 + 4 + 1) * (4 * sizeof(uint32_t))) + (2 * sizeof(uint32_t)),
|
||||||
|
"Expected 8 Q regs, QACC, UA_STATE, XACC. Please update gdbstub internals.");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* GDB regfile structure, configuration dependent */
|
/* GDB regfile structure, configuration dependent */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t x[32];
|
uint32_t x[32];
|
||||||
uint32_t pc;
|
uint32_t pc;
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
RvFPUSaveArea f;
|
||||||
|
#endif
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
RvPIESaveArea pie;
|
||||||
|
#endif
|
||||||
} esp_gdbstub_gdb_regfile_t;
|
} esp_gdbstub_gdb_regfile_t;
|
||||||
|
|
||||||
|
#if GDBSTUB_QXFER_FEATURES_ENABLED
|
||||||
|
extern const char target_xml[];
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rv_decode.h"
|
#include "rv_decode.h"
|
||||||
|
#include "riscv/csr.h"
|
||||||
#include "riscv/csr_hwlp.h"
|
#include "riscv/csr_hwlp.h"
|
||||||
#include "soc/soc_caps.h"
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
116
components/esp_gdbstub/src/port/riscv/target_xml.c
Normal file
116
components/esp_gdbstub/src/port/riscv/target_xml.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE
|
||||||
|
const char target_xml[] =
|
||||||
|
"<?xml version=\"1.0\"?>"
|
||||||
|
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
||||||
|
"<target>"
|
||||||
|
"<architecture>riscv</architecture>"
|
||||||
|
"<feature name=\"org.gnu.gdb.riscv.cpu\">"
|
||||||
|
"<reg name=\"zero\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"ra\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
|
"<reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
|
"<reg name=\"gp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
|
"<reg name=\"tp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
|
"<reg name=\"t0\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"t1\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"t2\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"fp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
|
"<reg name=\"s1\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a0\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a1\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a2\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a3\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a4\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a5\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a6\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"a7\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s2\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s3\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s4\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s5\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s6\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s7\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s8\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s9\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s10\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"s11\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"t3\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"t4\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"t5\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"t6\" bitsize=\"32\"/>"
|
||||||
|
"<reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
|
"</feature>"
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
"<feature name=\"org.gnu.gdb.riscv.fpu\">"
|
||||||
|
"<reg name=\"ft0\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft1\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft2\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft3\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft4\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft5\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft6\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft7\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs0\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs1\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa0\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa1\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa2\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa3\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa4\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa5\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa6\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fa7\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs2\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs3\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs4\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs5\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs6\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs7\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs8\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs9\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs10\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fs11\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft8\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft9\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft10\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"ft11\" bitsize=\"32\" type=\"float\"/>"
|
||||||
|
"<reg name=\"fcsr\" bitsize=\"32\" regnum=\"68\"/>"
|
||||||
|
"</feature>"
|
||||||
|
#endif /* SOC_CPU_HAS_FPU */
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
"<feature name=\"org.gnu.gdb.riscv.xesppie\">"
|
||||||
|
"<vector id=\"v5i8\" type=\"int8\" count=\"5\"/>"
|
||||||
|
"<vector id=\"v3i8\" type=\"int8\" count=\"3\"/>"
|
||||||
|
"<vector id=\"v4i32\" type=\"int32\" count=\"4\"/>"
|
||||||
|
"<vector id=\"v2i64\" type=\"int64\" count=\"2\"/>"
|
||||||
|
"<union id=\"vec128\">"
|
||||||
|
"<field name=\"v4_int32\" type=\"v4i32\"/>"
|
||||||
|
"<field name=\"v2_int64\" type=\"v2i64\"/>"
|
||||||
|
"<field name=\"uint128\" type=\"uint128\"/>"
|
||||||
|
"</union>"
|
||||||
|
"<reg name=\"q0\" bitsize=\"128\" type=\"vec128\" group=\"vector\" regnum=\"4211\"/>"
|
||||||
|
"<reg name=\"q1\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"q2\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"q3\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"q4\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"q5\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"q6\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"q7\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"qacc_l_l\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"qacc_l_h\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"qacc_h_l\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"qacc_h_h\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"ua_state\" bitsize=\"128\" type=\"vec128\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"xacc\" bitsize=\"40\" type=\"v5i8\" group=\"vector\"/>"
|
||||||
|
"<reg name=\"misc\" bitsize=\"24\" type=\"v3i8\" group=\"vector\"/>" /* make GDB happy about "Truncated register 4207 in remote 'g' packet" */
|
||||||
|
"</feature>"
|
||||||
|
#endif /* SOC_CPU_HAS_PIE */
|
||||||
|
"</target>";
|
||||||
|
#endif
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -349,78 +349,73 @@ void esp_gdbstub_trigger_cpu(void)
|
|||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
|
|
||||||
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_ptr)
|
||||||
{
|
{
|
||||||
uint32_t temp_fpu_value = value;
|
uint32_t value = *value_ptr;
|
||||||
float *ptr0;
|
|
||||||
|
|
||||||
asm volatile ("mov %0, %1" : "=a" (ptr0) : "a" (&temp_fpu_value));
|
|
||||||
|
|
||||||
if (reg_index == 0) {
|
if (reg_index == 0) {
|
||||||
frame->pc = value;
|
frame->pc = value;
|
||||||
} else if (reg_index > 0 && (reg_index <= 27)) {
|
} else if (reg_index > 0 && (reg_index <= 27)) {
|
||||||
(&frame->a0)[reg_index - 1] = value;
|
(&frame->a0)[reg_index - 1] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if XCHAL_HAVE_FP
|
#if XCHAL_HAVE_FP
|
||||||
void *ptr1;
|
|
||||||
uint32_t cp_enabled;
|
uint32_t cp_enabled;
|
||||||
RSR(XT_REG_CPENABLE, cp_enabled);
|
RSR(XT_REG_CPENABLE, cp_enabled);
|
||||||
if (cp_enabled != 0) {
|
if (cp_enabled != 0) {
|
||||||
if (reg_index == 87) {
|
if (reg_index == 87) {
|
||||||
asm volatile ("lsi f0, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f0, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 88) {
|
if (reg_index == 88) {
|
||||||
asm volatile ("lsi f1, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f1, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 89) {
|
if (reg_index == 89) {
|
||||||
asm volatile ("lsi f2, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f2, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 90) {
|
if (reg_index == 90) {
|
||||||
asm volatile ("lsi f3, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f3, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 91) {
|
if (reg_index == 91) {
|
||||||
asm volatile ("lsi f4, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f4, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 92) {
|
if (reg_index == 92) {
|
||||||
asm volatile ("lsi f5, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f5, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 93) {
|
if (reg_index == 93) {
|
||||||
asm volatile ("lsi f6, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f6, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 94) {
|
if (reg_index == 94) {
|
||||||
asm volatile ("lsi f7, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f7, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 95) {
|
if (reg_index == 95) {
|
||||||
asm volatile ("lsi f8, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f8, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 96) {
|
if (reg_index == 96) {
|
||||||
asm volatile ("lsi f9, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f9, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 97) {
|
if (reg_index == 97) {
|
||||||
asm volatile ("lsi f10, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f10, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 98) {
|
if (reg_index == 98) {
|
||||||
asm volatile ("lsi f11, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f11, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 99) {
|
if (reg_index == 99) {
|
||||||
asm volatile ("lsi f12, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f12, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 100) {
|
if (reg_index == 100) {
|
||||||
asm volatile ("lsi f13, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f13, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 101) {
|
if (reg_index == 101) {
|
||||||
asm volatile ("lsi f14, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f14, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 102) {
|
if (reg_index == 102) {
|
||||||
asm volatile ("lsi f15, %0, 0" :: "a" (ptr0));
|
asm volatile ("lsi f15, %0, 0" :: "a" (value_ptr));
|
||||||
}
|
}
|
||||||
if (reg_index == 103) {
|
if (reg_index == 103) {
|
||||||
asm volatile ("l32i %0, %1, 0" : "=a" (ptr1) : "a" (ptr0));
|
asm volatile ("wur.FCR %0" : "=a" (value));
|
||||||
asm volatile ("wur.FCR %0" : "=a" (ptr1));
|
|
||||||
}
|
}
|
||||||
if (reg_index == 104) {
|
if (reg_index == 104) {
|
||||||
asm volatile ("l32i %0, %1, 0" : "=a" (ptr1) : "a" (ptr0));
|
asm volatile ("wur.FSR %0" : "=a" (value));
|
||||||
asm volatile ("wur.FSR %0" : "=a" (ptr1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // XCHAL_HAVE_FP
|
#endif // XCHAL_HAVE_FP
|
||||||
|
@@ -5,4 +5,4 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
#define CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME 1
|
||||||
|
@@ -264,6 +264,7 @@ _hwlp_restore_end:
|
|||||||
esp.vst.128.ip q0, \frame, 16
|
esp.vst.128.ip q0, \frame, 16
|
||||||
esp.vst.128.ip q1, \frame, 16
|
esp.vst.128.ip q1, \frame, 16
|
||||||
esp.vst.128.ip q2, \frame, 16
|
esp.vst.128.ip q2, \frame, 16
|
||||||
|
esp.vst.128.ip q3, \frame, 16
|
||||||
esp.vst.128.ip q4, \frame, 16
|
esp.vst.128.ip q4, \frame, 16
|
||||||
esp.vst.128.ip q5, \frame, 16
|
esp.vst.128.ip q5, \frame, 16
|
||||||
esp.vst.128.ip q6, \frame, 16
|
esp.vst.128.ip q6, \frame, 16
|
||||||
@@ -275,20 +276,57 @@ _hwlp_restore_end:
|
|||||||
esp.st.qacc.h.h.128.ip \frame, 16
|
esp.st.qacc.h.h.128.ip \frame, 16
|
||||||
/* UA_STATE register (128 bits) */
|
/* UA_STATE register (128 bits) */
|
||||||
esp.st.ua.state.ip \frame, 16
|
esp.st.ua.state.ip \frame, 16
|
||||||
/* XACC register (40 bits) */
|
/* XACC register (40 bits)
|
||||||
esp.st.u.xacc.ip \frame, 8
|
*
|
||||||
/* The following registers will be stored in the same word */
|
* Bit layout across two 32-bit words (total 64 bits, but only 40 bits are used):
|
||||||
|
*
|
||||||
|
* Word 0 (Lower 32 bits):
|
||||||
|
* +------+------+------+------+------+------+------+------+ 32
|
||||||
|
* | res | res | res | res | res | res | res | res |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 24
|
||||||
|
* | res | res | sar | sar | sar | sar | sar | sar |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 16
|
||||||
|
* | sarB | sarB | sarB | sarB | fftW | fftW | fftW | fftW |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 8
|
||||||
|
* | xacc | xacc | xacc | xacc | xacc | xacc | xacc | xacc |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 0
|
||||||
|
*
|
||||||
|
* Word 1 (Upper 32 bits, only lower 8 bits used):
|
||||||
|
* +------+------+------+------+------+------+------+------+ 32
|
||||||
|
* | xacc | xacc | xacc | xacc | xacc | xacc | xacc | xacc |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 24
|
||||||
|
* | xacc | xacc | xacc | xacc | xacc | xacc | xacc | xacc |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 16
|
||||||
|
* | xacc | xacc | xacc | xacc | xacc | xacc | xacc | xacc |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 8
|
||||||
|
* | xacc | xacc | xacc | xacc | xacc | xacc | xacc | xacc |
|
||||||
|
* +------+------+------+------+------+------+------+------+ 0
|
||||||
|
*
|
||||||
|
* Legend:
|
||||||
|
* - `xacc` = xacc bits
|
||||||
|
* - `sar` = sar bits
|
||||||
|
* - `sarB` = sar_bytes bits
|
||||||
|
* - `fftW` = FFT bit width bits
|
||||||
|
* - `res` = Reserved bits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pointer not increased to write data as tightly as possible (minimum increment is 8). */
|
||||||
|
esp.st.u.xacc.ip \frame, 0
|
||||||
|
|
||||||
|
/* SAR_BYTES and FFT_BIT_WIDTH registers
|
||||||
|
* Prepare the 8-bit value: (SAR_BYTES << 4) | FFT_BIT_WIDTH */
|
||||||
|
esp.movx.r.sar.bytes a1 /* Load SAR_BYTES register (4 bits) */
|
||||||
|
slli a2, a1, 4 /* a2 = (SAR_BYTES << 4) */
|
||||||
|
|
||||||
|
esp.movx.r.fft.bit.width a1 /* Load FFT_BIT_WIDTH register (4 bits) */
|
||||||
|
or a2, a2, a1 /* a2 |= FFT_BIT_WIDTH */
|
||||||
|
|
||||||
|
sb a2, 5(\frame) /* Store byte after the XACC data */
|
||||||
|
|
||||||
/* SAR register (6 bits) */
|
/* SAR register (6 bits) */
|
||||||
esp.movx.r.sar a1
|
esp.movx.r.sar a1
|
||||||
slli a2, a1, 8
|
sb a1, 6(\frame) /* Store byte after the SAR_BYTES and FFT_BIT_WIDTH */
|
||||||
/* SAR_BYTES register (4 bits) */
|
|
||||||
esp.movx.r.sar.bytes a1
|
|
||||||
slli a1, a1, 4
|
|
||||||
or a2, a2, a1
|
|
||||||
/* FFT_BIT_WIDTH register (4 bits) */
|
|
||||||
esp.movx.r.fft.bit.width a1
|
|
||||||
or a2, a2, a1
|
|
||||||
sw a2, (\frame)
|
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
|
||||||
@@ -297,6 +335,7 @@ _hwlp_restore_end:
|
|||||||
esp.vld.128.ip q0, \frame, 16
|
esp.vld.128.ip q0, \frame, 16
|
||||||
esp.vld.128.ip q1, \frame, 16
|
esp.vld.128.ip q1, \frame, 16
|
||||||
esp.vld.128.ip q2, \frame, 16
|
esp.vld.128.ip q2, \frame, 16
|
||||||
|
esp.vld.128.ip q3, \frame, 16
|
||||||
esp.vld.128.ip q4, \frame, 16
|
esp.vld.128.ip q4, \frame, 16
|
||||||
esp.vld.128.ip q5, \frame, 16
|
esp.vld.128.ip q5, \frame, 16
|
||||||
esp.vld.128.ip q6, \frame, 16
|
esp.vld.128.ip q6, \frame, 16
|
||||||
@@ -308,21 +347,31 @@ _hwlp_restore_end:
|
|||||||
esp.ld.qacc.h.h.128.ip \frame, 16
|
esp.ld.qacc.h.h.128.ip \frame, 16
|
||||||
/* UA_STATE register (128 bits) */
|
/* UA_STATE register (128 bits) */
|
||||||
esp.ld.ua.state.ip \frame, 16
|
esp.ld.ua.state.ip \frame, 16
|
||||||
/* XACC register (40 bits) */
|
/* XACC register (40 bits)
|
||||||
esp.ld.xacc.ip \frame, 8
|
* Pointer not increased because the minimum step is 8, preventing data loss */
|
||||||
/* The following registers are stored in the same word */
|
esp.ld.xacc.ip \frame, 0
|
||||||
lw a2, (\frame)
|
|
||||||
|
/* The following registers are packed in the same word (addr: frame + 4):
|
||||||
|
* - XACC (upper byte) [7..0]
|
||||||
|
* - FFT_BIT_WIDTH [11..8]
|
||||||
|
* - SAR_BYTES [15..12]
|
||||||
|
* - SAR [21..16]
|
||||||
|
*
|
||||||
|
* See pie_save_regs macro for more details.
|
||||||
|
*/
|
||||||
|
lbu a1, 5(\frame) /* Load packed FFT_BIT_WIDTH and SAR_BYTES from offset 5
|
||||||
|
* (frame points to the start of the 5-byte XACC) */
|
||||||
|
|
||||||
/* FFT_BIT_WIDTH register (4 bits) */
|
/* FFT_BIT_WIDTH register (4 bits) */
|
||||||
andi a1, a2, 0xf
|
esp.movx.w.fft.bit.width a1 /* FFT_BIT_WIDTH[3:0] = rs1[3:0] */
|
||||||
esp.movx.w.sar a1
|
|
||||||
/* SAR_BYTES register (4 bits) */
|
/* SAR_BYTES register (4 bits) */
|
||||||
srli a2, a2, 4
|
srli a1, a1, 4
|
||||||
andi a1, a2, 0xf
|
esp.movx.w.sar.bytes a1 /* SAR_BYTE[3:0] = rs1[3:0] */
|
||||||
esp.movx.w.sar.bytes a1
|
|
||||||
/* SAR register (6 bits) */
|
/* SAR register (6 bits) */
|
||||||
srli a2, a2, 4
|
lbu a1, 6(\frame)
|
||||||
andi a1, a2, 0x3f
|
esp.movx.w.sar a1 /* SAR[5:0] = rs1[5:0] */
|
||||||
esp.movx.w.fft.bit.width a1
|
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
generate_coprocessor_routine pie, PIE_COPROC_IDX, pie_enable, pie_save_regs, pie_restore_regs
|
generate_coprocessor_routine pie, PIE_COPROC_IDX, pie_enable, pie_save_regs, pie_restore_regs
|
||||||
|
@@ -239,10 +239,13 @@ FORCE_INLINE_ATTR void rv_utils_intr_set_threshold(int priority_threshold)
|
|||||||
|
|
||||||
#if SOC_CPU_HAS_FPU
|
#if SOC_CPU_HAS_FPU
|
||||||
|
|
||||||
FORCE_INLINE_ATTR bool rv_utils_enable_fpu(void)
|
FORCE_INLINE_ATTR void rv_utils_enable_fpu(void)
|
||||||
{
|
{
|
||||||
/* Set mstatus[14:13] to 0b01 to start the floating-point unit initialization */
|
/* Set mstatus[14:13] to 0b01 to enable the floating-point unit */
|
||||||
RV_SET_CSR(mstatus, CSR_MSTATUS_FPU_ENA);
|
RV_SET_CSR(mstatus, CSR_MSTATUS_FPU_ENA);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR bool rv_utils_clear_fpu(void) {
|
||||||
/* On the ESP32-P4, the FPU can be used directly after setting `mstatus` bit 13.
|
/* On the ESP32-P4, the FPU can be used directly after setting `mstatus` bit 13.
|
||||||
* Since the interrupt handler expects the FPU states to be either 0b10 or 0b11,
|
* Since the interrupt handler expects the FPU states to be either 0b10 or 0b11,
|
||||||
* let's write the FPU CSR and clear the dirty bit afterwards. */
|
* let's write the FPU CSR and clear the dirty bit afterwards. */
|
||||||
|
@@ -233,8 +233,8 @@ STRUCT_AFIELD (long, 4, RV_PIE_QACC_H_L, qacc_h_l, 4)
|
|||||||
STRUCT_AFIELD (long, 4, RV_PIE_QACC_H_H, qacc_h_h, 4)
|
STRUCT_AFIELD (long, 4, RV_PIE_QACC_H_H, qacc_h_h, 4)
|
||||||
STRUCT_AFIELD (long, 4, RV_PIE_UA_STATE, ua_state, 4)
|
STRUCT_AFIELD (long, 4, RV_PIE_UA_STATE, ua_state, 4)
|
||||||
STRUCT_FIELD (long, 4, RV_PIE_XACC, xacc)
|
STRUCT_FIELD (long, 4, RV_PIE_XACC, xacc)
|
||||||
/* This register contains SAR, SAR_BYTES and FFT_BIT_WIDTH in this order (from top to low) */
|
/* misc field contains registers: XACC (upper byte) [7:0], FFT_BIT_WIDTH [11:8], SAR_BYTES [15:12], and SAR [21:16] */
|
||||||
STRUCT_FIELD (long, 4, RV_PIE_MISC, misc)
|
STRUCT_FIELD (long, 4, RV_PIE_MISC, misc)
|
||||||
STRUCT_END(RvPIESaveArea)
|
STRUCT_END(RvPIESaveArea)
|
||||||
|
|
||||||
/* Redefine the coprocessor area size previously defined to 0 */
|
/* Redefine the coprocessor area size previously defined to 0 */
|
||||||
|
@@ -1,10 +1,16 @@
|
|||||||
set(srcs "test_app_main.c")
|
set(srcs "test_app_main.c")
|
||||||
if(CONFIG_IDF_TARGET_ARCH_RISCV AND CONFIG_SOC_CPU_HAS_HWLOOP)
|
if(CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||||
|
if(CONFIG_SOC_CPU_HAS_HWLOOP)
|
||||||
list(APPEND srcs "xesppie_loops.S")
|
list(APPEND srcs "xesppie_loops.S")
|
||||||
|
endif()
|
||||||
|
if(CONFIG_SOC_CPU_HAS_FPU OR CONFIG_SOC_CPU_HAS_PIE)
|
||||||
|
list(APPEND srcs "coproc_regs.c")
|
||||||
|
set(ext_comp "riscv")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
idf_component_register(SRCS ${srcs}
|
idf_component_register(SRCS ${srcs}
|
||||||
INCLUDE_DIRS ""
|
INCLUDE_DIRS ""
|
||||||
REQUIRES esp_gdbstub)
|
REQUIRES esp_gdbstub ${ext_comp})
|
||||||
|
|
||||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-unused-label")
|
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-unused-label")
|
||||||
|
163
tools/test_apps/system/gdbstub_runtime/main/coproc_regs.c
Normal file
163
tools/test_apps/system/gdbstub_runtime/main/coproc_regs.c
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "riscv/rvruntime-frames.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
static SemaphoreHandle_t sem = NULL;
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
static volatile bool test_pie_ready = false;
|
||||||
|
#endif
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
static volatile bool test_fpu_ready = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
static void test_pie(void * arg)
|
||||||
|
{
|
||||||
|
RvPIESaveArea pie_regs_sample, pie_regs;
|
||||||
|
register void* ptr asm("a5");
|
||||||
|
|
||||||
|
memset(pie_regs_sample.q0, 0x10 + (uint32_t) arg, sizeof(pie_regs_sample.q0));
|
||||||
|
memset(pie_regs_sample.q1, 0x11 + (uint32_t) arg, sizeof(pie_regs_sample.q1));
|
||||||
|
memset(pie_regs_sample.q2, 0x12 + (uint32_t) arg, sizeof(pie_regs_sample.q2));
|
||||||
|
memset(pie_regs_sample.q3, 0x13 + (uint32_t) arg, sizeof(pie_regs_sample.q3));
|
||||||
|
memset(pie_regs_sample.q4, 0x14 + (uint32_t) arg, sizeof(pie_regs_sample.q4));
|
||||||
|
memset(pie_regs_sample.q5, 0x15 + (uint32_t) arg, sizeof(pie_regs_sample.q5));
|
||||||
|
memset(pie_regs_sample.q6, 0x16 + (uint32_t) arg, sizeof(pie_regs_sample.q6));
|
||||||
|
memset(pie_regs_sample.q7, 0x17 + (uint32_t) arg, sizeof(pie_regs_sample.q7));
|
||||||
|
memset(pie_regs_sample.qacc_l_l, 0x18 + (uint32_t) arg, sizeof(pie_regs_sample.qacc_l_l));
|
||||||
|
memset(pie_regs_sample.qacc_l_h, 0x19 + (uint32_t) arg, sizeof(pie_regs_sample.qacc_l_h));
|
||||||
|
memset(pie_regs_sample.qacc_h_l, 0x1a + (uint32_t) arg, sizeof(pie_regs_sample.qacc_h_l));
|
||||||
|
memset(pie_regs_sample.qacc_h_h, 0x1b + (uint32_t) arg, sizeof(pie_regs_sample.qacc_h_h));
|
||||||
|
memset(pie_regs_sample.ua_state, 0x1c + (uint32_t) arg, sizeof(pie_regs_sample.ua_state));
|
||||||
|
memset(&pie_regs_sample.xacc, 0x1d + (uint32_t) arg, sizeof(uint8_t) * 5 /* 40-bits */);
|
||||||
|
|
||||||
|
pie_start:
|
||||||
|
asm volatile ("nop");
|
||||||
|
|
||||||
|
while (!test_pie_ready) {
|
||||||
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
__asm__ volatile ("mv %0, %1" : "=r"(ptr) : "r" (&pie_regs)); /* ptr = &pie_regs; */
|
||||||
|
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q0, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q1, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q2, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q3, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q4, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q5, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q6, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.vst.128.ip q7, %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.l.l.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.l.h.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.h.l.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.qacc.h.h.128.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.ua.state.ip %0, 16" :: "r" (ptr));
|
||||||
|
__asm__ volatile ("esp.st.s.xacc.ip %0, 0" :: "r" (ptr));
|
||||||
|
|
||||||
|
if (!memcmp(&pie_regs_sample, &pie_regs, sizeof(pie_regs))) {
|
||||||
|
xSemaphoreGive((SemaphoreHandle_t) sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
static void test_fpu(void * arg)
|
||||||
|
{
|
||||||
|
RvFPUSaveArea fpu_regs_sample, fpu_regs;
|
||||||
|
|
||||||
|
uint32_t *ptr = (uint32_t *)&fpu_regs_sample;
|
||||||
|
for (int i = 0; i < sizeof(fpu_regs_sample)/sizeof(uint32_t); i++) {
|
||||||
|
ptr[i] = i + (uintptr_t) arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
fpu_start:
|
||||||
|
asm volatile ("nop");
|
||||||
|
|
||||||
|
while (!test_fpu_ready) {
|
||||||
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
__asm__ volatile ("fsw ft0, %0" : "=m" (fpu_regs.ft0));
|
||||||
|
__asm__ volatile ("fsw ft1, %0" : "=m" (fpu_regs.ft1));
|
||||||
|
__asm__ volatile ("fsw ft2, %0" : "=m" (fpu_regs.ft2));
|
||||||
|
__asm__ volatile ("fsw ft3, %0" : "=m" (fpu_regs.ft3));
|
||||||
|
__asm__ volatile ("fsw ft4, %0" : "=m" (fpu_regs.ft4));
|
||||||
|
__asm__ volatile ("fsw ft5, %0" : "=m" (fpu_regs.ft5));
|
||||||
|
__asm__ volatile ("fsw ft6, %0" : "=m" (fpu_regs.ft6));
|
||||||
|
__asm__ volatile ("fsw ft7, %0" : "=m" (fpu_regs.ft7));
|
||||||
|
__asm__ volatile ("fsw fs0, %0" : "=m" (fpu_regs.fs0));
|
||||||
|
__asm__ volatile ("fsw fs1, %0" : "=m" (fpu_regs.fs1));
|
||||||
|
__asm__ volatile ("fsw fa0, %0" : "=m" (fpu_regs.fa0));
|
||||||
|
__asm__ volatile ("fsw fa1, %0" : "=m" (fpu_regs.fa1));
|
||||||
|
__asm__ volatile ("fsw fa2, %0" : "=m" (fpu_regs.fa2));
|
||||||
|
__asm__ volatile ("fsw fa3, %0" : "=m" (fpu_regs.fa3));
|
||||||
|
__asm__ volatile ("fsw fa4, %0" : "=m" (fpu_regs.fa4));
|
||||||
|
__asm__ volatile ("fsw fa5, %0" : "=m" (fpu_regs.fa5));
|
||||||
|
__asm__ volatile ("fsw fa6, %0" : "=m" (fpu_regs.fa6));
|
||||||
|
__asm__ volatile ("fsw fa7, %0" : "=m" (fpu_regs.fa7));
|
||||||
|
__asm__ volatile ("fsw fs2, %0" : "=m" (fpu_regs.fs2));
|
||||||
|
__asm__ volatile ("fsw fs3, %0" : "=m" (fpu_regs.fs3));
|
||||||
|
__asm__ volatile ("fsw fs4, %0" : "=m" (fpu_regs.fs4));
|
||||||
|
__asm__ volatile ("fsw fs5, %0" : "=m" (fpu_regs.fs5));
|
||||||
|
__asm__ volatile ("fsw fs6, %0" : "=m" (fpu_regs.fs6));
|
||||||
|
__asm__ volatile ("fsw fs7, %0" : "=m" (fpu_regs.fs7));
|
||||||
|
__asm__ volatile ("fsw fs8, %0" : "=m" (fpu_regs.fs8));
|
||||||
|
__asm__ volatile ("fsw fs9, %0" : "=m" (fpu_regs.fs9));
|
||||||
|
__asm__ volatile ("fsw fs10, %0" : "=m" (fpu_regs.fs10));
|
||||||
|
__asm__ volatile ("fsw fs11, %0" : "=m" (fpu_regs.fs11));
|
||||||
|
__asm__ volatile ("fsw ft8, %0" : "=m" (fpu_regs.ft8));
|
||||||
|
__asm__ volatile ("fsw ft9, %0" : "=m" (fpu_regs.ft9));
|
||||||
|
__asm__ volatile ("fsw ft10, %0" : "=m" (fpu_regs.ft10));
|
||||||
|
__asm__ volatile ("fsw ft11, %0" : "=m" (fpu_regs.ft11));
|
||||||
|
__asm__ volatile ("csrr %0, fcsr" : "=r" (fpu_regs.fcsr));
|
||||||
|
|
||||||
|
if (!memcmp(&fpu_regs_sample, &fpu_regs, sizeof(fpu_regs))) {
|
||||||
|
xSemaphoreGive((SemaphoreHandle_t) sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* TODO: IDF-12550. Extend test for both CPU0 and CPU1 */
|
||||||
|
void test_coproc_regs(void) {
|
||||||
|
sem = xSemaphoreCreateCounting(2, 0);
|
||||||
|
#if SOC_CPU_HAS_FPU
|
||||||
|
xTaskCreatePinnedToCore(test_fpu, "test_fpu_1", 4096, (void *)1, 10, NULL, 0);
|
||||||
|
xTaskCreatePinnedToCore(test_fpu, "test_fpu_2", 4096, (void *)2, 10, NULL, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2;) {
|
||||||
|
if (xSemaphoreTake(sem, 500 / portTICK_PERIOD_MS)) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vTaskDelay(10); // Allow tasks to clean up
|
||||||
|
fpu_succeed:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_CPU_HAS_PIE
|
||||||
|
xTaskCreatePinnedToCore(test_pie, "test_pie_1", 4096, (void *)1, 10, NULL, 0);
|
||||||
|
xTaskCreatePinnedToCore(test_pie, "test_pie_2", 4096, (void *)2, 10, NULL, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2;) {
|
||||||
|
if (xSemaphoreTake(sem, 500 / portTICK_PERIOD_MS)) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vTaskDelay(10); // Allow tasks to clean up
|
||||||
|
pie_succeed:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vSemaphoreDelete(sem);
|
||||||
|
}
|
@@ -1,18 +1,28 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#define TEST_COPROCESSOR_REGISTERS (__riscv && (SOC_CPU_HAS_FPU || SOC_CPU_HAS_PIE))
|
||||||
|
#define TEST_HWLOOP_INSTRUCTIONS (__riscv && SOC_CPU_HAS_HWLOOP)
|
||||||
|
|
||||||
int var_1;
|
int var_1;
|
||||||
int var_2;
|
int var_2;
|
||||||
int do_panic;
|
int do_panic;
|
||||||
|
int start_testing;
|
||||||
|
#if TEST_COPROCESSOR_REGISTERS
|
||||||
|
int do_test_coproc_regs;
|
||||||
|
#endif
|
||||||
|
#if TEST_HWLOOP_INSTRUCTIONS
|
||||||
|
int do_test_xesppie_loops;
|
||||||
|
#endif
|
||||||
|
|
||||||
void foo(void)
|
void foo(void)
|
||||||
{
|
{
|
||||||
@@ -21,14 +31,27 @@ void foo(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_xesppie_loops(void);
|
void test_xesppie_loops(void);
|
||||||
|
void test_coproc_regs(void);
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
printf("tested app is running.\n");
|
printf("waiting start_testing variable to be changed.\n");
|
||||||
|
while (!start_testing) { /* set via GDB */
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
#if TEST_HWLOOP_INSTRUCTIONS
|
||||||
|
if (do_test_xesppie_loops) { /* set via GDB */
|
||||||
|
test_xesppie_loops();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SOC_CPU_HAS_HWLOOP
|
#if TEST_COPROCESSOR_REGISTERS
|
||||||
test_xesppie_loops();
|
if (do_test_coproc_regs) { /* set via GDB */
|
||||||
|
test_coproc_regs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
@@ -3,8 +3,8 @@
|
|||||||
import os
|
import os
|
||||||
import os.path as path
|
import os.path as path
|
||||||
import sys
|
import sys
|
||||||
|
from collections.abc import Callable
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pytest_embedded_idf.utils import idf_parametrize
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
@@ -23,12 +23,12 @@ def get_line_number(lookup: str, offset: int = 0) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def start_gdb(dut: PanicTestDut) -> None:
|
def start_gdb(dut: PanicTestDut) -> None:
|
||||||
dut.expect_exact('tested app is running.')
|
dut.expect_exact('waiting start_testing variable to be changed.')
|
||||||
dut.write(b'\x03') # send Ctrl-C
|
dut.write(b'\x03') # send Ctrl-C
|
||||||
dut.start_gdb_for_gdbstub()
|
dut.start_gdb_for_gdbstub()
|
||||||
|
|
||||||
|
|
||||||
def run_and_break(dut: PanicTestDut, cmd: str) -> Dict[Any, Any]:
|
def run_and_break(dut: PanicTestDut, cmd: str) -> dict[Any, Any]:
|
||||||
responses = dut.gdb_write(cmd)
|
responses = dut.gdb_write(cmd)
|
||||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||||
if not dut.find_gdb_response('stopped', 'notify', responses): # have not stopped on breakpoint yet
|
if not dut.find_gdb_response('stopped', 'notify', responses): # have not stopped on breakpoint yet
|
||||||
@@ -39,11 +39,37 @@ def run_and_break(dut: PanicTestDut, cmd: str) -> Dict[Any, Any]:
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def dut_set_variable(dut: PanicTestDut, var_name: str, value: int) -> None:
|
||||||
|
cmd = f'-gdb-set {var_name}={value}'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def dut_enable_test(dut: PanicTestDut, testcase: str | None = None) -> None:
|
||||||
|
dut_set_variable(dut, 'start_testing', 1)
|
||||||
|
|
||||||
|
# enable specific testcase (otherwise default testcase)
|
||||||
|
if testcase:
|
||||||
|
dut_set_variable(dut, f'do_test_{testcase}', 1)
|
||||||
|
|
||||||
|
|
||||||
|
def dut_get_threads(dut: PanicTestDut) -> Any:
|
||||||
|
cmd = '-thread-info'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
if not responses[0]['message']:
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert responses is not None
|
||||||
|
return responses[0]['payload']['threads']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.generic
|
@pytest.mark.generic
|
||||||
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||||
def test_hwloop_jump(dut: PanicTestDut) -> None:
|
def test_hwloop_jump(dut: PanicTestDut) -> None:
|
||||||
start_gdb(dut)
|
start_gdb(dut)
|
||||||
|
|
||||||
|
# enable coprocessors registers testing
|
||||||
|
dut_enable_test(dut, 'xesppie_loops')
|
||||||
|
|
||||||
cmd = '-break-insert --source xesppie_loops.S --function test_loop_start'
|
cmd = '-break-insert --source xesppie_loops.S --function test_loop_start'
|
||||||
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||||
assert response is not None
|
assert response is not None
|
||||||
@@ -98,11 +124,153 @@ def test_hwloop_jump(dut: PanicTestDut) -> None:
|
|||||||
assert payload['stopped-threads'] == 'all'
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
|
||||||
|
def check_registers_numbers(dut: PanicTestDut) -> None:
|
||||||
|
cmd = '-data-list-register-values d'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
registers = responses[0]['payload']['register-values']
|
||||||
|
assert len(registers) == 83 # 80 registers supported + xesppie misc + pseudo frm, fflags
|
||||||
|
|
||||||
|
r_id = 0
|
||||||
|
for r in registers:
|
||||||
|
assert int(r['number']) == r_id
|
||||||
|
|
||||||
|
if r_id == 4211: # check if value of q0 register is uint128
|
||||||
|
assert 'uint128' in r['value']
|
||||||
|
|
||||||
|
if r_id == 64:
|
||||||
|
r_id = 68 # fcsr
|
||||||
|
elif r_id == 68:
|
||||||
|
r_id = 4211 # q0
|
||||||
|
else:
|
||||||
|
r_id += 1
|
||||||
|
|
||||||
|
|
||||||
|
def set_float_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
|
||||||
|
cmd = f'-thread-select {t_id}'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
for i in range(32):
|
||||||
|
cmd = f'-data-write-register-values d {33 + i} {i + addition}'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
# Note that it's a gap between the last floating register number and fcsr register number.
|
||||||
|
cmd = f'-data-write-register-values d 68 {32 + addition}'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def set_pie_registers(dut: PanicTestDut, t_id: int, addition: int) -> None:
|
||||||
|
cmd = f'-thread-select {t_id}'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
def set_gdb_128_bit_register(reg: str, byte: int) -> None:
|
||||||
|
val64 = f'0x{hex(byte)[2:] * 8}'
|
||||||
|
value = f'{{{val64}, {val64}}}'
|
||||||
|
cmd = f'-interpreter-exec console "set ${reg}.v2_int64={value}"'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
for i in range(8):
|
||||||
|
set_gdb_128_bit_register(f'q{i}', 0x10 + i + addition)
|
||||||
|
|
||||||
|
set_gdb_128_bit_register('qacc_l_l', 0x18 + addition)
|
||||||
|
set_gdb_128_bit_register('qacc_l_h', 0x19 + addition)
|
||||||
|
set_gdb_128_bit_register('qacc_h_l', 0x1A + addition)
|
||||||
|
set_gdb_128_bit_register('qacc_h_h', 0x1B + addition)
|
||||||
|
set_gdb_128_bit_register('ua_state', 0x1C + addition)
|
||||||
|
|
||||||
|
xacc_val = ','.join([hex(0x1D + addition)] * 5)
|
||||||
|
cmd = f'-interpreter-exec console "set $xacc={{{xacc_val}}}"'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def coproc_registers_test(dut: PanicTestDut, regs_type: str, set_registers: Callable) -> None:
|
||||||
|
# set start test breakpoint
|
||||||
|
cmd = f'-break-insert --source coproc_regs.c --function test_{regs_type} --label {regs_type}_start'
|
||||||
|
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||||
|
assert response is not None
|
||||||
|
|
||||||
|
# stop when the second task is stopped
|
||||||
|
for i in range(2):
|
||||||
|
cmd = '-exec-continue'
|
||||||
|
payload = run_and_break(dut, cmd)
|
||||||
|
assert payload['reason'] == 'breakpoint-hit'
|
||||||
|
assert payload['frame']['func'] == f'test_{regs_type}'
|
||||||
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
threads = dut_get_threads(dut)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Set expected values to both testing tasks.
|
||||||
|
This will test setting register for both:
|
||||||
|
- Task coproc owner (direct registers write)
|
||||||
|
- Other tasks (write registers to task's stack)
|
||||||
|
"""
|
||||||
|
coproc_tasks = [f'test_{regs_type}_1', f'test_{regs_type}_2']
|
||||||
|
found_tasks = [False] * len(coproc_tasks)
|
||||||
|
for t in threads:
|
||||||
|
for index, test in enumerate(coproc_tasks):
|
||||||
|
if test in t['details']:
|
||||||
|
set_registers(dut, t['id'], index + 1)
|
||||||
|
found_tasks[index] = True
|
||||||
|
|
||||||
|
assert all(found_tasks)
|
||||||
|
|
||||||
|
dut_set_variable(dut, f'test_{regs_type}_ready', 1)
|
||||||
|
|
||||||
|
cmd = '-break-delete'
|
||||||
|
responses = dut.gdb_write(cmd)
|
||||||
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
|
cmd = f'-break-insert --source coproc_regs.c --function test_coproc_regs --label {regs_type}_succeed'
|
||||||
|
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||||
|
assert response is not None
|
||||||
|
|
||||||
|
cmd = '-exec-continue'
|
||||||
|
payload = run_and_break(dut, cmd)
|
||||||
|
assert payload['reason'] == 'breakpoint-hit'
|
||||||
|
assert payload['frame']['func'] == 'test_coproc_regs'
|
||||||
|
assert payload['stopped-threads'] == 'all'
|
||||||
|
|
||||||
|
threads = dut_get_threads(dut)
|
||||||
|
|
||||||
|
found_tasks = [False] * len(coproc_tasks)
|
||||||
|
for t in threads:
|
||||||
|
for index, test in enumerate(coproc_tasks):
|
||||||
|
if test in t['details']:
|
||||||
|
found_tasks[index] = True
|
||||||
|
|
||||||
|
assert not any(found_tasks)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||||
|
def test_coproc_registers(dut: PanicTestDut) -> None:
|
||||||
|
start_gdb(dut)
|
||||||
|
|
||||||
|
# enable coprocessors registers testing
|
||||||
|
dut_enable_test(dut, 'coproc_regs')
|
||||||
|
|
||||||
|
check_registers_numbers(dut)
|
||||||
|
|
||||||
|
coproc_registers_test(dut, 'fpu', set_float_registers)
|
||||||
|
if dut.target == 'esp32p4':
|
||||||
|
coproc_registers_test(dut, 'pie', set_pie_registers)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.generic
|
@pytest.mark.generic
|
||||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||||
def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||||
start_gdb(dut)
|
start_gdb(dut)
|
||||||
|
|
||||||
|
dut_enable_test(dut)
|
||||||
|
|
||||||
# Test breakpoint
|
# Test breakpoint
|
||||||
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
||||||
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||||
@@ -187,9 +355,7 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
|||||||
assert dut.find_gdb_response('done', 'result', responses) is not None
|
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||||
|
|
||||||
# test set variable
|
# test set variable
|
||||||
cmd = '-gdb-set do_panic=1'
|
dut_set_variable(dut, 'do_panic', 1)
|
||||||
responses = dut.gdb_write(cmd)
|
|
||||||
assert dut.find_gdb_response('done', 'result', responses) is not None
|
|
||||||
|
|
||||||
# test panic handling
|
# test panic handling
|
||||||
cmd = '-exec-continue'
|
cmd = '-exec-continue'
|
||||||
@@ -207,6 +373,8 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
|||||||
def test_gdbstub_runtime_xtensa_stepping_bug(dut: PanicTestDut) -> None:
|
def test_gdbstub_runtime_xtensa_stepping_bug(dut: PanicTestDut) -> None:
|
||||||
start_gdb(dut)
|
start_gdb(dut)
|
||||||
|
|
||||||
|
dut_enable_test(dut)
|
||||||
|
|
||||||
# Test breakpoint
|
# Test breakpoint
|
||||||
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
||||||
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||||
|
Reference in New Issue
Block a user