Hello, we have encountered a problem with hardware breakpoint triggering during development of our project. Please look at the following code for set and reset the breakpoint:
void enableBreakpoint(void)
{
extern unsigned int startBkpt;
MEMORY_MAPPED_DEBUG_REGS *regs = get_debug_regs();
_disable_interrupts_();
/* Unlock DBGLAR. */
regs->LOCKACCESS = 0xC5ACCE55;
/* Enable monitor mode. */
regs->DBGDSCR.bits.monitor_mode = 1;
/* Disable the breakpoint being set. */
regs->DBGBCR[5].all = 0;
asm volatile(" dsb\n");
asm volatile(" dmb\n");
asm volatile(" isb\n");
/* startFlush(); */
regs->DBGBVR[5].all = ((unsigned long)&startBkpt) & 0xFFFFFFFC;
regs->DBGBCR[5].all = 7 | (0xF << 5);
asm volatile(" dsb\n");
asm volatile(" dmb\n");
asm volatile(" isb\n");
/* startFlush(); */
asm volatile("startBkpt:\n");
asm volatile(" nop\n");
_enable_interrupts_();
}
void disableBreakpoint(void)
{
MEMORY_MAPPED_DEBUG_REGS *regs = get_debug_regs();
_disable_interrupts_();
/* Disable the breakpoint. */
regs->DBGBCR[5].all = 0;
/* Clear the DBGBVR register. */
regs->DBGBVR[5].all = 0;
asm volatile(" dsb\n");
asm volatile(" dmb\n");
asm volatile(" isb\n");
/* startFlush(); */
_enable_interrupts_();
}
MEMORY_MAPPED_DEBUG_REGS *get_debug_regs(void)
{
UINT32T addr = _read_debug_DBGDRAR_() & 0xFFFFFFF0;
addr += _read_debug_DBGDSAR_() & 0xFFFFFFF0;
return ((MEMORY_MAPPED_DEBUG_REGS *)addr);
}
.def _read_debug_DBGDRAR_
.asmfunc
_read_debug_DBGDRAR_
MRC p14, #0, r0, c1, c0, #0
bx lr
.endasmfunc
.def _read_debug_DBGDSAR_
.asmfunc
_read_debug_DBGDSAR_
MRC p14, #0, r0, c2, c0, #0
bx lr
.endasmfunc
The problem is that the prefetch abort exception is not always triggered even though the breakpoint is set. It looks like it has something to do with the code padding in the executable image. I have found some very strange workaround, which temporarily resolves this problem but I really don't understand why it works.
The workaround is to create separate sections for enableBreakpoint and disableBreakpoint and then align them with padding to 32 bytes. The additional function also has to be created:
extern void startFlush void;
void flushFunction(void)
{
asm volatile(" nop\n");
asm volatile(" nop\n");
asm volatile(" nop\n");
asm volatile("startFlush:\n");
asm volatile(" nop\n");
}
Now, the startFlush (function with just one nop instruction created inside the flushFunction in assembly) must be called in enableBreakpoint and disableBreakpoint functions right after the ISB instructions (it is commented out in the code above). What is really strange is that the startFlush function must be aligned to 32 bytes plus offset 0xC. That's why there is a flushFunction wrapper needed (it is placed in the separate section and also aligned with padding to 32 bytes. The first three nop instructions in the flushFunction are used to create 0xC offset for startFlush).
The cache is not enabled.
Maybe someone encountered the similar problem and knows the resolution?