5
\$\begingroup\$

I am trying to learn about STM32 programming. I started to read some references before start the programming.

I am currently reading this ST presentation.

It says:

When an interrupt request with lower or equal priority is raised during execution of an interrupt handler, it becomes pending. Once the current interrupt handler is finished, the context saving and restoring process is skipped and control is transferred directly to the new exception handler to decrease interrupt latency. So back-to-back interrupts with decreasing priorities (higher priority values) are chained with a very short latency of a few clock cycles.

What is the context saving and restoring process mentioned in the above quoted text?

If someone can explain entire STM32 interrupt process, greatly appreciate.

\$\endgroup\$
1
  • \$\begingroup\$ Context save and restore is a concept basic to interrupts on essentially all conventional platforms, not STM32- or ARM-specific except in the specific details of what is done, and in this case, when it can be deferred. \$\endgroup\$ Jul 29, 2019 at 14:13

3 Answers 3

3
\$\begingroup\$

When an exception (interrupt) happens the context of what the core is doing at that moment needs to be pushed on stack, so it is possible to return to it later. This is done by a stackframe.

For an exception a stackframe looks like this:
enter image description here

Registers R4 to R11 are not pushed to stack unless the exception code needs them. The compiler may push/pop these registers if required. Above is the minimum amount of registers pushed by hardware, to keep things quick.

When the exception is finished, the hardware reverses the stackframe and restores the context to continue execution.

The processor can skip returning the stackframe if another interrupt is still pending. This is called tail-chaining.

More info about the Exception Model of the Cortex M0 can be found in the ARM docs.

\$\endgroup\$
6
  • 3
    \$\begingroup\$ Just to clarify, if any registers R4 through R11 need to be preserved on the stack they will be pushed in software as part of the ISR code, whereas R0 through R3 are pushed by the hardware during the interrupt stacking. \$\endgroup\$ Jul 29, 2019 at 11:43
  • 1
    \$\begingroup\$ You might want to clarify that if a return-from-interrupt sequence were initiated, and an interrupt became active just before the end of it, software would load the values of registers from the stack and then store to the stack the exact same values it had just loaded, to the places from which they were just read. The ARM assumes that if it can tell that an interrupt will be pending when another returns, it can skip those reads and writes--an assumption that would normally be invisible to user code, but must be provided for in some kinds of unusual interrupt-handling code. \$\endgroup\$
    – supercat
    Jul 29, 2019 at 16:31
  • \$\begingroup\$ @supercat In what cases should the software be aware that tail-chaining has occurred? \$\endgroup\$
    – Jeroen3
    Jul 29, 2019 at 20:15
  • \$\begingroup\$ @Jeroen3: If it's necessary to have both main-line code and an ISR update different bits in an I/O register, and disabling interrupts during an update would be unacceptable, a workaround on some systems would be to have the main-line and interrupt code agree that if a certain word in RAM is non-zero, a particular CPU register will hold the value of that I/O register. This sort of thing is a nasty horrible hack which wouldn't be necessary if controllers allowed unrelated objects to be accessed separately, but all too often controllers fail to provide clean ways of updating things. \$\endgroup\$
    – supercat
    Jul 29, 2019 at 21:07
  • \$\begingroup\$ @supercat ARM fixed this with exclusive access. Not for M0 though. There you still need to disable irq and a memory barrier. M0 only has one bus. But I still don't see how this concurrency problem is relevant to context saving. \$\endgroup\$
    – Jeroen3
    Jul 30, 2019 at 5:59
4
\$\begingroup\$

Whenever an interrupt is triggered and the program is leaving its current work, there is some saving to do. The status bits in the status register are used for many tasks, that can be interrupted by an external/internal event. Imagine you check if a variable has reached a specific value and if this i true a conditional jump is to be made. Therefor the comparison will (potentially) set the zero bit of the status register. But if an interrupt is happening right between these two steps (comparing and jumping), the zero bit of the status register might be changed by operations in the ISR. That's why your mircoprocessor has a stack to save the statusregister with a push operation before entering an ISR and restoring it with pull after leaving the ISR. That way it is ensured, that an ISR will not break you program.

Also the processor has to save the current position in the code, the program counter. After finishing the ISR the program counter can be set back to the stored value for the program to continue.

\$\endgroup\$
4
  • \$\begingroup\$ I need some clarification, As you said in your example, compassion and jumping operations done by in the ISR. So, ISR may be change the status register. This changing may be affect to all program and stack is prevent such kind of unwanted things. Stacks save current statue register value using push operation. If ISR changes status register or not, stack restore the status register by stack using pull operation. Am I correct @jusaca ? \$\endgroup\$
    – user_fs10
    Jul 29, 2019 at 6:55
  • 1
    \$\begingroup\$ Exactly. Comparison and jump were just one example, there are many similar cases. It just is important, that the statusregister is stored and restored with every call of an ISR. When you use a high level programming language like C you don't have to take care of this, because the compiler is doing this for you automatically. \$\endgroup\$
    – jusaca
    Jul 29, 2019 at 7:04
  • 1
    \$\begingroup\$ I think you should clarify the difference between a push operation and a PUSH instruction. When an interrupt occurs the registers are put on the stack without actually executing an instruction code...it is all done in the hardware. \$\endgroup\$ Jul 29, 2019 at 11:45
  • \$\begingroup\$ @ElliotAlderson As you said, PUSH is automatically done by hardware. Not need any instruction in the code. But, according to jusaca mentioned above, the C compiler involve to this. \$\endgroup\$
    – user_fs10
    Jul 29, 2019 at 17:16
