diff --git a/cpu/cortexm_common/cortexm_init.c b/cpu/cortexm_common/cortexm_init.c index 0bf89d2a4b..c20ecc939a 100644 --- a/cpu/cortexm_common/cortexm_init.c +++ b/cpu/cortexm_common/cortexm_init.c @@ -33,9 +33,14 @@ extern const void *_isr_vectors; 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 */ - /* set pendSV interrupt to same priority as the rest */ - NVIC_SetPriority(PendSV_IRQn, CPU_DEFAULT_IRQ_PRIO); + /* set pendSV interrupt to its own priority */ + NVIC_SetPriority(PendSV_IRQn, CPU_CORTEXM_PENDSV_IRQ_PRIO); /* set SVC interrupt to same priority as the rest */ NVIC_SetPriority(SVCall_IRQn, CPU_DEFAULT_IRQ_PRIO); /* initialize all vendor specific interrupts with the same value */ diff --git a/cpu/cortexm_common/include/cpu_conf_common.h b/cpu/cortexm_common/include/cpu_conf_common.h index 7e9efe21a8..1e0b4f4a93 100644 --- a/cpu/cortexm_common/include/cpu_conf_common.h +++ b/cpu/cortexm_common/include/cpu_conf_common.h @@ -68,6 +68,83 @@ extern "C" { #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 */