up_cpu_wfi Implementation Guide
[ English | 简体中文 ]
The up_cpu_wfi() function is central to placing the platform into a low-power state. It is called by up_pm_idle_handler in the example code from Standardizing Idle Thread Power Management with pm_idle. The implementation of this function is tightly coupled with the CPU architecture. This section provides reference implementations and an analysis of key technical points for mainstream architectures (Cortex-M and RISC-V).
I. Cortex-M Architecture
On the Cortex-M architecture, especially in systems with zero-latency interrupts enabled (CONFIG_ARCH_ZOOLATENCY), the implementation of up_cpu_wfi() requires special attention to ensure that all interrupts, including high-priority zero-latency interrupts, are correctly masked before entering the WFI (Wait For Interrupt) state.
The standard up_irq_save() function typically masks all maskable interrupts by setting the PRIMASK register, but it cannot mask zero-latency interrupts. Therefore, it is necessary to directly manipulate the BASEPRI register to temporarily raise the interrupt mask level.
Reference Implementation: Source Link
void up_cpu_wfi(void)
{
/* The WFI implementation is architecture-specific */
#ifdef CONFIG_ARCH_CORTEXM4
/*
* This implementation is required for systems that use BASEPRI for interrupt
* management, especially when zero-latency interrupts are enabled.
*/
int basepri = 0;
/* Change BASEPRI to the minimal priority
* value for waking up from PRIMASK == 1
*/
__asm__ __volatile__
(
#ifdef CONFIG_ARMV7M_USEBASEPRI
"\tcpsid i\n" /* Disable interrupts globally */
#endif
"\tmrs %0, basepri\n" /* Save current BASEPRI value */
"\tmsr basepri, %1\n" /* Set BASEPRI to block all maskable IRQs */
"\tdsb\n" /* Ensure all memory accesses complete */
"\twfi\n" /* Enter wait-for-interrupt state */
"\tmsr basepri, %0\n" /* Restore original BASEPRI value */
#ifdef CONFIG_ARMV7M_USEBASEPRI
"\tcpsie i\n" /* Re-enable interrupts globally */
#endif
: "+r" (basepri) /* Output/Input: original basepri value */
: "r" (0xff)
: "memory"
);
#else
__asm__ __volatile__
(
"\tdsb\n"
"\twfi\n"
);
#endif
}
Implementation Analysis
cpsid i: Globally disables interrupts. This is to prevent a race condition where an interrupt (even a low-priority one) is serviced whileBASEPRIis being modified.mrs %0, basepri: Saves the current value of theBASEPRIregister into thebaseprivariable.msr basepri, %1: SetsBASEPRItoNVIC_SYSH_PRIORITY_MIN. This value is typically the highest numerical priority (i.e., the lowest actual priority) supported by the system, ensuring that all interrupts with a lower or equal priority are masked. This effectively masks all maskable interrupts, including zero-latency ones.dsb: Data Synchronization Barrier. Ensures that all memory access operations preceding theWFIinstruction have completed.wfi: Executes theWait For Interruptinstruction, causing the CPU to enter a low-power state until an interrupt event wakes it.msr basepri, %0: Immediately after the CPU wakes fromWFI, restores the previously savedBASEPRIvalue, returning the interrupt mask to its normal state.cpsie i: Globally re-enables interrupts, corresponding to thecpsid iin the first step.
II. RISC-V Architecture
In contrast, the implementation of up_cpu_wfi for the RISC-V architecture is typically much simpler. The standard WFI instruction is sufficient to place the core (hart) into a low-power state to wait for an interrupt.
Reference Implementation: Source Link
void up_cpu_wfi(void)
{
__asm__ volatile("wfi");
}
Implementation Analysis
In RISC-V, by the time the up_idle function is called, the operating system has already disabled global interrupts via up_irq_save() (typically by manipulating the MIE bit in the mstatus register). Therefore, within up_cpu_wfi(), you only need to execute the wfi instruction. The processor will pause execution until an external interrupt, local interrupt, or debug request becomes pending.