zephyr/doc/kernel/other/cpu_idle.rst

144 lines
3.5 KiB
ReStructuredText
Raw Normal View History

.. _cpu_idle:
CPU Idling
##########
Although normally reserved for the idle thread, in certain special
applications, a thread might want to make the CPU idle.
.. contents::
:local:
:depth: 2
Concepts
********
Making the CPU idle causes the kernel to pause all operations until an event,
normally an interrupt, wakes up the CPU. In a regular system, the idle thread
is responsible for this. However, in some constrained systems, it is possible
that another thread takes this duty.
Implementation
**************
Making the CPU idle
===================
Making the CPU idle is simple: call the k_cpu_idle() API. The CPU will stop
executing instructions until an event occurs. Make sure interrupts are not
locked before invoking it. Most likely, it will be called within a loop.
.. code-block:: c
static k_sem my_sem;
void my_isr(void *unused)
{
k_sem_give(&my_sem);
}
void main(void)
{
k_sem_init(&my_sem, 0, 1);
/* wait for semaphore from ISR, then do related work */
for (;;) {
/* wait for ISR to trigger work to perform */
if (k_sem_take(&my_sem, K_NO_WAIT) == 0) {
/* ... do processing */
}
/* put CPU to sleep to save power */
k_cpu_idle();
}
}
Making the CPU idle in an atomic fashion
========================================
It is possible that there is a need to do some work atomically before making
the CPU idle. In such a case, k_cpu_atomic_idle() should be used instead.
In fact, there is a race condition in the previous example: the interrupt could
occur between the time the semaphore is taken, finding out it is not available
and making the CPU idle again. In some systems, this can cause the CPU to idle
until *another* interrupt occurs, which might be *never*, thus hanging the
system completely. To prevent this, k_cpu_atomic_idle() should have been used,
like in this example.
.. code-block:: c
static k_sem my_sem;
void my_isr(void *unused)
{
k_sem_give(&my_sem);
}
void main(void)
{
k_sem_init(&my_sem, 0, 1);
for (;;) {
unsigned int key = irq_lock();
/*
* Wait for semaphore from ISR; if acquired, do related work, then
* go to next loop iteration (the semaphore might have been given
* again); else, make the CPU idle.
*/
if (k_sem_take(&my_sem, K_NO_WAIT) == 0) {
irq_unlock(key);
/* ... do processing */
} else {
/* put CPU to sleep to save power */
k_cpu_atomic_idle(key);
}
}
}
Suggested Uses
**************
Use k_cpu_atomic_idle() when a thread has to do some real work in addition to
idling the CPU to wait for an event. See example above.
Use k_cpu_idle() only when a thread is only responsible for idling the CPU,
i.e. not doing any real work, like in this example below.
.. code-block:: c
void main(void)
{
/* ... do some system/application initialization */
/* thread is only used for CPU idling from this point on */
for (;;) {
k_cpu_idle();
}
}
.. note::
**Do not use these APIs unless absolutely necessary.** In a normal system,
the idle thread takes care of power management, including CPU idling.
APIs
****
The following CPU idling APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_cpu_idle()`
* :cpp:func:`k_cpu_atomic_idle()`