mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
riscv: Use semihosting to set breakpoint and watchpoint when running under debugger
This commit is contained in:
parent
4edc903bb3
commit
dfd3a9c3bc
@ -180,6 +180,9 @@ __attribute__((naked)) static void prvTaskExitError(void)
|
||||
".option norvc\n" \
|
||||
"nop\n" \
|
||||
".option pop");
|
||||
/* Task entry's RA will point here. Shifting RA into prvTaskExitError is necessary
|
||||
to make GDB backtrace ending inside that function.
|
||||
Otherwise backtrace will end in the function laying just before prvTaskExitError in address space. */
|
||||
_prvTaskExitError();
|
||||
}
|
||||
|
||||
@ -293,7 +296,7 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
|
||||
memset(frame, 0, sizeof(*frame));
|
||||
/* Shifting RA into prvTaskExitError is necessary to make GDB backtrace ending inside that function.
|
||||
Otherwise backtrace will end in the function laying just before prvTaskExitError in address space. */
|
||||
frame->ra = (UBaseType_t)prvTaskExitError + 4/*nop size*/;
|
||||
frame->ra = (UBaseType_t)prvTaskExitError + 4/*size of the nop insruction at the beginning of prvTaskExitError*/;
|
||||
frame->mepc = (UBaseType_t)pxCode;
|
||||
frame->a0 = (UBaseType_t)pvParameters;
|
||||
frame->gp = (UBaseType_t)&__global_pointer$;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "soc/assist_debug_reg.h"
|
||||
#include "esp_attr.h"
|
||||
#include "riscv/csr.h"
|
||||
#include "riscv/semihosting.h"
|
||||
|
||||
/*performance counter*/
|
||||
#define CSR_PCER_MACHINE 0x7e0
|
||||
@ -72,8 +73,29 @@ static inline void cpu_ll_init_hwloop(void)
|
||||
// Nothing needed here for ESP32-C3
|
||||
}
|
||||
|
||||
static inline bool cpu_ll_is_debugger_attached(void)
|
||||
{
|
||||
return REG_GET_BIT(ASSIST_DEBUG_C0RE_0_DEBUG_MODE_REG, ASSIST_DEBUG_CORE_0_DEBUG_MODULE_ACTIVE);
|
||||
}
|
||||
|
||||
static inline void cpu_ll_set_breakpoint(int id, uint32_t pc)
|
||||
{
|
||||
if (cpu_ll_is_debugger_attached()) {
|
||||
/* If we want to set breakpoint which when hit transfers control to debugger
|
||||
* we need to set `action` in `mcontrol` to 1 (Enter Debug Mode).
|
||||
* That `action` value is supported only when `dmode` of `tdata1` is set.
|
||||
* But `dmode` can be modified by debugger only (from Debug Mode).
|
||||
*
|
||||
* So when debugger is connected we use special syscall to ask it to set breakpoint for us.
|
||||
*/
|
||||
long args[] = {true, id, (long)pc};
|
||||
int ret = semihosting_call_noerrno(ESP_SEMIHOSTING_SYS_BREAKPOINT_SET, args);
|
||||
if (ret == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* The code bellow sets breakpoint which will trigger `Breakpoint` exception
|
||||
* instead transfering control to debugger. */
|
||||
RV_WRITE_CSR(tselect,id);
|
||||
RV_SET_CSR(CSR_TCONTROL,TCONTROL_MTE);
|
||||
RV_SET_CSR(CSR_TDATA1, TDATA1_USER|TDATA1_MACHINE|TDATA1_EXECUTE);
|
||||
@ -83,6 +105,14 @@ static inline void cpu_ll_set_breakpoint(int id, uint32_t pc)
|
||||
|
||||
static inline void cpu_ll_clear_breakpoint(int id)
|
||||
{
|
||||
if (cpu_ll_is_debugger_attached()) {
|
||||
/* see description in cpu_ll_set_breakpoint() */
|
||||
long args[] = {false, id};
|
||||
int ret = semihosting_call_noerrno(ESP_SEMIHOSTING_SYS_BREAKPOINT_SET, args);
|
||||
if (ret == 0){
|
||||
return;
|
||||
}
|
||||
}
|
||||
RV_WRITE_CSR(tselect,id);
|
||||
RV_CLEAR_CSR(CSR_TCONTROL,TCONTROL_MTE);
|
||||
RV_CLEAR_CSR(CSR_TDATA1, TDATA1_USER|TDATA1_MACHINE|TDATA1_EXECUTE);
|
||||
@ -106,6 +136,17 @@ static inline void cpu_ll_set_watchpoint(int id,
|
||||
bool on_write)
|
||||
{
|
||||
uint32_t addr_napot;
|
||||
|
||||
if (cpu_ll_is_debugger_attached()) {
|
||||
/* see description in cpu_ll_set_breakpoint() */
|
||||
long args[] = {true, id, (long)addr, (long)size,
|
||||
(long)((on_read ? ESP_SEMIHOSTING_WP_FLG_RD : 0) | (on_write ? ESP_SEMIHOSTING_WP_FLG_WR : 0))};
|
||||
int ret = semihosting_call_noerrno(ESP_SEMIHOSTING_SYS_WATCHPOINT_SET, args);
|
||||
if (ret == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RV_WRITE_CSR(tselect,id);
|
||||
RV_SET_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE);
|
||||
RV_SET_CSR(CSR_TDATA1, TDATA1_USER|TDATA1_MACHINE);
|
||||
@ -124,6 +165,14 @@ static inline void cpu_ll_set_watchpoint(int id,
|
||||
|
||||
static inline void cpu_ll_clear_watchpoint(int id)
|
||||
{
|
||||
if (cpu_ll_is_debugger_attached()) {
|
||||
/* see description in cpu_ll_set_breakpoint() */
|
||||
long args[] = {false, id};
|
||||
int ret = semihosting_call_noerrno(ESP_SEMIHOSTING_SYS_WATCHPOINT_SET, args);
|
||||
if (ret == 0){
|
||||
return;
|
||||
}
|
||||
}
|
||||
RV_WRITE_CSR(tselect,id);
|
||||
RV_CLEAR_CSR(CSR_TCONTROL,TCONTROL_MTE);
|
||||
RV_CLEAR_CSR(CSR_TDATA1, TDATA1_USER|TDATA1_MACHINE);
|
||||
@ -133,11 +182,6 @@ static inline void cpu_ll_clear_watchpoint(int id)
|
||||
return;
|
||||
}
|
||||
|
||||
FORCE_INLINE_ATTR bool cpu_ll_is_debugger_attached(void)
|
||||
{
|
||||
return REG_GET_BIT(ASSIST_DEBUG_C0RE_0_DEBUG_MODE_REG, ASSIST_DEBUG_CORE_0_DEBUG_MODULE_ACTIVE);
|
||||
}
|
||||
|
||||
static inline void cpu_ll_break(void)
|
||||
{
|
||||
asm volatile("ebreak\n");
|
||||
|
99
components/riscv/include/riscv/semihosting.h
Normal file
99
components/riscv/include/riscv/semihosting.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* ESP custom semihosting calls numbers */
|
||||
|
||||
/**
|
||||
* @brief Set/clear breakpoint
|
||||
*
|
||||
* @param set if true set breakpoint, otherwise clear it
|
||||
* @param id breakpoint ID
|
||||
* @param addr address to set breakpoint at. Ignored if `set` is false.
|
||||
* @return return 0 on sucess or non-zero error code
|
||||
*/
|
||||
#define ESP_SEMIHOSTING_SYS_BREAKPOINT_SET 0x66
|
||||
|
||||
/**
|
||||
* @brief Set/clear watchpoint
|
||||
*
|
||||
* @param set if true set watchpoint, otherwise clear it
|
||||
* @param id watchpoint ID
|
||||
* @param addr address to set watchpoint at. Ignored if `set` is false.
|
||||
* @param size size of watchpoint. Ignored if `set` is false.
|
||||
* @param flags watchpoint flags, see description below. Ignored if `set` is false.
|
||||
* @return return 0 on sucess or non-zero error code
|
||||
*/
|
||||
#define ESP_SEMIHOSTING_SYS_WATCHPOINT_SET 0x67
|
||||
|
||||
/* bit values for `flags` argument of ESP_SEMIHOSTING_SYS_WATCHPOINT_SET call. Can be ORed. */
|
||||
/* watch for 'reads' at `addr` */
|
||||
#define ESP_SEMIHOSTING_WP_FLG_RD (1UL << 0)
|
||||
/* watch for 'writes' at `addr` */
|
||||
#define ESP_SEMIHOSTING_WP_FLG_WR (1UL << 1)
|
||||
|
||||
/**
|
||||
* @brief Perform semihosting call
|
||||
*
|
||||
* See https://github.com/riscv/riscv-semihosting-spec/ and the linked
|
||||
* ARM semihosting spec for details.
|
||||
*
|
||||
* @param id semihosting call number
|
||||
* @param data data block to pass to the host; number of items and their
|
||||
* meaning depends on the semihosting call. See the spec for
|
||||
* details.
|
||||
*
|
||||
* @return return value from the host
|
||||
*/
|
||||
static inline long semihosting_call_noerrno(long id, long *data)
|
||||
{
|
||||
register long a0 asm ("a0") = id;
|
||||
register long a1 asm ("a1") = (long) data;
|
||||
__asm__ __volatile__ (
|
||||
".option push\n"
|
||||
".option norvc\n"
|
||||
"slli zero, zero, 0x1f\n"
|
||||
"ebreak\n"
|
||||
"srai zero, zero, 0x7\n"
|
||||
".option pop\n"
|
||||
: "+r"(a0) : "r"(a1) : "memory");
|
||||
return a0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform semihosting call and retrieve errno
|
||||
*
|
||||
* @param id semihosting call number
|
||||
* @param data data block to pass to the host; number of items and their
|
||||
* meaning depends on the semihosting call. See the spec for
|
||||
* details.
|
||||
* @param[out] out_errno output, errno value from the host. Only set if
|
||||
* the return value is negative.
|
||||
* @return return value from the host
|
||||
*/
|
||||
static inline long semihosting_call(long id, long *data, int *out_errno)
|
||||
{
|
||||
long ret = semihosting_call_noerrno(id, data);
|
||||
if (ret < 0) {
|
||||
/* Constant also defined in openocd_semihosting.h,
|
||||
* which is common for RISC-V and Xtensa; it is not included here
|
||||
* to avoid a circular dependency.
|
||||
*/
|
||||
const int semihosting_sys_errno = 0x13;
|
||||
*out_errno = (int) semihosting_call_noerrno(semihosting_sys_errno, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user