riscv/debug: Add support for steppoint

Steppoint can be implemented by icount(instruction count)
from RISC-V debug extension, but it may not implemented in all RISC-V cores.

Unfortunately, the currently supported RISC-V cores do not implement it.

Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
This commit is contained in:
Huang Qi 2024-07-31 15:56:51 +08:00 committed by Xiang Xiao
parent fe45d8aace
commit 6ea3bc1217

View File

@ -78,12 +78,12 @@
* Private Type * Private Type
****************************************************************************/ ****************************************************************************/
/* Trigger Match Control, from version 0.13.2. /* Represent the tdata1 register, from the RISC-V Debug Specification 0.13.2
* Read https://riscv.org/wp-content/uploads/2019/03/riscv-debug-release.pdf * Read https://riscv.org/wp-content/uploads/2019/03/riscv-debug-release.pdf
* for more information * for more information
*/ */
union mcontrol union tdata1
{ {
uintptr_t reg; uintptr_t reg;
struct struct
@ -109,7 +109,21 @@ union mcontrol
uintptr_t maskmax : 6; uintptr_t maskmax : 6;
uintptr_t dmode : 1; uintptr_t dmode : 1;
uintptr_t type : 4; uintptr_t type : 4;
}; } match;
struct
{
uintptr_t action: 6;
uintptr_t u : 1;
uintptr_t s : 1;
uintptr_t reserved0 : 1;
uintptr_t m : 1;
uintptr_t count : 14;
uintptr_t hit : 1;
uintptr_t reserved1 : sizeof(uintptr_t) * 8 - 30;
uintptr_t dmode : 1;
uintptr_t type : 4;
} icount;
}; };
struct riscv_debug_trigger struct riscv_debug_trigger
@ -190,7 +204,7 @@ static int riscv_debug_handler(int irq, void *context, void *arg)
static int riscv_debug_init(void) static int riscv_debug_init(void)
{ {
union mcontrol mc; union tdata1 reg;
/* Attach the debug exception handler */ /* Attach the debug exception handler */
@ -223,22 +237,23 @@ static int riscv_debug_init(void)
WRITE_CSR(CSR_TSELECT, 0); WRITE_CSR(CSR_TSELECT, 0);
mc.reg = READ_CSR(CSR_TDATA1); reg.reg = READ_CSR(CSR_TDATA1);
/* REVISIT: NAPOT match is supported and tested on /* REVISIT: NAPOT match is supported and tested on
* QEMU and ESP32C3, prefer to use it. * QEMU and ESP32C3, prefer to use it.
*/ */
mc.match = MATCH_TYPE_TOPBITS; reg.match.type = TRIGGER_TYPE_ADDRESS_DATA;
reg.match.match = MATCH_TYPE_TOPBITS;
/* Write it to tdata1 and read back /* Write it to tdata1 and read back
* to check if the NAPOT is supported * to check if the NAPOT is supported
*/ */
WRITE_CSR(CSR_TDATA1, mc.reg); WRITE_CSR(CSR_TDATA1, reg.reg);
mc.reg = READ_CSR(CSR_TDATA1); reg.reg = READ_CSR(CSR_TDATA1);
if (mc.match == MATCH_TYPE_TOPBITS) if (reg.match.match == MATCH_TYPE_TOPBITS)
{ {
g_support_napot = true; g_support_napot = true;
} }
@ -256,6 +271,74 @@ static int riscv_debug_init(void)
return 0; return 0;
} }
/****************************************************************************
* Name: riscv_debug_setup_match
****************************************************************************/
static uintptr_t riscv_debug_setup_match(int type, void *addr, size_t size)
{
union tdata1 reg;
uintptr_t addr_napot;
if (type == DEBUGPOINT_BREAKPOINT)
{
reg.match.execute = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_RO)
{
reg.match.load = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_WO)
{
reg.match.store = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_RW)
{
reg.match.load = 1;
reg.match.store = 1;
}
reg.match.type = TRIGGER_TYPE_ADDRESS_DATA;
reg.match.m = 1;
reg.match.u = 1;
reg.match.hit = 0;
reg.match.dmode = DMODE_TYPE_BOTH;
reg.match.action = ACTION_TYPE_EXCEPTION;
/* From RISC-V Debug Specification:
* tdata1(mcontrol) match = 0 : Exact byte match
*
* tdata1(mcontrol) match = 1 : NAPOT (Naturally Aligned Power-Of-Two):
*
* Examples for understanding how to calculate match pattern to tdata2:
*
* nnnn...nnnnn 1-byte Exact byte match
* nnnn...nnnn0 2-byte NAPOT range
* nnnn...nnn01 4-byte NAPOT range
* nnnn...nn011 8-byte NAPOT range
* nnnn...n0111 16-byte NAPOT range
* nnnn...01111 32-byte NAPOT range
* ...
* n011...11111 2^31 byte NAPOT range
* where n are bits from original address
*/
if (size > 1 && g_support_napot)
{
reg.match.match = MATCH_TYPE_TOPBITS;
addr_napot = ((uintptr_t)addr & ~(size - 1)) |
((size - 1) >> 1);
WRITE_CSR(CSR_TDATA2, addr_napot);
}
else
{
reg.match.match = MATCH_TYPE_EQUAL;
WRITE_CSR(CSR_TDATA2, (uintptr_t)addr);
}
return reg.reg;
}
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@ -268,9 +351,8 @@ int up_debugpoint_add(int type, void *addr, size_t size,
debug_callback_t callback, void *arg) debug_callback_t callback, void *arg)
{ {
int slot; int slot;
union mcontrol mc; union tdata1 reg;
int ret = OK; int ret = OK;
uintptr_t addr_napot;
/* Initialize the debug module if it is not initialized yet */ /* Initialize the debug module if it is not initialized yet */
@ -297,77 +379,31 @@ int up_debugpoint_add(int type, void *addr, size_t size,
WRITE_CSR(CSR_TSELECT, slot); WRITE_CSR(CSR_TSELECT, slot);
/* Fetch the current setting from tdata1 */ switch (type)
mc.reg = READ_CSR(CSR_TDATA1);
/* Configure trigger */
mc.m = 1;
mc.u = 1;
mc.hit = 0;
mc.dmode = DMODE_TYPE_BOTH;
mc.action = ACTION_TYPE_EXCEPTION;
mc.execute = 0;
mc.load = 0;
mc.store = 0;
if (type == DEBUGPOINT_BREAKPOINT)
{ {
mc.execute = 1; case DEBUGPOINT_STEPPOINT:
} /* NOTICE: STEPPOINT implemented but not tested since no such
else if (type == DEBUGPOINT_WATCHPOINT_RO) * hardware that implemented icount is available.
{
mc.load = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_WO)
{
mc.store = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_RW)
{
mc.load = 1;
mc.store = 1;
}
else
{
/* DEBUGPOINT_STEPPOINT is not supported since current test platform
* such as QEMU don't implemented yet.
*/ */
return -ENOTSUP; reg.reg = 0;
} reg.icount.type = TRIGGER_TYPE_ICOUNT;
reg.icount.dmode = DMODE_TYPE_BOTH;
/* From RISC-V Debug Specification: reg.icount.count = 1;
* tdata1(mcontrol) match = 0 : Exact byte match reg.icount.hit = 0;
* reg.icount.m = 1;
* tdata1(mcontrol) match = 1 : NAPOT (Naturally Aligned Power-Of-Two): reg.icount.s = 1;
* reg.icount.u = 1;
* Examples for understanding how to calculate match pattern to tdata2: reg.icount.action = ACTION_TYPE_EXCEPTION;
* break;
* nnnn...nnnnn 1-byte Exact byte match case DEBUGPOINT_BREAKPOINT:
* nnnn...nnnn0 2-byte NAPOT range case DEBUGPOINT_WATCHPOINT_RO:
* nnnn...nnn01 4-byte NAPOT range case DEBUGPOINT_WATCHPOINT_RW:
* nnnn...nn011 8-byte NAPOT range case DEBUGPOINT_WATCHPOINT_WO:
* nnnn...n0111 16-byte NAPOT range reg.reg = riscv_debug_setup_match(type, addr, size);
* nnnn...01111 32-byte NAPOT range break;
* ... default:
* n011...11111 2^31 byte NAPOT range return -EINVAL;
* where n are bits from original address
*/
if (size > 1 && g_support_napot)
{
mc.match = MATCH_TYPE_TOPBITS;
addr_napot = ((uintptr_t)addr & ~(size - 1)) |
((size - 1) >> 1);
WRITE_CSR(CSR_TDATA2, addr_napot);
}
else
{
mc.match = MATCH_TYPE_EQUAL;
WRITE_CSR(CSR_TDATA2, (uintptr_t)addr);
} }
/* Register the callback and arg */ /* Register the callback and arg */
@ -377,7 +413,7 @@ int up_debugpoint_add(int type, void *addr, size_t size,
g_trigger_map[slot].size = size; g_trigger_map[slot].size = size;
g_trigger_map[slot].callback = callback; g_trigger_map[slot].callback = callback;
g_trigger_map[slot].arg = arg; g_trigger_map[slot].arg = arg;
WRITE_CSR(CSR_TDATA1, mc.reg); WRITE_CSR(CSR_TDATA1, reg.reg);
return 0; return 0;
} }