Hi
Its my understanding that the Reset vector is at a higher priority than NMI and that the NMI should pend to be executed on return from the reset handler. [Sec 7.4 p247,248 Definitive Guide to ARM cortex..]
However when I run in debug starting at the reset vector (i.e. not from main) and I force the NMI line low in hardware before running the code. The NMI handler interrupts immediately. This has left me confused.... is this behaviour because the code is running from the debugger or is my understanding of the interrupt priority scheme incorrect?
Background:-
I have a hardware problem that asserts NMI at power-up. Whilst waiting for the hardware fix I thought I could fix this in software by controlling the code that runs in the NMI Handler using the variable NMI_armed
The problem with this approach is that it relies on the ROM COPY function to execute without being pre-empted. The rom
copy is performed by the DLIB function __iar_data_init3 and is called from within the reset handler. It was my assumption, that because this code is in the reset handler that the NMI couldn't pre-empt it.
Code snippet from the IAR supplied startup_MK24F12.s
__vector_table
DCD sfe(CSTACK)
DCD Reset_Handler ;Reset Handler (priority -3)
DCD NMI_Handler ;NMI Handler (priority -2)
DCD HardFault_Handler ;Hard Fault Handler (priority -1)
DCD MemManage_Handler ;MPU Fault Handler (programmable)
DCD BusFault_Handler ;Bus Fault Handler (programmable)
DCD UsageFault_Handler ;Usage Fault Handler (programmable)
THUMB
PUBWEAK Reset_Handler
SECTION .text:CODE:REORDER:NOROOT(2)
Reset_Handler
CPSID I ; Mask interrupts PRIMASK
LDR R0, =_NVIC_ICER0 ; Disable interrupts and clear pending flags
LDR R1, =_NVIC_ICPR0
LDR R2, =0xFFFFFFFF
MOV R3, #8
_irq_clear
CBZ R3, _irq_clear_end
STR R2, [R0], #4 ; NVIC_ICERx - clear enable IRQ register
STR R2, [R1], #4 ; NVIC_ICPRx - clear pending IRQ register
SUB R3, R3, #1
B _irq_clear
_irq_clear_end
LDR R0, =SystemInit
BLX R0
LDR R0, =init_data_bss
BLX R0
CPSIE I ; Unmask interrupts
LDR R0, =__iar_program_start ; calls __iar_data_init3 before entering main()
BX R0
PUBWEAK NMI_Handler
SECTION .text:CODE:REORDER:NOROOT(1)
NMI_Handler ;Implemented in 'C' in prw_fail.c
B .
In my module prw_fail.c
EXPORT VOID NMI_Handler(VOID)
{
if(NMI_armed) // NMI_armed controlled in main() as I only want to be true on power down
{
//do stuff
}
}
Thanks
已解决! 转到解答。
Hi Dave
I think that the confusion is that the reset context terminates on the very first instruction of code that is executed. That means that the system reset will cause the stack pointer to be loaded with the value at address 0x00000000 (sfe(CSTACK) in your case) and then will cause the program counter to be loaded with the address of the first instruction to be executed (Reset_Handler in your case). Now code start executing the processor is no longer in the reset context but the supervisor state and NMI can occur.
Since NMI cannot be masked you will get its vector (NMI_Handler in your case) taken, when the NMI line is asserted, before any code is executed - the line
CPSID I | ; Mask interrupts PRIMASK |
WILL NOT be executed until the NMI vector has been handled in this case so it is importent that:
1) The stack pointer has a valid address so that the interrupt vector can push the NMI interrupt context to the stack
2) The NMI handler is in Flash so that it can execute without further configuration
3) The NMI handler code does anything necessary to remove the pending NMI so that the code can continue after the return
Summary:
Reset_Handler in your code is not a 'reset vector' in the sense of an interrupt vector that is operating in a "reset context". It is the first instruction of code operating in the 'normal' supervisor context, which is not an interrupt based context. This context can be interrupted by NMI. Normally the global interrupt mask is set so that no 'normal' interrupt can occur yet (the CPSID I should be superfluous).
Regards
Mark
Hi Dave
I think that the confusion is that the reset context terminates on the very first instruction of code that is executed. That means that the system reset will cause the stack pointer to be loaded with the value at address 0x00000000 (sfe(CSTACK) in your case) and then will cause the program counter to be loaded with the address of the first instruction to be executed (Reset_Handler in your case). Now code start executing the processor is no longer in the reset context but the supervisor state and NMI can occur.
Since NMI cannot be masked you will get its vector (NMI_Handler in your case) taken, when the NMI line is asserted, before any code is executed - the line
CPSID I | ; Mask interrupts PRIMASK |
WILL NOT be executed until the NMI vector has been handled in this case so it is importent that:
1) The stack pointer has a valid address so that the interrupt vector can push the NMI interrupt context to the stack
2) The NMI handler is in Flash so that it can execute without further configuration
3) The NMI handler code does anything necessary to remove the pending NMI so that the code can continue after the return
Summary:
Reset_Handler in your code is not a 'reset vector' in the sense of an interrupt vector that is operating in a "reset context". It is the first instruction of code operating in the 'normal' supervisor context, which is not an interrupt based context. This context can be interrupted by NMI. Normally the global interrupt mask is set so that no 'normal' interrupt can occur yet (the CPSID I should be superfluous).
Regards
Mark