mirror of
https://github.com/apache/nuttx.git
synced 2026-10-08 14:55:18 +00:00
The documentation grew one page at a time, so the tree follows the
history of who wrote what and not the shape of NuttX. Scheduling is
spread over three places, a driver page can sit above the subsystem
that owns it, and the front page lists everything at the same level.
That is a lot to face when all you want to know is where the scheduler
lives.
This change files every page under the code it describes. It is a move,
not a rewrite: outside the ten pages named below, every page keeps the
text that is already in master, and no page's text is deleted.
What it does:
* Groups the table of contents into nine chapters.
* Moves the OS subsystems under os/: scheduling, memory, drivers,
filesystem, networking, IPC, interrupts, libs, time.
* Renames the platform pages to the names the source tree uses, and
derives their tags from the tree instead of by hand.
* Splits guides/ by subject.
* Adds Documentation/redirects.py, with a rule for every page that left
its old path, so old URLs keep working. The redirect page also carries
a link's #anchor across to the new page.
Ten pages have text that is new or rewritten. Nine of them are the
landing page of a chapter, which has to exist for the new structure:
index the front page
os/index OS Design
os/scheduling/index Scheduling
os/interrupts/index Interrupts
os/ipc/index IPC
os/time/index Time and timers
about/index About
developing/index Developing NuttX
ReleaseNotes/index Release notes
The tenth is os/libs/libbuiltin, the only page here with technical
content: libs/libbuiltin/ had no page at all. Five SVG diagrams come
with these pages, hand-written XML with no editor metadata.
Nothing outside Documentation/ is touched.
How it was checked:
* Sphinx builds with -W: no warnings, and no document left outside a
toctree.
* A script, offered in the PR, proves the narrow claim this rests on.
For every page outside the ten named above it erases what a move
touches -- link target, path, tag line, toctree block, table border --
from the whole old text and the whole new text, and requires the two
to be byte for byte identical. It also requires every sentence of a
deleted page to turn up somewhere, and every page that left its old
path to have a redirect, from a URL that existed, to where its content
went. It exits non-zero and names the page if any of that is not true,
and it tests added pages too, so forgetting to declare one cannot make
it pass.
* An independent audit checked 133 factual claims on these ten pages
against the tree, one shell command per claim: 130 confirmed, 1
refuted and fixed here, 2 not checkable.
* tools/checkpatch.sh is clean over the range.
The diff is large because moving a page changes every link that points
to it. Most of it is pure renames, and board pages that gained one tag
line.
Assisted-by: Claude:claude-opus-5
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/os/arch/arch_api.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.
|