cm版本已经更新,关于项目的更多说明
见https://code.youkuaiyun.com/wzyy2/gdbstub4rtt/tree/master/readme-zh.txt
移植主要分三个主要的实现和debug模式
ps : CM0的断点设置有所不同,不能运行
Debug Monitor(调试监控器)
CM系列可以脱离JTAG调试的主要基础就是debugmon模式
关于debugmon和halt的区别,见以下图
简单的说debugmon可以按照一般的异常处理,而halt是直接停机内核
关于debugmon和halt debug的办法,见以下两张图
使用 debug monitor的办法就是置位DEMCR寄存器的MON_EN
软件唤起debug monitor的办法是置位DEMCR寄存器的MON_PENDING
base = (unsigned long*)(GDB_DEBUG_REG_BASE + GDB_DEBUG_REG_DEMCR);
/*
* Enable the debug monitor. When enabled, the System handler priority
* register controls its priority level.
* If disabled, then all debug events go
* to Hard fault
*/
*base |= GDB_DEBUG_REG_DEMCR_MON_EN;
//Fall into debug monitor
*base |= GDB_DEBUG_REG_DEMCR_MON_PEND;
1.断点(breakpoint)
CM断点的主要实现是使用Flashpatch and Breakpoint(FPB):
.A set of address matching tags, that reroute accesses into flash to a special part of SRAM. This permits patching flash locations for breakpointing and quick fixes orchanges.FPB可以被编程用来实现断点功能,他有一个比较器,当地址,有两个选择,一是将该地址指向指定的ram地址,二是在该地址设置BKPT指令,因为我们要用的是断点功能,自然是设置断点了
FPB的使用有两个使能位,一个是总开关
// Enable the FPB-FLASH PATCH BREAKPOINT
base = (unsigned long*)(GDB_FPB_REG_BASE + GDB_FPB_REG_CTRL);
*base |= GDB_FPB_REG_CTRL_KEY | GDB_FPB_REG_CTRL_ENABLE ;
一个是每个比较器的开关
base = (unsigned long*)(GDB_FPB_REG_BASE + GDB_FPB_REG_COMP + i * 4);
*base = GDB_FPB_REG_COMP_ADDR & ((unsigned long)(breakinfo[i].addr));
if (breakinfo[i].addr & 2)
*base |= (1UL << 31); //set BKPT on upper halfword
else
*base |= (1UL << 30); //set BKPT on lower halfword,
*base |= GDB_FPB_REG_COMP_ENABLE ;
设置断点的办法如上,把要设置地址的28-2位写入FPB_COMP的28-2位,要在高半字设置断点需要置位BIT31,低半字则置位BIT30
更多功能及寄存器说明见Cortex-M3 Technical Reference Manual 11.4 FPB
2.观察点(watchpoint)
CM观察点的主要实现是使用Data Watchpoint and Trace(DWT)
待更
/**
* gdb_breakpoint - generate a breadk
* It is used to sync up with a debugger and stop progarm
*/
void gdb_breakpoint()
{
volatile unsigned long *base;
// Enable the FPB-FLASH PATCH BREAKPOINT
base = (unsigned long*)(GDB_FPB_REG_BASE + GDB_FPB_REG_CTRL);
*base |= GDB_FPB_REG_CTRL_KEY | GDB_FPB_REG_CTRL_ENABLE ;
base = (unsigned long*)(GDB_DEBUG_REG_BASE + GDB_DEBUG_REG_DEMCR);
/*
* Enable the debug monitor. When enabled, the System handler priority
* register controls its priority level.
* If disabled, then all debug events go
* to Hard fault
*/
*base |= GDB_DEBUG_REG_DEMCR_MON_EN;
// Enable DWT
*base |= GDB_DEBUG_REG_DEMCR_TRCENA ;
//Fall into debug monitor
*base |= GDB_DEBUG_REG_DEMCR_MON_PEND;
}
// Install the watchpoint
for (i = 0; i < HWP_NUM; i++) {
if (watchinfo[i].enabled) {
base = (unsigned long*)(GDB_DWT_REG_BASE + GDB_DWT_REG_COMP + i * 12);
*base = watchinfo[i].addr;
base = (unsigned long*)(GDB_DWT_REG_BASE + GDB_DWT_REG_MASK + i * 12);
while (watchinfo[i].len >> num) {
num++;
}
*base = num - 1; //DWT matching is performed as:(ADDR & (~0 << MASK)) == COMP
base = (unsigned long*)(GDB_DWT_REG_BASE + GDB_DWT_REG_FUNCTION + i * 12);
if (watchinfo[i].type == BP_WRITE_WATCHPOINT)
*base = (*base & ~GDB_DWT_REG_FUNCTION_FUC) + 0x05;
else if (watchinfo[i].type == BP_READ_WATCHPOINT)
*base = (*base & ~GDB_DWT_REG_FUNCTION_FUC) + 0x06;
else if (watchinfo[i].type == BP_ACCESS_WATCHPOINT)
*base = (*base & ~GDB_DWT_REG_FUNCTION_FUC) + 0x07;
}
}
更多功能及寄存器说明见Cortex-M3 Technical Reference Manual 11.5 DWT
3.单步(singlestep)
单步主要实现是DEMCR寄存器的MON_STEP位
.When MON_EN= 1, this steps the core. When MON_EN= 0, this bitis ignored. This is the equivalent to C_STEP. Interrupts are only stepped according to the priority of the monitor and settings of PRIMASK, FAULTMASK, or BASEPRI/*we need to block all pending interrupts by swtting basepri
* before doing the steo
*/
void gdb_single_step()
{
volatile unsigned long *base;
//mask all interrupts
single_step_basepri = regs->basepri;
regs->basepri = GDB_CORTEXM_PRIORITY_MAX;
//When MON_EN = 1, this steps the core
base = (unsigned long*)(GDB_DEBUG_REG_BASE + GDB_DEBUG_REG_DEMCR);
*base |= GDB_DEBUG_REG_DEMCR_MON_STEP;
/* Clear any bits set in DFSR*/
base = (unsigned long*)(GDB_NVIC_REG_BASE + GDB_NVIC_REG_DFSR);
*base = 0xffffffff;
}
void gdb_clear_single_step()
{
volatile unsigned long *base;
regs->basepri = single_step_basepri;
/*clear single step*/
base = (unsigned long*)(GDB_DEBUG_REG_BASE + GDB_DEBUG_REG_DEMCR);
*base &= ~GDB_DEBUG_REG_DEMCR_MON_STEP;
// Clear any bits set in DFSR
base = (unsigned long*)(GDB_NVIC_REG_BASE + GDB_NVIC_REG_DFSR);
*base = 0xffffffff;
这里有很重要的一点就是我们要通过basepri来屏蔽中断
之所以要屏蔽中断是因为设置单步的目的的是为了到达当前函数的下一行
而不是在中断里单步
如果不屏蔽的话,调试模式返回后会先去执行中断
然后我们就要淹没在无止境的systick里出不来了
还有一个地方就是GDB_CORTEXM_PRIORITY_MAX
#ifndef GDB_CORTEXM_PRIORITY_MAX
#define GDB_CORTEXM_PRIORITY_MAX (1 << 6)
#endif
如果优先级分组有更改要更改这个宏
我们设置basepri的目的是屏蔽低优先级的中断,同时不屏蔽debugmon
debugmon的默认优先级是0,所以至少要保证GDB_CORTEXM_PRIORITY_MAX的抢占优先级要大于0
不然可能出现单步之后无法进入调试模式的情况
寄存器说明见Cortex-M3 Technical Reference Manual 10.2.4 Debug Exception and Monitor Control Register