which register stores the address of instruction which caused an exception(eg DataAbort)
is it always lr-4 (in 32) or there is a specific register
in powerpc there is but in arm I dont know
For Cortex-A and Cortex-R, at entry to the Data Abort handler the LR of ABT mode will contain the address of the offending instruction +8 (no matter whether the offending instruction was in ARM state or Thumb state).
See section B1.8.3 (especially Table B1-7) of the ARM ARM for details.
Related
Starting up a STM32 i try to allocate memory for a struture pointed to by a pointer.
TLxbEvents *LxbEvents
memset((void*)LxbEvents, 0, sizeof(TLxbEvents));
Looking into the disassembly, it crashes always on the line
STMCS r0!,{r2-r3,r12,lr}
I could not find a document describing the STMCS instruction nether on ARM website or Google or elsewhere...
The registers at that point are
r0 0x2000D694
r2 0x00000000
r3 0x00000000
r12 0x00000000
lr 0x00000000
I tried to move the call to another routine, without any changes, checked the alignment and that also seems to be okay. Everytime the program runs into that line it crashes with a HardFault and according to some debug variables, it is caused by a watchdog reset, what i do not believe...
What does this line do and has someone an idea, what is causing the hard fault?
STMCS is an ARM instruction (base instruction is STM and CS is the conditional instruction suffix) It seems you are compiling your code in ARM mode, but STM32 is a Cortex-M core and only supports Thumb-2 instruction set variant. Double-check you build settings and compilation switches.
I'm working on a program that runs under Xilinx's QEMU builds. This question is tagged zynq, but I'm actually targeting a Zynq MP in QEMU, specifically the Cortex-R5.
My background is in microcontrollers and Cortex-M, so this is a big jump and there are a lot of features (MPU, AXI, extra caches) that I'm not accustomed to. I'm writing a driver that is accessing peripheral registers in one of the hard peripherals (in the low power domain). I've written a function, reginsert to simplify accessing bits in these registers.
void reginsert(uint32_t base, uint32_t offset,
uint32_t mask, uint32_t data)
{
uint32_t volatile * reg = (uint32_t volatile *)(base + offset);
uint32_t regdata;
/* This section actually happens in a critical section for atomicity,
* but I've trimmed that out for brevity */
regdata = *reg;
*reg = (regdata & ~mask) | data;
/* End of critical section */
}
Not a very exciting function at all. To me, it's also straightforward.
I call the function like so:
/* Magic number provided by Xilinx, I don't actually plan to keep it as such a
* magical number, but for now while trying things to debug. */
reginsert(XPAR_PSU_UART_0_BASEADDR, XUARTPS_CR_OFFSET, 0x3C, 0);
Additional information:
#define XPART_PSU_UART_0_BASEADDR 0xFF000000
#define XUARTPS_CR_OFFSET 0x00000000
In stepping through the code, I find that this function correctly calculates the address for the register as 0xFF000000, and I can see this within the Xilinx SDK debugger (gdb based) when viewing the value for reg. I can also see, stepping through the assembly that the register r3 is loaded with the proper address.
When execution reaches:
regdata = *reg;
The disassembly has:
ldr r3, [r3]
And I get 0x00000000 in r3. If I use the memory view, I can see that 0xFF000000 has a value of 0x00000114. If I look in the variable view, I can see that *reg has the same value (0x00000114). If I step to the line where the local copy is masked and then written back, I end up at the instruction:
str r2, [r3] ; r3 has been reloaded with the register address,
; and r2 contains the read-modify-write data.
Stepping past this, I see that the value of 0x00000000 (though not the correct value) should have been written, but the value does not actually change (via memory or variable view). In either of these views, I can manually change the value and it writes correctly.
I don't know how thoroughly the bus architecture of the Zynq MP is being emulated in this build of QEMU, but the silicon has separate memory ports for debug access and processor access, and does not access memory through the CPU, so there is a possibility for things to be mapped differently. However, the Xilinx demo code does appropriately perform the same actions.
So, a summary of what I've tried:
Verify disassembly should load/store from register location.
Step through instructions to verify CPU registers have the correct value to access peripheral register.
Accessed memory and variable directly via debugger.
Verify with Xilinx code that QEMU emulates peripheral.
Checked MPU settings (the region is set to non-shared and no restrictions on access)
I don't know what else I should try, and I'm basically stumped. It appears that QEMU, at least through the gdb interface, properly simulates the peripheral register, so I would think it would be fairly straightforward for the CPU to read/write. I appreciate any help, pointers, tips, etc. There's a lot going on that is new to me, including using QEMU as a development platform.
I am currently debugging a hard fault trap which turned out to be a precise data bus error on a STM32F205 Cortex-M3 processor, using Keil uVision. Due to a lengthy debugging and googling process I found the assembly instruction that caused the trap. Now I am looking for a way to avoid this lengthy process next time a trap occurs.
In the application note 209 by Keil it says:
PRECISEERR: Precise data bus error:
0 = no precise data bus error
1 = a data bus error has occurred, and the PC value stacked for the exception return points to the instruction that caused the fault. When the processor sets this bit it writes the faulting address to SCB->BFAR
and also this:
An exception saves the state of registers R0-R3, R12, PC & LR either the Main Stack or the Process Stack (depends on the stack in use when the exception occurred).
The last quote I am interpreting as such that there should be 7 registers plus the respective stack. When I look up my SP address in the memory I see the address that caused the error at an address 10 words higher than the stack pointer address.
My questions are:
Is the address of the instruction that caused the trap always saved 10 words higher than the current stack pointer? And could you please point out a document where I can read up on how and why this is?
Is there another register that would contain this address as well?
As you said, exceptions (or interrupts) on ARM Cortex-M3 will automatically stack some registers, namely :
Program Counter (PC)
Processor Status Register (xPSR)
r0-r3
r12
Link Register (LR).
For a total of 8 registers (reference : Cortex™-M3 Technical Reference Manual, Chapter 5.5.1).
Now, if you write your exception handler in a language other than assembly, the compiler may stack additional registers, and you can't be sure how many.
One simple solution is to add a small code before the real handler, to pass the address of the auto-stacked registers :
void myRealHandler( void * stack );
void handler(void) {
asm volatile("mov r0, sp");
asm volatile("b myRealHandler");
}
The register BFAR is specific to bus faults. It will contain the faulty address, not the address of the faulty instruction. For example, if there was an error reading an integer at address 0x30005632, BFAR will be set to 0x30005632.
The precise stack location of the return address depends on how much stack the interrupt handler requires. If you look at the disassembly of your HardFault_Handler, you should be able to see how much data is stored on the stack / how many registers are pushed in addition to the registers pushed by the hardware interrupt machinery (R0-R3, R12, PC, LR & PSR)
I found this to be a pretty good idea on how to debug Hard Faults, though it requires a bit of inline assembly.
How does the R15 of ARM differ from the general PC of a CPU?
Both of them are program counters only. What is the difference?
ARM's PC is more similar to a regular register with some restrictions than x86's IP is similar to a regular register.
Considering general PC is an Intel x86 based CPU, in x86's case you can't manipulate PC (Instruction pointer) directly but it is updated implicitly by provided control flow instructions.
In ARM's case historically Program Counter (PC), mapped as register at index 15 (16th register) can be manipulated directly via arithmetic instructions. For example you can add 16 to PC which would alter flow of instruction stream similar to a 16-byte forward jump instruction.
The ARM PC maybe more of a general register than most CPUs, but it is still very special. The traditional simple arithmetic instructions can use the PC as an input argument in many cases. Here it functions as a pointer or array base. It can also be used as the output for control transfer with these instructions. As a read-only value, it is useful for calculating return values in a PC-independent way. It is also useful to use as a constant table look-up in near-by code. For these cases, the PC is very much like a regular register. This is probably more common on many RISC CPUs as opposed to a CISC ISA.
However, when the PC is used as a destination (lvalue or updated and written), the behavior is often non-standard. Some examples of special cases (for some ARM architechure versions) for R15/PC are,
adcs - copies SPSR to CPSR
adds - copies SPSR to CPSR
ands - copies SPSR to CPSR
bics - copies SPSR to CPSR
bx r15 - highly discourage or not supported.
clz r15 - not supported.
mcr pXX, xx, r15,... - unpredictable
etc.
In most cases, using the PC as a destination of an instruction will have some special case. Especially, the use of the S (normally to set conditions codes) can be used to return from an exception. This might be used as some sort of veneer when returning from an exception or just a direct return. In some cases, the meaning of the instruction might change completely. For instance, ldm sp, {r0-r15}^ and ldm sp, {r0-r14}^ use different register banks; the first will load the registers according to the mode in the SPSR; whereas the 2nd will load the register to user mode.
For load/store, atomics, mode manipulation, co-processor and complex arithmetic (64 bit multiplies, etc) instructions, the PC is often unsupported or has a different meaning; the different meaning is often a mechanism for handling exceptions for system level code.
I am writing some bare metal code for the Raspberry Pi and am getting an unexpected warning from the ARM cross assembler on Windows. The instructions causing the warnings were:
stmdb sp!,{r0-r14}^
and
ldmia sp!,{r0-r14}^
The warning is:
Warning: writeback of base register is UNPREDICTABLE
I can sort of understand this as although the '^' modifier tells the processor to store the user mode copies of the registers, it doesn't know what mode the processor will be in when the instruction is executed and there doesn't appear to be a way to tell it. I was a little more concerned to get the same warning for:
stmdb sp!,{r0-r9,sl,fp,ip,lr}^
and:
ldmia sp!,{r0-r9,sl,fp,ip,lr}^
despite the fact that I am explicitly not storing ANY sp register.
My concern is that, although I used to do a lot of assembler coding about 15 years ago, ARM code is new to me and I may be misunderstanding something! Also, if I can safely ignore the warnings, is there any way to suppress them?
The ARM Architecture Reference Manual says that writeback is not allowed in LDM/SMT of user registers. It is allowed in the exception return case, where pc is in the register list.
LDM (exception return)
LDM{<amode>}<c> <Rn>{!},<registers_with_pc>^
LDM (user registers)
LDM{<amode>}<c> <Rn>,<registers_without_pc>^
The term "writeback" refers not to the presence or absence of SP in the register list, but to the ! symbol which means the instruction is supposed to update the SP value with the end of transfer area address. The base register (SP) value will be used from the current mode, not the User mode, so you can still load or store user-mode SP value into your stack. From the ARM ARM B9.3.6 LDM (User registers):
In a PL1 mode other than System mode, Load Multiple (User registers)
loads multiple User mode registers from consecutive memory locations
using an address from a base register. The registers loaded cannot
include the PC. The processor reads the base register value normally,
using the current mode to determine the correct Banked version of the
register. This instruction cannot writeback to the base register.
The encoding diagram reflects this by specifying the bit 21 (W, writeback) as '(0)' which means that the result is unpredictable if the bit is not 0.
So the solution is just to not specify the ! and decrement or increment SP manually if necessary.