mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
arch/tricore: Add hardware debug breakpoint and watchpoint support.
Implement hardware breakpoint and watchpoint support for TriCore architecture using the Trigger Event (TREVT) registers. This enables the standard NuttX debugpoint API (up_debugpoint_add/remove) on TriCore processors, supporting read/write/execute watchpoints and breakpoints via the on-chip debug unit. Signed-off-by: zhangyu117 <zhangyu117@xiaomi.com>
This commit is contained in:
parent
49d7959b96
commit
c858f3cc44
6 changed files with 430 additions and 0 deletions
|
|
@ -24,6 +24,7 @@ endchoice # Tricore Toolchain Selection
|
|||
|
||||
config ARCH_TC1V6
|
||||
bool
|
||||
select ARCH_HAVE_DEBUG
|
||||
select ARCH_HAVE_MPU
|
||||
select ARCH_HAVE_IRQTRIGGER
|
||||
select ARCH_HAVE_PERF_EVENTS
|
||||
|
|
@ -38,6 +39,7 @@ config ARCH_TC1V6
|
|||
config ARCH_TC1V8
|
||||
bool
|
||||
select ARCH_DCACHE
|
||||
select ARCH_HAVE_DEBUG
|
||||
select ARCH_ICACHE
|
||||
select ARCH_HAVE_MPU
|
||||
select ARCH_HAVE_IRQTRIGGER
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ if(CONFIG_ARCH_HAVE_FPU)
|
|||
list(APPEND SRCS tricore_fpu.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ARCH_HAVE_DEBUG)
|
||||
list(APPEND SRCS tricore_hwdebug.c)
|
||||
endif()
|
||||
|
||||
set(IFXFLAGS -DIFX_CFG_EXTEND_TRAP_HOOKS -DIFX_USE_SW_MANAGED_INT)
|
||||
|
||||
target_sources(arch PRIVATE ${SRCS})
|
||||
|
|
|
|||
|
|
@ -66,5 +66,9 @@ ifeq ($(CONFIG_ARCH_HAVE_FPU),y)
|
|||
CMN_CSRCS += tricore_fpu.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_HAVE_DEBUG),y)
|
||||
CMN_CSRCS += tricore_hwdebug.c
|
||||
endif
|
||||
|
||||
CFLAGS += -DIFX_CFG_EXTEND_TRAP_HOOKS
|
||||
CFLAGS += -DIFX_USE_SW_MANAGED_INT
|
||||
|
|
|
|||
412
arch/tricore/src/common/tricore_hwdebug.c
Normal file
412
arch/tricore/src/common/tricore_hwdebug.c
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
/****************************************************************************
|
||||
* arch/tricore/src/common/tricore_hwdebug.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <arch/irq.h>
|
||||
|
||||
#include <IfxCpu_bf.h>
|
||||
#include <IfxCbs_reg.h>
|
||||
|
||||
#include "tricore_internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Watchpoint and breakpoint share a set of trigger events */
|
||||
|
||||
#define TREVT_DEBUG_NUM 8
|
||||
#define TREVT_DEBUG_REGS 4
|
||||
#define TREVT_DEBUG_MATCH_TRAPMODE 0x03
|
||||
#define TREVT_DEBUG_EVTSRC_BASE 0x10
|
||||
#ifdef CONFIG_ARCH_TC1V8
|
||||
# define IFX_CPU_TR_EVT_BBM_OFF IFX_CPU_TREVT_BBM_OFF
|
||||
# define IFX_CPU_TR_EVT_TYP_OFF IFX_CPU_TREVT_TYP_OFF
|
||||
# define IFX_CPU_TR_EVT_AST_OFF IFX_CPU_TREVT_AST_OFF
|
||||
# define IFX_CPU_TR_EVT_ALD_OFF IFX_CPU_TREVT_ALD_OFF
|
||||
#endif
|
||||
|
||||
#define TREVT_DEBUG_BBM (1 << IFX_CPU_TR_EVT_BBM_OFF)
|
||||
#define TREVT_DEBUG_WP (0 << IFX_CPU_TR_EVT_TYP_OFF)
|
||||
#define TREVT_DEBUG_BP (1 << IFX_CPU_TR_EVT_TYP_OFF)
|
||||
#define TREVT_DEBUG_WP_AST (1 << IFX_CPU_TR_EVT_AST_OFF)
|
||||
#define TREVT_DEBUG_WP_ALD (1 << IFX_CPU_TR_EVT_ALD_OFF)
|
||||
#define TREVT_DEBUG_WP_ASTLD (TREVT_DEBUG_WP_AST | TREVT_DEBUG_WP_ALD)
|
||||
|
||||
/* Register TREVT[2:0] have difference between tc3xx and tc4xx */
|
||||
|
||||
#ifdef CONFIG_ARCH_TC1V6
|
||||
# define TREVT_CFG_REG_EN_MASK IFX_CPU_TR_EVT_EVTA_MSK
|
||||
# define TREVT_CFG_REG_EN_VALUE TREVT_DEBUG_MATCH_TRAPMODE
|
||||
#elif defined(CONFIG_ARCH_TC1V8)
|
||||
# define TREVT_CFG_REG_EN_MASK IFX_CPU_TREVT_EN_MSK
|
||||
# define TREVT_CFG_REG_EN_VALUE 0x01
|
||||
#endif
|
||||
|
||||
#define TREVT_SET_CASE(reg, n, val) \
|
||||
case n: __mtcr(CPU_TR##n##_##reg, val); break;
|
||||
#define TREVT_GET_CASE(reg, n, val) \
|
||||
case n: val = __mfcr(CPU_TR##n##_##reg); break;
|
||||
|
||||
#define TREVT_GET_CFG_REG(n) TREVT_GET(EVT, n)
|
||||
#define TREVT_SET_CFG_REG(n, val) TREVT_SET(EVT, n, val)
|
||||
#define TREVT_GET_ADDR_REG(n) TREVT_GET(ADR, n)
|
||||
#define TREVT_SET_ADDR_REG(n, val) TREVT_SET(ADR, n, val)
|
||||
|
||||
#define TREVT_SET(reg, n, val) \
|
||||
({ \
|
||||
switch (n) \
|
||||
{ \
|
||||
TREVT_SET_CASE(reg, 0, val) \
|
||||
TREVT_SET_CASE(reg, 1, val) \
|
||||
TREVT_SET_CASE(reg, 2, val) \
|
||||
TREVT_SET_CASE(reg, 3, val) \
|
||||
TREVT_SET_CASE(reg, 4, val) \
|
||||
TREVT_SET_CASE(reg, 5, val) \
|
||||
TREVT_SET_CASE(reg, 6, val) \
|
||||
TREVT_SET_CASE(reg, 7, val) \
|
||||
} \
|
||||
})
|
||||
|
||||
#define TREVT_GET(reg, n) \
|
||||
({ \
|
||||
uint32_t _val = 0; \
|
||||
switch (n) \
|
||||
{ \
|
||||
TREVT_GET_CASE(reg, 0, _val) \
|
||||
TREVT_GET_CASE(reg, 1, _val) \
|
||||
TREVT_GET_CASE(reg, 2, _val) \
|
||||
TREVT_GET_CASE(reg, 3, _val) \
|
||||
TREVT_GET_CASE(reg, 4, _val) \
|
||||
TREVT_GET_CASE(reg, 5, _val) \
|
||||
TREVT_GET_CASE(reg, 6, _val) \
|
||||
TREVT_GET_CASE(reg, 7, _val) \
|
||||
} \
|
||||
_val; \
|
||||
})
|
||||
|
||||
/****************************************************************************
|
||||
* Private Type
|
||||
****************************************************************************/
|
||||
|
||||
struct tricore_debugpoint_s
|
||||
{
|
||||
int type;
|
||||
void *addr;
|
||||
size_t size;
|
||||
debug_callback_t callback;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct tricore_debug_s
|
||||
{
|
||||
uintptr_t aligned_data(XCPTCONTEXT_SIZE) dcx[TREVT_DEBUG_REGS];
|
||||
struct tricore_debugpoint_s dp[TREVT_DEBUG_NUM];
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static struct tricore_debug_s g_trevt_debug;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function
|
||||
****************************************************************************/
|
||||
|
||||
static uint32_t tricore_convert_type(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DEBUGPOINT_WATCHPOINT_RO:
|
||||
return TREVT_DEBUG_WP | TREVT_DEBUG_WP_ALD;
|
||||
|
||||
case DEBUGPOINT_WATCHPOINT_WO:
|
||||
return TREVT_DEBUG_WP | TREVT_DEBUG_WP_AST;
|
||||
|
||||
case DEBUGPOINT_WATCHPOINT_RW:
|
||||
return TREVT_DEBUG_WP | TREVT_DEBUG_WP_ASTLD;
|
||||
|
||||
case DEBUGPOINT_BREAKPOINT:
|
||||
case DEBUGPOINT_STEPPOINT:
|
||||
default:
|
||||
return TREVT_DEBUG_BP;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tricore_trevt_add
|
||||
*
|
||||
* Description:
|
||||
* Add a watchpoint on the address.
|
||||
*
|
||||
* Input Parameters:
|
||||
* type - The type of the watchpoint
|
||||
* addr - The address to be watched
|
||||
* size - The size of the address to be watched
|
||||
*
|
||||
* Returned Value:
|
||||
* Index in wprs array on success; a negated errno value on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int tricore_trevt_add(int type, uint32_t addr, size_t size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TREVT_DEBUG_NUM; i++)
|
||||
{
|
||||
if (!(TREVT_GET_CFG_REG(i) & TREVT_CFG_REG_EN_MASK))
|
||||
{
|
||||
TREVT_SET_ADDR_REG(i, addr);
|
||||
TREVT_SET_CFG_REG(i, tricore_convert_type(type) |
|
||||
TREVT_CFG_REG_EN_VALUE);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tricore_trevt_remove
|
||||
*
|
||||
* Description:
|
||||
* Remove a trevt on the address.
|
||||
*
|
||||
* Input Parameters:
|
||||
* addr - The address to be debugged.
|
||||
*
|
||||
* Returned Value:
|
||||
* Index of trevt on success; a negated errno value on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int tricore_trevt_remove(uint32_t addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TREVT_DEBUG_NUM; i++)
|
||||
{
|
||||
if (TREVT_GET_ADDR_REG(i) == addr)
|
||||
{
|
||||
TREVT_SET_CFG_REG(i, 0);
|
||||
TREVT_SET_ADDR_REG(i, 0);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tricore_dbgmonitor
|
||||
*
|
||||
* Description:
|
||||
* This is Debug Monitor exception handler. This function is entered when
|
||||
* the processor enters debug mode. The debug monitor handler will handle
|
||||
* debug events, and resume execution.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void tricore_dbgmonitor(void)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"svlcx\n\t"
|
||||
"call tricore_trevt_match\n\t"
|
||||
"rslcx\n\t"
|
||||
"rfm\n\t"
|
||||
);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tricore_trevt_match
|
||||
****************************************************************************/
|
||||
|
||||
void tricore_trevt_match(void)
|
||||
{
|
||||
struct tricore_debugpoint_s *dp;
|
||||
int evtsrc;
|
||||
|
||||
dp = g_trevt_debug.dp;
|
||||
|
||||
evtsrc = ((__mfcr(CPU_DBGSR) >> IFX_CPU_DBGSR_EVTSRC_OFF) &
|
||||
IFX_CPU_DBGSR_EVTSRC_MSK) - TREVT_DEBUG_EVTSRC_BASE;
|
||||
|
||||
dp[evtsrc].callback(dp[evtsrc].type, dp[evtsrc].addr,
|
||||
dp[evtsrc].size, dp[evtsrc].arg);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tricore_init_dbgmonitor
|
||||
*
|
||||
* Description:
|
||||
* This function init the debug monitor exception.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int tricore_init_dbgmonitor(void)
|
||||
{
|
||||
Ifx_CPU_DBGTCR dbgtcr;
|
||||
|
||||
dbgtcr.B.DTA = 0;
|
||||
|
||||
if (!(__mfcr(CPU_DBGSR) & 0x1))
|
||||
{
|
||||
CBS_OEC.U = 0xa1;
|
||||
CBS_OEC.U = 0x5e;
|
||||
CBS_OEC.U = 0xa1;
|
||||
CBS_OEC.U = 0x5e;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_TC1V8
|
||||
Ifx_CPU_DBGCFG dbgcfg;
|
||||
Ifx_CPU_DBGACT dbgact;
|
||||
|
||||
dbgcfg.B.EN = 1;
|
||||
dbgcfg.B.TC = 0;
|
||||
dbgcfg.B.TCP = 0;
|
||||
dbgact.B.EVTA = TREVT_DEBUG_MATCH_TRAPMODE;
|
||||
|
||||
/* Set debug configuration register */
|
||||
|
||||
__mtcr(CPU_DBGCFG, dbgcfg.U);
|
||||
|
||||
/* Set debug action configuration register */
|
||||
|
||||
__mtcr(CPU_DBGACT, dbgact.U);
|
||||
#endif
|
||||
|
||||
/* Set debug trap control register */
|
||||
|
||||
__mtcr(CPU_DBGTCR, dbgtcr.U);
|
||||
|
||||
/* Set trevt trap handler */
|
||||
|
||||
__mtcr(CPU_DMS, (uintptr_t)tricore_dbgmonitor);
|
||||
|
||||
/* Set dcx register */
|
||||
|
||||
__mtcr(CPU_DCX, (uintptr_t)g_trevt_debug.dcx);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: up_debugpoint_add
|
||||
*
|
||||
* Description:
|
||||
* Add a debugpoint.
|
||||
*
|
||||
* Input Parameters:
|
||||
* type - The debugpoint type. optional value:
|
||||
* DEBUGPOINT_WATCHPOINT_RO - Read only watchpoint.
|
||||
* DEBUGPOINT_WATCHPOINT_WO - Write only watchpoint.
|
||||
* DEBUGPOINT_WATCHPOINT_RW - Read and write watchpoint.
|
||||
* DEBUGPOINT_BREAKPOINT - Breakpoint.
|
||||
* DEBUGPOINT_STEPPOINT - Single step.
|
||||
* addr - The address to be debugged.
|
||||
* size - The watchpoint size. only for watchpoint.
|
||||
* callback - The callback function when debugpoint triggered.
|
||||
* if NULL, the debugpoint will be removed.
|
||||
* arg - The argument of callback function.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on success; a negated errno value on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_debugpoint_add(int type, void *addr, size_t size,
|
||||
debug_callback_t callback, void *arg)
|
||||
{
|
||||
struct tricore_debugpoint_s *dp;
|
||||
int ret;
|
||||
|
||||
ret = tricore_trevt_add(type, (uint32_t)addr, size);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
dp = g_trevt_debug.dp;
|
||||
dp[ret].type = type;
|
||||
dp[ret].addr = addr;
|
||||
dp[ret].size = size;
|
||||
dp[ret].callback = callback;
|
||||
dp[ret].arg = arg;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: up_debugpoint_remove
|
||||
*
|
||||
* Description:
|
||||
* Remove a debugpoint.
|
||||
*
|
||||
* Input Parameters:
|
||||
* type - The debugpoint type. optional value:
|
||||
* DEBUGPOINT_WATCHPOINT_RO - Read only watchpoint.
|
||||
* DEBUGPOINT_WATCHPOINT_WO - Write only watchpoint.
|
||||
* DEBUGPOINT_WATCHPOINT_RW - Read and write watchpoint.
|
||||
* DEBUGPOINT_BREAKPOINT - Breakpoint.
|
||||
* DEBUGPOINT_STEPPOINT - Single step.
|
||||
* addr - The address to be debugged.
|
||||
* size - The watchpoint size. only for watchpoint.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on success; a negated errno value on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_debugpoint_remove(int type, void *addr, size_t size)
|
||||
{
|
||||
struct tricore_debugpoint_s *dp;
|
||||
int ret;
|
||||
|
||||
ret = tricore_trevt_remove((uintptr_t)addr);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
dp = g_trevt_debug.dp;
|
||||
dp[ret].type = 0;
|
||||
dp[ret].addr = 0;
|
||||
dp[ret].size = 0;
|
||||
dp[ret].callback = NULL;
|
||||
dp[ret].arg = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
@ -81,4 +81,8 @@ void up_initialize(void)
|
|||
#ifdef USE_SERIALDRIVER
|
||||
tricore_serialinit();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_HAVE_DEBUG
|
||||
tricore_init_dbgmonitor();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,6 +212,10 @@ void tricore_earlyserialinit(void);
|
|||
void tricore_fpuinit(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_HAVE_DEBUG
|
||||
int tricore_init_dbgmonitor(void);
|
||||
#endif
|
||||
|
||||
/* System Timer *************************************************************/
|
||||
|
||||
struct oneshot_lowerhalf_s *
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue