cpu/cortexm_common: add irq sub-priorities

This commit enables Cortex-M CPU interrupt sub-priorities
and allows the PendSV interrupt to have a priority different
from the default one.  Together these two preprocessor
defines can be used to have PendSV always run as the last interrupt
before returning from the interrupt stack back to the user space.

Running PendSV as the last interrupt before returning to the
user space is recommended by ARM, as it increases efficiency.
Furthermore, that change enhances stability a lot with the
new nRF52 SoftDevice support, currently being worked in
PR #9473.

This commit merely enables sub-priorities and a separate
PendSV priority to be used without changing the default
RIOT behaviour.
This commit is contained in:
Pekka Nikander 2020-04-03 18:49:31 +03:00 committed by GitHub
parent 3b34768eaf
commit 4534e9b773
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 2 deletions

View File

@ -33,9 +33,14 @@ extern const void *_isr_vectors;
CORTEXM_STATIC_INLINE void cortexm_init_isr_priorities(void) CORTEXM_STATIC_INLINE void cortexm_init_isr_priorities(void)
{ {
#if CPU_CORTEXM_PRIORITY_GROUPING != 0
/* If defined, initialise priority subgrouping, see cpu_conf_common.h */
NVIC_SetPriorityGrouping(CPU_CORTEXM_PRIORITY_GROUPING);
#endif
/* initialize the interrupt priorities */ /* initialize the interrupt priorities */
/* set pendSV interrupt to same priority as the rest */ /* set pendSV interrupt to its own priority */
NVIC_SetPriority(PendSV_IRQn, CPU_DEFAULT_IRQ_PRIO); NVIC_SetPriority(PendSV_IRQn, CPU_CORTEXM_PENDSV_IRQ_PRIO);
/* set SVC interrupt to same priority as the rest */ /* set SVC interrupt to same priority as the rest */
NVIC_SetPriority(SVCall_IRQn, CPU_DEFAULT_IRQ_PRIO); NVIC_SetPriority(SVCall_IRQn, CPU_DEFAULT_IRQ_PRIO);
/* initialize all vendor specific interrupts with the same value */ /* initialize all vendor specific interrupts with the same value */

View File

@ -68,6 +68,83 @@ extern "C" {
#endif #endif
/** @} */ /** @} */
/**
* @name ARM Cortex-M interrupt sub-priorities and PendSV priority
* @{
*/
/**
* @brief Enable Cortex-M sub-priorities, with the given number of bits
*
* Cortex-M CPUs allow interrupt priorities to be arranged in subgroups,
* meaning that any interrupts on the same subgroup will not
* pre-empt each other, but those on a lower sub-priority will run
* only once all on the higher sub-priorities have been completed.
*
* The usual practice on Cortex-M is to run the PendSV interrupt
* as the last one, just before returning to the user context,
* running the scheduler from PendSV. However, in RIOT we don't want
* the scheduler to be interrupted by any "normal" interrupts, which
* may change the mutexes or something else that would affect the
* scheduler. At the same time, we don't want to explicitly
* disable all interrupts while in the scheduler, as that would
* increase scheduling latency.
*
* A currently experimental way to make PendSV on Cortex-M to run
* last is to
* - make sure @ref CPU_DEFAULT_IRQ_PRIO is even (e.g. `6U`),
* - set @ref CPU_CORTEXM_PENDSV_IRQ_PRIO to one higher, and
* - set this one
*
* For example, as follows:
* @code{.c}
* # define CPU_CORTEXM_PRIORITY_GROUPING (1U)
* # define CPU_CORTEXM_PENDSV_IRQ_PRIO (CPU_DEFAULT_IRQ_PRIO + 1U)
* @endcode
*
* See cpu/cortexm_common/cortexm_init.c how these are used.
*
* If you want to set this, define it in your `cpu_conf.h`.
*/
#ifndef CPU_CORTEXM_PRIORITY_GROUPING
#define CPU_CORTEXM_PRIORITY_GROUPING (0)
#endif
/**
* @brief Define a separate priority for the PendSV interrupt
*
* According to the ARM Cortex-M documentation, the recommended best
* practice is to place the PendSV at the lowest interrupt priority.
* By default, RIOT runs PendSV on the same interrupt priority with all
* other interrupts.
*
* For efficency (or other reasons), one may want to run the PendSV as
* the last one, just before returning to the (next) thread. However,
* since PendSV triggers the RIOT scheduler _without_ interrupts being
* disabled, any interrupts that pre-empt the scheduler, including the
* timer interrupts, must not call anything that may affect the
* scheduler, such as mutex or scheduler functions. With the current
* design of RIOT, writing interrupt handlers in such a manner is not
* exactly trivial.
*
* An experimental way to to run PendSV as the last thing before
* returning to the user thread context is to enable Cortex-M
* sub-priorities with @ref CPU_CORTEXM_PRIORITY_GROUPING and then
* make the PendSV interrupt sub-priority lower than the default.
* (Remember, on Cortex-M lower urgency means higher priority number.)
*
* For now, by default, we preserve the traditional RIOT behaviour, but
* allow specific CPUs, boards, or apps to change this.
*
* See cpu/cortexm_common/cortexm_init.c how these are used.
*
* If you want to set this, define it in your `cpu_conf.h`.
*/
#ifndef CPU_CORTEXM_PENDSV_IRQ_PRIO
#define CPU_CORTEXM_PENDSV_IRQ_PRIO (CPU_DEFAULT_IRQ_PRIO)
#endif
/** @} */
/** /**
* @brief Attribute for memory sections required by SRAM PUF * @brief Attribute for memory sections required by SRAM PUF
*/ */