Now that the funny PABT-behaviour is found to be (probably) caused ny debug state, I'd like to exit debug state before return from PABT exception. The ARM v7-A/R ARM says that I should write RRQ to DBGDRCR, but it seems that in Cortex-A7 it's not accessible via CP14.
It should be accessible via memory-mapped registers, and the manual gives the offset, but offset from what?
I understood that the register offset is the register number multiplied by four, so for DBGDRCR the offset would be 36*4 = 144 (0x90).
In the TRM it says:
Table 10-26 Address mapping for debug trace components Address range Componenta 0x00000 - 0x00FFF ROM table 0x01000 - 0x0FFFF Reserved 0x10000 - 0x10FFF CPU 0 Debug 0x11000 - 0x11FFF CPU 0 PMU 0x12000 - 0x12FFF CPU 1 Debug 0x13000 - 0x13FFF CPU 1 PMU 0x14000 - 0x14FFF CPU 2 Debug 0x15000 - 0x15FFF CPU 2 PMU 0x16000 - 0x16FFF CPU 3 debug
Table 10-26 Address mapping for debug trace components
Address range Componenta
0x00000 - 0x00FFF ROM table
0x01000 - 0x0FFFF Reserved
0x10000 - 0x10FFF CPU 0 Debug
0x11000 - 0x11FFF CPU 0 PMU
0x12000 - 0x12FFF CPU 1 Debug
0x13000 - 0x13FFF CPU 1 PMU
0x14000 - 0x14FFF CPU 2 Debug
0x15000 - 0x15FFF CPU 2 PMU
0x16000 - 0x16FFF CPU 3 debug
Are these too offsets from somewhere, or absolute addresses?
Do I write to DBGDRCR by just writing the value into address 0x100090 (core 0, assuming not locked)?
Or do I need to calculate the register address from the contents of DBGDRAR?
And what's the ROM table?
Yeah. The PABT-problem is solved.
The debug state was not entered at all.
I realized that everything worked as long as I used the 'raw write', but as soon as I called the interrupt driven serial I/O, bad things happened.
And I found the cause: when the string has been written into the serial buffer, the tx interrupts are enabled and characters are read from the buffer and written into the tx FIFO (if it wasn't full) to put the ball rolling. It was done in serial_start_tx().
void serial_start_tx() { uint32_t cpsr_store; // no messing with tx interrupt cpsr_store = disable_save_ints(); // re-enable tx interrupts *((volatile uint32_t *)UART0_IMSC) |= (1<<5); (void) serial_tx(); // Clear transmit interrupt - probably not needed //*((volatile uint32_t *)UART0_ICR) = (1<<5); restore_ints(cpsr_store); }
To not mess up with the interrupts (simultaneous reading from the tx buffer by both serial_start_tx and tx interrupt) the IRQs were disabled before starting the transmission and restored after that, but there was a small bug in the restoring:
static inline uint32_t disable_save_ints() { uint32_t status; asm volatile ( "mrs %[var_reg], cpsr\n\t" "dsb\n\t" "cpsid aif\n\t" :[var_reg] "=r" (status):: ); return status; } static inline void restore_ints(uint32_t status) { asm volatile ( "msr cpsr_fsxc, %[var_reg]\n\t" :[var_reg] "=r" (status):: ); }
In the definition I promised that the restote_ints() doesn't return anything...
This solved the problem:
static inline void restore_ints(uint32_t status) { asm volatile ( "msr cpsr_fsxc, %[var_reg]\n\t" ::[var_reg] "r" (status): ); }
The good news is that while debugging the problem, I learned A LOT about the debug HW!
Congratulations on finding this bug. Such bugs are really annoying.
I can recognize this situation; I've been there a lot of times:
The Debuging-code contains error.
-I do not know why this happens so often (probably more often than errors in the actual code); perhaps because the debugging code is usually written (too) quickly ?
My best advice is to keep debug code simple; but of course it won't be any guarantee against bugs.
I think it's because when something simple doesn't seem to work, you just add little something to check... and that's when such bugs get introduced...
Mine was obviously a copy-paste - didn't pay too much attention... and it bit me.