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:
Gerardo Aceves 2016-04-29 11:18:24 -05:00
commit 6a578ad14f

View file

@ -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.