1
\$\begingroup\$

When the CPU receives an interrupt, it must save some of the current status of the CPU before invoking the interrupt handler so that it can be restored when the interrupt handler is finished. These data include the current flags and value of the program counter, and usually some other registers. (Jeroen3's answer gives the full details for this particular CPU.)

When an interrupt handler is currently running, other interrupts can arrive. Depending on the relative priority of the two interrupts, one of two things can happen: 1. The new interrupt is higher priority, and interrupts the current interrupt handler, just as that had interrupted the main program. Once the new interrupts handler is complete, control returns to the previous interrupt handler and, once that's complete, control returns to the main program. 2. The new interrupt is lower priority, so it's "saved" until the current interrupt handler is complete. At that point the new interrupt's handler can run.

Thus, in this second case, where another interupt is pending but blocked until the current interrupt handler is complete, you might see a flow like this:

  1. First interrupt arrives
  2. Second (lower-priority) interrupt arrives.
  3. Current instruction of the main program completes.
  4. Save current registers on to the stack.
  5. Call first interrupt handler.
  6. Restore registers from stack.
  7. Save current registers on to the stack (again).
  8. Call second interrupt handler.
  9. Restore registers from stack.

Here step 6 above restores the registers from the stack, only to have step 7 save the exact same data from the unchanged registers back on to the stack, leaving the stack unchanged after those two steps.

Dropping steps 6 and 7 doesn't change the behaviour at all, and saves the time it takes to read the data only to write it back again. This is the optimization they are using.

(Actually, it isn't quite true that the behaviour is unchanged, becuase depending on how the CPU sets the registers before entry to an interrupt handler, the interrupt handler may see different data in the registers. For example, if register R0 is left at the current value on entry to the first interrupt handler, but it changes R0, the following interrupt handler will see the value that the first interrupt handler left behind in R0, not the value that had been there before the first interrupt handler was called. This isn't a problem because normally an interrupt handler wouldn't care what (essentially random) value was in R0 when it started. If it did care, it should read the value from the stack rather than assuming what's in R0 is what what the interrupted routine had in R0 when it was interrupted.)

\$\endgroup\$
5
  • \$\begingroup\$ Thanks. What happen when interrupt arrives (lower priority or higher priority) while executing interrupt handler? I cant understand what is the meaning of "interrupt pending" ? \$\endgroup\$
    – user_fs10
    Jul 29, 2019 at 17:37
  • 1
    \$\begingroup\$ @user_fs10 I've expanded the answer to add an explanation of interrupt priorities. Let me know if this makes sense or if you still have questions. \$\endgroup\$
    – cjs
    Jul 29, 2019 at 18:28
  • \$\begingroup\$ Its more clear than before. But, still have several problems. As you listed above, 1 & 2 both interrupt arrive while main program is executing (in same instruction executing). So, both Interrupt wait for current main problem instruction is compleated. What happen if several interrupts arrive ? In step 4, save register for run first interrupt handler. In step 6, restore stack because of first interrupt handler is finished. As soon as need to run second interrupt handler. So, save registers again for second interrupt. Am I correct? \$\endgroup\$
    – user_fs10
    Jul 30, 2019 at 15:11
  • 1
    \$\begingroup\$ @user_fs10 No matter how many interrupts arrive, only one is ever executing at a time and the others are held until the currently executing interupt handler is finished.And yes, without the optimization whenver another interrupt is pending when an interrupt handler finishes it would, without the optimization, take all the data off the stack and then put all that same data back on the stack again, just as it had been before. \$\endgroup\$
    – cjs
    Jul 30, 2019 at 15:30
  • 1
    \$\begingroup\$ @user_fs10 Think of it this way. I have a table with six playing cards stacked on top of each other, ace at the bottom, 2, 3, 4, 5, and 6 on top.. I ask you to remove the playing cards from from the table from the top down, so you take 6, 5, 4, 3, 2 ace, and then I ask you to put them back again, ace, 2, 3, 4, 5, 6. Now what's on the table is exactly what was there before, and if I'd turned around, you pretended to do this, and yet you actually didn't, I would have no way to tell. That's the optimization: you don't do a bunch of actions that would leave you exactly where you started anyway. \$\endgroup\$
    – cjs
    Jul 30, 2019 at 15:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.