mirror of
https://github.com/apache/nuttx.git
synced 2026-08-02 20:59:02 +00:00
The document context-switch-notes.rst was leaking into "Supported Platforms" table of contents. To fix this, it was moved to separate common directory and referenced from AVR index.rst Similar to that, the gpio_intr_mux.rst document was leaking into list of supported AVR families. This too was fixed by moving it to separate docs directory. The change was tested by building the Documentation using make html Signed-off-by: Kerogit <kr.git@kerogit.eu>
118 lines
4.7 KiB
ReStructuredText
118 lines
4.7 KiB
ReStructuredText
===========================
|
|
Notes on AVR context switch
|
|
===========================
|
|
|
|
This document describes the ways and circumstances in which context
|
|
switches happen in AVR MCUs.
|
|
|
|
Used terms and context switch basics
|
|
====================================
|
|
|
|
Context creation
|
|
----------------
|
|
|
|
There are two ways context is created when a task is suspended.
|
|
Either the task is suspended in response to a hardware interrupt
|
|
(context created ``in-interrupt`` in the following text), or it is
|
|
suspended voluntarily, eg. by calling sleep(), read() etc.
|
|
(``in-task`` context.)
|
|
|
|
The resulting context is identical and interchangeable with two differences:
|
|
|
|
- ``SREG`` - ``in-interrupt`` context has global interrupt enabled ("I"-flag) set
|
|
- position in the program where the task resumes running (arbitrary point for ``in-interrupt`` vs. inside ``up_switch_context()`` for ``in-task``
|
|
|
|
Task resumption
|
|
---------------
|
|
|
|
Task can be resumed in two corresponding situations - context switch
|
|
in response to a hardware interrupt (``by-interrupt``) or in response
|
|
to other task relinquishing the CPU (``by-task``)
|
|
|
|
Context switch
|
|
==============
|
|
|
|
Two ways of context creation combined with two ways of task resumption
|
|
give 4 possibilities of context switch process, two of which are
|
|
not interesting:
|
|
|
|
1st combination
|
|
---------------
|
|
|
|
``in-task`` context resumed in ``by-task`` context switch. Context
|
|
to be resumed has "I" flag cleared and SREG is restored with that flag cleared.
|
|
The ``ret`` instruction is used to resume the task,"returning" to the point
|
|
where it gave the CPU up, which is inside ``up_switch_context()``.
|
|
|
|
This function is supposed to be executed with interrupt disabled ("This
|
|
function is called only from the NuttX scheduling logic. Interrupts
|
|
will always be disabled when this function is
|
|
called." https://nuttx.apache.org/docs/latest/reference/os/arch.html ) Caller
|
|
of the context switch method is therefore responsible
|
|
for re-enabling interrupts.
|
|
|
|
2nd
|
|
---
|
|
|
|
``in-interrupt`` resumed in ``by-interrupt`` context switch. The task
|
|
essentially (from its point of view) exits interrupt handler after
|
|
it entered it. Instruction ``reti`` is used to return from the handler,
|
|
setting "I"-flag in the process.
|
|
|
|
3rd
|
|
---
|
|
|
|
Third and fourth combinations are more interesting:
|
|
|
|
``in-task`` context resumed in ``by-interrupt``. The CPU enters
|
|
ISR and regular program flow requires returning from ISR and setting
|
|
"I"-flag by ``reti`` which does not happen. Task is resumed with
|
|
interrupts disabled. However, it is resumed inside ``up_switch_context()``
|
|
and caller of that function will set the "I"-flag at some point.
|
|
Task then runs with interrupts enabled, all is well.
|
|
|
|
4th
|
|
---
|
|
|
|
``in-interrupt`` resumed in ``by-task``, ie. in ``up_switch_context()``.
|
|
``reti`` is used to resume the task, setting "I"-flag in the process.
|
|
This would be incorrect for ``up_switch_context()`` - it is supposed
|
|
to run with interrupts disabled - but the task resumes running
|
|
from the point where it was interrupted, which is not inside
|
|
of ``up_switch_context()``. All is well.
|
|
|
|
|
|
AVRDx core considerations
|
|
=========================
|
|
|
|
Now, all of the above holds true for eg. ATmega chips which control
|
|
interrupt execution solely by the "I"-flag, allowing the code
|
|
to not care about where the context switch was triggered. Regardless
|
|
of that, the MCU will always end up in correct state even if the
|
|
context switch cause doesn't match the context being restored
|
|
(cases 3 and 4.)
|
|
|
|
This is not the case for AVR Dx family which behaves differently.
|
|
The interrupt controller does respect the "I"-flag in a sense where
|
|
it considers interrupts disabled when the flag is cleared. However,
|
|
it is possible that interrupts are not enabled when the flag is set.
|
|
That depends on a logical AND between "I"-flag and "interrupt handler
|
|
is not executing" internal state. (Refer to the documentation
|
|
for more precise explanation.)
|
|
|
|
What this means is that if eg. ``in-task`` context gets
|
|
resumed in ``by-interrupt`` condition (case 3 above), then ``ret``
|
|
instruction is used
|
|
to resume the task. As discussed above, the "I"-flag is not set this
|
|
way but that is not a problem, it is eventually set later. However,
|
|
the internal state "running the interrupt handler" is not cleared.
|
|
This means that the task keeps running with "global interrupts are
|
|
enabled" but is actually unable to be interrupted. The context switch
|
|
code needs to handle this.
|
|
|
|
Conversely, there is a similar problem with ``in-interrupt`` context
|
|
being resumed in ``by-task`` (case 4). Instruction ``reti`` is used
|
|
but there is no internal state to be cleared. Unlike the previous case,
|
|
no problem related to this was observed but it still looks like something
|
|
the code wants to avoid. It could trigger all sorts of undefined behaviour
|
|
in the chip otherwise.
|