Cortex M4 exception return sequence

Hi,

I think I am just getting confused with this even if (or because of) I read the book and manuals again and again.

At exception entry, the processor saves R0-R3, R12, LR, PC and PSR on the stack. Saving PC means that the address of the instruction to be executed next after return from the exception handler is saved on the stack. However, the documentation also says that LR is updated with EXC_RETURN and that when the EXC_RETURN value is loaded to the PC, the exception return sequence begins.

So, the confusion is - it is the stacked PC value which should be loaded to the PC to return to the place where it left to attend the exception handler. If the PC is loaded with EXC_RETURN value, it is not a valid address but that only the lower 5 bits indicate which stack was used and the return mode (thread/handler) etc.

Or is it that the loading of PC with EXC_RETURN is then followed up by loading of the PC with the stacked PC value?

Can someone please help clearing this confusion?

Thanks,

Gopal

Parents
  • To me, it looks like you've understood almost all of it correctly.

    Loading PC with the value of LR is sufficient. LR already holds EXC_RETURN, and you do not have to worry about which stack you need to use; the EXC_RETURN in LR is pre-encoded with the correct value.

    Normally you only have to change the EXC_RETURN value when you're writing a context-switcher.

    This is what more or less happens:

    1. An interrupt is signalled; a pending-flag is set.
    2. The interrupt is started, the registers xPSR, PC, LR, R12, R3-R0 are all pushed onto the interrupt-stack.
    3. The processor state is changed to use the interrupt-stack.
    4. The LR is loaded with the EXC_RETURN value (which is one of these: 0xFFFFFFF1, 0xFFFFFFF9, 0xFFFFFFFD, 0xFFFFFFE1, 0xFFFFFFE9 or 0xFFFFFFED).
    5. The PC is loaded with the address from the interrupt-vector.
    6. Your Interrupt Service Routine is executed.
    7. You make sure the LR register is saved/restored if it's changed.
    8. You finish your Interrupt Service Routine by executing a BX LR instruction.
    9. The EXC_RETURN value from the LR register is now moved into PC.
    10. The core now sees that this is a special return-address, so it restores the registers from the current stack.
    11. When the registers are restored, the execution continues where it was interrupted.

    The EXC_RETURN is actually one real cool feature of the Cortex architecture. It means that you do not have to have a RFI instruction (Return From Interrupt), as you use the standard "return-instruction" to return from an interrupt. So there's no real difference in writing an interrupt-routine and a normal subroutine for a Cortex-M based microcontroller.

    I remember in the 80's (speaking about the M68xxx in particular), you would have to unstack some 'info-words', depending on which kind of interrupt occurred, and you'd have to make sure that the stack-frame had the correct format. Sometimes you'd have to modify the values on the stack, before returning, and if that wasn't enough, you had great chances that your interrupt would crash the entire system if you made a minor mistake.

    It's not like that with an ARM Cortex-M microcontroller. The architecture makes your interrupt and exception handling much more robust; you don't have to worry too much about reentrancy, and if you're writing a context-switcher from scratch, ARM did most of it for you in hardware in advance.

  • Thanks jensbauer

    It was step 10 and 11 where I was not getting clear idea.

    Thanks daith for your inputs as well !

  • As daith says, it's possible that another interrupt will be handled by tail-chaining.

    This may occur between step 8 and step 9.

    jyiu once explained the details about this, that the registers R0-R3 and R12 will not contain values identical to what is on the stack on interrupt entry.

    -In fact, you can never trust what's in R0-R3 and R12, so if you need those values (for instance if you're using SVC, or if you're making some debug-facility), then fetch them from the stack.

Reply
  • As daith says, it's possible that another interrupt will be handled by tail-chaining.

    This may occur between step 8 and step 9.

    jyiu once explained the details about this, that the registers R0-R3 and R12 will not contain values identical to what is on the stack on interrupt entry.

    -In fact, you can never trust what's in R0-R3 and R12, so if you need those values (for instance if you're using SVC, or if you're making some debug-facility), then fetch them from the stack.

Children
More questions in this forum