doc: Edit arch.rst markup
Edited and added minor markup for consistancy. Edited some '/' to clarify 'and' 'or'. Change-Id: I5aa84f278d65e79d12b634fcbef2a8b5070c8ebd Signed-off-by: Gerardo Aceves <gerardo.aceves@intel.com>
This commit is contained in:
parent
ded3070c1c
commit
6a578ad14f
1 changed files with 43 additions and 43 deletions
|
@ -11,7 +11,7 @@ The following are examples of ISAs and ABIs that Zephyr supports:
|
|||
|
||||
* x86_32 ISA with System V ABI
|
||||
* x86_32 ISA with IAMCU ABI
|
||||
* ARMv7-M ISA with Thumb2 instruction set and ARM Embedded ABI (aeabi).
|
||||
* ARMv7-M ISA with Thumb2 instruction set and ARM Embedded ABI (aeabi)
|
||||
* ARCv2 ISA
|
||||
|
||||
An architecture port is mostly constrained to the nanokernel. The reason is
|
||||
|
@ -60,19 +60,19 @@ some architectures require a bit more work to be performed.
|
|||
Common steps for all architectures:
|
||||
|
||||
* Setup an initial stack.
|
||||
* If running an eXecute-In-Place (XIP) kernel, copy initialized data from ROM
|
||||
* If running an :abbr:`XIP (eXecute-In-Place)` kernel, copy initialized data from ROM
|
||||
to RAM.
|
||||
* If not using an ELF loader, zero the BSS section.
|
||||
* Jump to _Cstart(), the early kernel initialization
|
||||
* Jump to :code:`_Cstart()`, the early kernel initialization
|
||||
|
||||
* _Cstart() is responsible for context switching out of the fake context
|
||||
* :code:`_Cstart()` is responsible for context switching out of the fake context
|
||||
running at startup into the background/idle task
|
||||
|
||||
Some examples of architecture-specific steps that have to be taken:
|
||||
|
||||
* If given control in real mode on x86_32, switch to 32-bit protected mode.
|
||||
* Setup the segment registers on x86_32 to handle boot loaders that leave them
|
||||
in an unknown/broken state.
|
||||
in an unknown or broken state.
|
||||
* Initialize a board-specific watchdog on Cortex-M3/4.
|
||||
* Switch stacks from MSP to PSP on Cortex-M3/4.
|
||||
|
||||
|
@ -100,7 +100,7 @@ performs. For example:
|
|||
|
||||
* After getting control back from the handler:
|
||||
|
||||
* Decide wether to perform a context switch.
|
||||
* Decide whether to perform a context switch.
|
||||
* When performing a context switch, restore the context being context
|
||||
switched in.
|
||||
|
||||
|
@ -126,16 +126,16 @@ an ISR. As such there are two commonly used methods for handling the
|
|||
parameter.
|
||||
|
||||
* Using some architecture defined mechanism, the parameter value is forced in
|
||||
the stub. This is commonly found in X86-based architectures.
|
||||
the stub. This is commonly found in X86-based architectures.
|
||||
|
||||
* The parameters to the ISR are inserted and tracked via a separate table
|
||||
requiring the architecture to discover at runtime which interrupt is
|
||||
executing. A common interrupt handler demuxer is installed for all entries of
|
||||
the real interrupt vector table, which then fetches the device's ISR and
|
||||
parameter from the separate table. This approach is commonly used in the ARC
|
||||
and ARM architectures via the sw_isr_table implementation. You can find
|
||||
parameter from the separate table. This approach is commonly used in the ARC
|
||||
and ARM architectures via the :option:`SW_ISR_TABLE` implementation. You can find
|
||||
examples of the stubs by looking at _IntEnt in x86, _IntExit in x86 and ARM,
|
||||
_isr_wrapper() in ARM, or the full implementation description for ARC in
|
||||
:code:`_isr_wrapper()` in ARM, or the full implementation description for ARC in
|
||||
:file:`arch/arc/core/isr_wrapper.S`.
|
||||
|
||||
Each architecture also has to implement primitives for interrupt control:
|
||||
|
@ -147,9 +147,9 @@ Each architecture also has to implement primitives for interrupt control:
|
|||
|
||||
.. note::
|
||||
|
||||
Zephyr currently supports for connecting interrupts dynamically, i.e. at
|
||||
Zephyr currently supports for connecting interrupts dynamically, i.e. at
|
||||
runtime using :c:func:`irq_connect_dynamic`: this is getting deprecated.
|
||||
:c:func:`irq_connect` is a macro that uses assembler and/or linker script
|
||||
:c:macro:`irq_connect` is a macro that uses assembler and/or linker script
|
||||
tricks to connect interrupts at build time, saving boot time and text size.
|
||||
|
||||
The vector table should contain a handler for each interrupt and exception that
|
||||
|
@ -180,7 +180,7 @@ Two crucial concepts when writing an architecture port are the following:
|
|||
When talking about "the task" in this document, it refers to the task the
|
||||
nanokernel is currently aware of.
|
||||
|
||||
For a refresher on nanokernel/microkernel concepts, see
|
||||
For a refresher on nanokernel and microkernel concepts, see
|
||||
:ref:`kernel_fundamentals`.
|
||||
|
||||
A context switch can happen in several circumstances:
|
||||
|
@ -201,7 +201,7 @@ A context switch can happen in several circumstances:
|
|||
|
||||
Therefore, the context switching must thus be able to handle all these cases.
|
||||
|
||||
The microkernel handles conditions that cause task-to-task transitions. Recall
|
||||
The microkernel handles conditions that cause task-to-task transitions. Recall
|
||||
that the microkernel is architecture-agnostic. Thus, these transitions are of
|
||||
no concern to an architecture port. One example of these is a task blocking on
|
||||
a microkernel mutex object.
|
||||
|
@ -217,16 +217,16 @@ So, the transitions of interest for an architecture port are:
|
|||
* thread-to-ISR
|
||||
* ISR-to-thread
|
||||
|
||||
There are two types of context switches: cooperative and preemptive.
|
||||
There are two types of context switches: :dfn:`cooperative` and :dfn:`preemptive`.
|
||||
|
||||
* A **cooperative** context switch happens when a thread willfully gives the
|
||||
* A *cooperative* context switch happens when a thread willfully gives the
|
||||
control to another thread. There are two cases where this happens
|
||||
|
||||
* When a thread explicitly yields.
|
||||
* When a thread tries to take an object that is currently unavailable and is
|
||||
willing to wait until the object becomes available.
|
||||
|
||||
* A **preemptive** context switch happens either because an ISR or a
|
||||
* A *preemptive* context switch happens either because an ISR or a
|
||||
task causes an operation that schedules a thread of higher priority than the
|
||||
one currently running, if the currently running thread is a task.
|
||||
An example of such an operation is releasing an object on which the thread
|
||||
|
@ -238,29 +238,29 @@ There are two types of context switches: cooperative and preemptive.
|
|||
them is the running thread.
|
||||
|
||||
A cooperative context switch is always done by having a thread call the
|
||||
:func:`_Swap()` kernel internal symbol. When :c:func:`_Swap` is called, the
|
||||
kernel logic knows that a context switch has to happen: :c:func:`_Swap` does not
|
||||
check to see if a context switch must happen. Rather, :c:func:`_Swap` decides
|
||||
what thread to context switch in. :c:func:`_Swap` is called by a very select set
|
||||
:code:`_Swap()` kernel internal symbol. When :code:`_Swap` is called, the
|
||||
kernel logic knows that a context switch has to happen: :code:`_Swap` does not
|
||||
check to see if a context switch must happen. Rather, :code:`_Swap` decides
|
||||
what thread to context switch in. :code:`_Swap` is called by a very select set
|
||||
of nanokernel functions, basically nanokernel objects (fifo, lifo, stack,
|
||||
semaphore) primitives when the object being operated on is unavailable, and
|
||||
some fiber/task yielding/sleeping primitives.
|
||||
|
||||
.. note::
|
||||
|
||||
On x86, :c:func:`_Swap` is generic enough and the architecture flexible enough
|
||||
that :c:func:`_Swap` can be called when exiting an interrupt to provoke the
|
||||
context switch. This should not be taken as a rule, since neither the ARM
|
||||
On x86, :code:`_Swap` is generic enough and the architecture flexible enough
|
||||
that :code:`_Swap` can be called when exiting an interrupt to provoke the
|
||||
context switch. This should not be taken as a rule, since neither the ARM
|
||||
Cortex-M or ARCv2 port do this.
|
||||
|
||||
Since :c:func:`_Swap` is cooperative, the caller-saved registers from the ABI are
|
||||
Since :code:`_Swap` is cooperative, the caller-saved registers from the ABI are
|
||||
already on the stack. There is no need to save them in the TCS.
|
||||
|
||||
A context switch can also be performed preemptively. This happens upon exiting
|
||||
an ISR, in the kernel interrupt exit stub:
|
||||
|
||||
* :c:func:`_IntExit` on x86/ARM.
|
||||
* :c:func:`_firq_exit` and :c:func:`_rirq_exit` on ARCv2.
|
||||
* :code:`_IntExit` on x86/ARM.
|
||||
* :code:`_firq_exit` and :code:`_rirq_exit` on ARCv2.
|
||||
|
||||
In this case, the context switch must only be invoked when the interrupted
|
||||
thread was the task, not when it was a fiber, and only when the current
|
||||
|
@ -283,13 +283,13 @@ Thread Creation and Termination
|
|||
To start a new thread, a stack frame must be constructed so that the context
|
||||
switch can pop it the same way it would pop one from a thread that had been
|
||||
context switched out. This is to be implemented in an architecture-specific
|
||||
:func:`_new_thread` internal routine.
|
||||
:code:`_new_thread` internal routine.
|
||||
|
||||
The thread entry point is also not to be called directly, i.e. it should not be
|
||||
set as the :abbr:`PC (program counter)` for the new thread. Rather it must be
|
||||
wrapped in :c:func:`_thread_entry`. This means that the PC in the stack
|
||||
frame shall be set to :c:func:`_thread_entry`, and the thread entry point shall
|
||||
be passed as the first parameter to :c:func:`_thread_entry`. The specifics of
|
||||
wrapped in :code:`_thread_entry`. This means that the PC in the stack
|
||||
frame shall be set to :code:`_thread_entry`, and the thread entry point shall
|
||||
be passed as the first parameter to :code:`_thread_entry`. The specifics of
|
||||
this depend on the ABI.
|
||||
|
||||
The need for an architecture-specific thread termination implementation depends
|
||||
|
@ -304,7 +304,7 @@ mode if the thread triggered a fatal exception, but not if the thread
|
|||
gracefully exits its entry point function.
|
||||
|
||||
This means implementing an architecture-specific version of
|
||||
:c:func:`fiber_abort` and :c:func:`_TaskAbort`, and setting the two Kconfig
|
||||
:c:func:`fiber_abort` and :code:`_TaskAbort`, and setting the two Kconfig
|
||||
options :option:`ARCH_HAS_TASK_ABORT` and :option:`ARCH_HAS_NANO_FIBER_ABORT`
|
||||
as needed for the architecture (e.g. see
|
||||
:file:`arch/arm//core/cortex_m/Kconfig`).
|
||||
|
@ -379,7 +379,7 @@ nanokernel-only systems, part of the support has to be built in the
|
|||
architecture (:c:func:`nano_cpu_idle` and :c:func:`nano_cpu_atomic_idle`).
|
||||
|
||||
|
||||
The interrupt entry stub (:c:func:`_IntEnter`, :c:func:`_isr_wrapper`) needs to
|
||||
The interrupt entry stub (:code:`_IntEnter`, :code:`_isr_wrapper`) needs to
|
||||
be adapted to handle exiting tickless idle. See examples in the code for
|
||||
existing architectures.
|
||||
|
||||
|
@ -388,8 +388,8 @@ Console Over Serial Line
|
|||
|
||||
There is one other device that is almost a requirement for an architecture
|
||||
port, since it is so useful for debugging. It is a simple polling, output-only,
|
||||
serial port driver on which to send the console (:c:func:`printk`,
|
||||
:c:func:`printf`) output.
|
||||
serial port driver on which to send the console (:code:`printk`,
|
||||
:code:`printf`) output.
|
||||
|
||||
It is not required, and a RAM console (:option:`RAM_CONSOLE`) can be used to
|
||||
send all output to a circular buffer that can be read by a debugger instead.
|
||||
|
@ -404,8 +404,8 @@ expected to be implemented as part of an architecture port.
|
|||
* Atomic operators.
|
||||
|
||||
* If instructions do not exist for a give architecture, it is possible to
|
||||
create a generic version that wraps irq_lock/unlock around non-atomic
|
||||
operations. It is trivial to implement, but does not currently exist.
|
||||
create a generic version that wraps :c:func:`irq_lock` or :c:func:`irq_unlock`
|
||||
around non-atomic operations. It is trivial to implement, but does not currently exist.
|
||||
|
||||
* Find-least-significant-bit-set and find-most-significant-bit-set.
|
||||
|
||||
|
@ -419,9 +419,9 @@ The kernel provides support for CPU power management with two functions:
|
|||
:c:func:`nano_cpu_idle` and :c:func:`nano_cpu_atomic_idle`.
|
||||
|
||||
:c:func:`nano_cpu_idle` can be as simple as calling the power saving
|
||||
instruction for the architecture with interrupts unlocked, for example hlt on
|
||||
x86, wfi/wfe on ARM, sleep on ARC. This function can be called in a loop within
|
||||
a context that does not care if it get interrupted or not by an interrupt
|
||||
instruction for the architecture with interrupts unlocked, for example :code:`hlt` on
|
||||
x86, :code:`wfi` or :code:`wfe` on ARM, :code:`sleep` on ARC. This function can be called in a
|
||||
loop within a context that does not care if it get interrupted or not by an interrupt
|
||||
before going to sleep. There are basically two scenarios when it is correct to
|
||||
use this function:
|
||||
|
||||
|
@ -435,7 +435,7 @@ use this function:
|
|||
re-enable interrupts and invoke the power saving instruction. It can thus be
|
||||
used in real application code. For example, it is used in the implementation of
|
||||
nanokernel objects when the task is polling an object, waiting for the object
|
||||
to be available. Since the task is the lowest-priority thread, and it cannot
|
||||
to be available. Since the task is the lowest-priority thread, and it cannot
|
||||
block, the only thing to do for the CPU is to sleep and wait for an interrupt
|
||||
to release the object.
|
||||
|
||||
|
@ -452,8 +452,8 @@ Fault Management
|
|||
|
||||
Each architecture provides two fatal error handlers:
|
||||
|
||||
* :c:func:`_NanoFatalErrorHandler`, called by software for unrecoverable errors.
|
||||
* :c:func:`_SysFatalErrorHandler`, which makes the decision on how to handle
|
||||
* :code:`_NanoFatalErrorHandler`, called by software for unrecoverable errors.
|
||||
* :code:`_SysFatalErrorHandler`, which makes the decision on how to handle
|
||||
the thread where the error is generated, most likely by terminating it.
|
||||
|
||||
See the current architecture implementations for examples.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue