I’m using an at91sam7s256 combined with the yagarto/eclipse gnu toolchain, with
an ISR setup based on the PIT. The vector table in the startup code is as follows:
LDR pc, ResetAddr /* Reset */
LDR pc, UndefAddr /* Undefined instruction */
LDR pc, SWIAddr /* Software interrupt */
LDR pc, PAbortAddr /* Prefetch abort */
LDR pc, DAbortAddr /* Data abort */
LDR pc, ReservedAddr /* Reserved */
LDR pc, [PC,#-0xF20]
LDR pc, [PC,#-0xF20]
After the PIT increments to the appropriate value, the ISR routine is called. Whether or not I have code in the ISR, the program then branches to the data abort handler. I understand in programs like uVision it is necessary to use the __irq keyword before the ISR. The examples I have seen with GNU don’t seem to use a keyword such as that, but I believe this is related to why I’m getting sent to the data abort handler.
Any input would be greatly appreciated! I’m at the end of my list of things to try here, and am really unsure of whats going wrong/how to fix the problem.
Thanks in advance!
The reason I was getting an exception I believe was because I wasn’t setting the AIC_EOICR register. In any event… when I use LDR pc,[PC,#-0xF20] in the vector table for the IRQ address, when I come out of the ISR I noticed the ARM is still in IRQ mode and the I flag has been reset again (CPSR = 0x600000d2). Inside the ISR I can put it back into system mode as it was before (whether or not this is necessary, I’m not entirely sure), however I am unable to clear the I flag again (clearing it has no effect in the ISR).
Is it customary to manually set this flag each time one exits and ISR?
evmiller:
Is it customary to manually set this flag each time one exits and ISR?
At least in the end of my C-language IRQ handler (for AT91SAM7X256) I write zero to that register and then branch back to asm code (where I will restore CPSR and PC). Also datasheet says:
Datasheet for AT91SAM7X512/256/128:
If an interrupt condition happens (or is pending) during the interrupt treatment in progress, it is delayed until the software indicates to the AIC the end of the current service by writing the AIC_EOICR (End of Interrupt Command Register). The write of AIC_EOICR is the exit point of the interrupt handling.
Maybe I’m misunderstanding the fundamental idea of the whats going on here… I was under the impression that since I have LDR pc, [PC,#-0xF20] in the vector table, rather than a branch to a handler, that when the interrupt condition occurs the PC grabs the ISR vector from the IVR. I thought that because of this, I am not using a specific handler, and the code related to the interrupt is solely placed in the ISR. Is this not what is happening here?
You are correct, in auto-vectoring the value of AIC_IVR is read using the instruction you mentioned but if you are not using any ready made macros (for example IRQ_ENTRY, IRQ_EXIT) you still need to manually mark the EOI by writing something to AIC_EOICR. More information about auto-vectoring is available in Atmel’t document [Interrupt Management: Auto-vectoring and Prioritization.](http://www.atmel.com/dyn/resources/prod_documents/DOC1168.PDF)
evmiller:
A more alarming problem is that I am unable to declare a variable in the ISR. If I do anything other than, say, modify a register, the program throws an exception and moves to the data abort handler.
Just a guess but have you initialized IRQ mode stack? If the stack points to wrong location then storing something to a variable means that you are actually writing data to some random location (maybe to address 0), that might lead to data abort.
This would be taking place in the startup code, I presume? I have a very loose grasp on the specifics here, but I believe it is initialized in the following section of code (I will also upload my startup and low level init files I have been using):
/* Initialize stack pointers for all ARM modes */
MSR CPSR_c,#(IRQ_MODE | I_BIT | F_BIT)
LDR sp,=__irq_stack_top__ /* set the IRQ stack pointer */
MSR CPSR_c,#(FIQ_MODE | I_BIT | F_BIT)
LDR sp,=__fiq_stack_top__ /* set the FIQ stack pointer */
MSR CPSR_c,#(SVC_MODE | I_BIT | F_BIT)
LDR sp,=__svc_stack_top__ /* set the SVC stack pointer */
MSR CPSR_c,#(ABT_MODE | I_BIT | F_BIT)
LDR sp,=__abt_stack_top__ /* set the ABT stack pointer */
MSR CPSR_c,#(UND_MODE | I_BIT | F_BIT)
LDR sp,=__und_stack_top__ /* set the UND stack pointer */
MSR CPSR_c,#(SYS_MODE | I_BIT | F_BIT)
LDR sp,=__c_stack_top__ /* set the C stack pointer */
When I step through the code, the stack top locations are all the same. I’m not sure if this matters.
I quickly checked your code and that piece of code looks ok. I assume that you have main() function where your own code is and when the code branches to main() then stacks should be fine.
evmiller:
When I step through the code, the stack top locations are all the same. I’m not sure if this matters.
I think they should not be same (devices without MMU), if in SVC/SYS mode you have put variables to stack and IRQ happens (with the same stack address) then you start to overwrite values.
Maybe best way to proceed would be to try some ready made examples for AT91SAM7S256 (for example from http://gandalf.arubi.uni-kl.de/avr_proj … _at91.html) to see that those will work with your tool chain and once you have verified that then start to compare what you have different in your own project (for example linker scripts, makefile, RAM remapping etc.).