Power Management

XWOS power management

Overview

XWOS has two sets of power management frameworks, for MP systems and UP systems respectively.

The XWOS power management framework only provides the basic flow. How the SOC ultimately enters sleep is implemented by the callback functions in the BSP: users can decide whether to maintain SDRAM refresh, whether to maintain certain GPIO outputs, or whether to enter the lowest-power standby mode, etc.

In XWOS multi-core systems, CPUs are not unique. The sleep and wake-up flow for each CPU is identical and runs independently. In XWOS single-core systems, the sleep and wake-up flow of a single CPU is the same as that of any CPU in a multi-core system.

Flow

Power management flow per CPU
Photo: xwos.tech / CC-BY-SA-4.0

In the diagram, the left column is the sleep flow, the right column is the wake-up flow, and the arrows indicate the direction in which power management stage transitions can occur. The power management domain divides power management into four stages (green boxes in the diagram):

Running ( XWOS_PM_STAGE_RUNNING )

All CPUs are running normally. The following CAPI can be used to enter the sleep flow:

xwer_t xwos_pm_suspend(void);

This CAPI is from the operating system abstraction layer OSAL, and the actual calls are:

  • Multi-core system: xwer_t xwmp_pm_suspend(void);
  • Single-core system: xwer_t xwup_pm_suspend(void);
Freezing Threads — Thawing Threads ( XWOS_PM_STAGE_FREEZING - XWOS_PM_STAGE_THAWING )

Once the sleep flow begins, the kernel interrupts the waiting and sleeping states of all threads, sets the freezable flag for all threads, and then schedules each thread sequentially until they reach the freeze point and freeze.

This process takes a relatively long time. If a wake-up event occurs during execution, the kernel can switch to the thaw flow within the wake-up event interrupt: thaw frozen threads and cancel the freezable flag of unfrozen threads.

All Threads Frozen ( XWOS_PM_STAGE_ALLFRZ )

This is a relatively brief state. When the last thread completes freezing, the CPU is in the scheduler service interrupt, and the kernel will shut down the tick timer. From this point, the kernel switches the context to power management (XWOS_SKD_CONTEXT_PWRMNT).

If a wake-up event occurs at this moment, after the CPU exits the scheduler service interrupt, it immediately enters the wake-up event interrupt. The sleep flow switches to the wake-up flow, and the tick timer is re-enabled. According to the constraints in Scheduler Interrupts, the priority of the wake-up event interrupt is lower than that of the scheduler service interrupt, so the wake-up event interrupt will definitely begin execution after the scheduler service interrupt exits. The tick timer will definitely be shut down first and then re-enabled, and the order will not be disrupted.

If no wake-up interrupt occurs, after the CPU exits the scheduler service interrupt, it switches back to thread context. At this point, since all threads are frozen, the CPU can only run in the idle task.

Suspending — Resuming ( XWOS_PM_STAGE_SUSPENDING - XWOS_PM_STAGE_RESUMING )

The kernel executes the user’s suspend() callback function in the idle task. At this point, if a wake-up event occurs, the system switches the state from suspending to resuming within the wake-up event interrupt and executes the user’s resume() callback function.

  • The suspend() callback function allows users to shut down devices, configure SDRAM refresh mode, configure GPIO, etc., before sleep;
  • The resume() callback function performs operations opposite to the suspend() callback function before wake-up;

These two callback functions are per-CPU; each CPU can only execute its own callback functions. When executing these two functions, the kernel disables the CPU interrupt enable flag. If a wake-up event occurs while executing the suspend() callback function, the wake-up event interrupt is pended, waiting for suspend() to return. Only then does the kernel enable the CPU interrupt flag and execute the wake-up event interrupt function.

Example, resume() and suspend() callback functions for WeActMiniStm32H750:

void stm32hal_resume(void)
{
        xwds_pm_resume(&stm32xwds); /* Resume all devices */
}

void xwosac_pmcb_resume(void * arg)
{
        xwsq_t ctx;
        xwirq_t irq;

        XWOS_UNUSED(arg);
        xwos_skd_get_context_lc(&ctx, &irq); /* Get context and interrupt number for debugging */
        stm32hal_resume();
}

void stm32hal_suspend(void)
{
        xwds_pm_suspend(&stm32xwds); /* Suspend all devices */

        /* Set sleep mode to STOP mode:
           Registers and internal RAM data are not lost in STOP mode,
           so sleep mode is SuspendToRAM, and running state can be restored after wake-up. */
        LL_PWR_SetRegulModeDS(LL_PWR_REGU_DSMODE_LOW_POWER);
        LL_PWR_EnableFlashPowerDown();
        LL_PWR_CPU_SetD1PowerMode(LL_PWR_CPU_MODE_D1STOP);
        LL_PWR_CPU_SetD2PowerMode(LL_PWR_CPU_MODE_D2STOP);
        LL_PWR_CPU_SetD3PowerMode(LL_PWR_CPU_MODE_D3STOP);
        LL_LPM_EnableDeepSleep();
}

void xwosac_pmcb_suspend(void * arg)
{
        xwsq_t ctx;
        xwirq_t irq;

        XWOS_UNUSED(arg);
        xwos_skd_get_context_lc(&ctx, &irq); /* Get context and interrupt number for debugging */
        stm32hal_suspend();
}
Suspended ( XWOS_PM_STAGE_SUSPENDED )

This stage is the last stage of the sleep flow and the first stage of the wake-up flow. The callback functions provided to the user in this stage are:

  • sleep() : Put SOC to sleep
  • wakeup() : Wake up SOC

When the idle task of the last CPU finishes executing the suspend() callback function from the previous stage, the kernel switches the power management stage to this stage and executes the sleep() callback function. The SOC system enters the low-power state inside the sleep() callback function. At this point, the clock stops, code execution stops, and the sleep() callback function does not return.

XWOS designs the sleep() callback function to execute in the idle task because interrupt priority issues could prevent the system from waking up, for example, on ARM Cortex-M core MCUs. If the sleep instruction ( WFI ) is executed in a high-priority interrupt, a low-priority wake-up interrupt cannot wake up the system.

When a wake-up event occurs, the wakeup() callback function is executed in the wake-up event interrupt.

There is no lock protection between the sleep() and wakeup() callback functions, so the sleep() callback function must be designed to be interruptible by the wakeup() callback function.

Example, wakeup() and sleep() callback functions for WeActMiniStm32H750:

void stm32hal_wakeup(void)
{
        LL_LPM_EnableSleep(); /* Clear DEEPSLEEP bit */
        SystemClock_Config(); /* Reconfigure clock after recovering from STOP mode */
}

void xwosac_pmcb_wakeup(void * arg)
{
        XWOS_UNUSED(arg);
        stm32hal_wakeup();
}

void stm32hal_sleep(void)
{
}

void xwosac_pmcb_sleep(void * arg)
{
        XWOS_UNUSED(arg);
        stm32hal_sleep();
        /* Position 1 */
        armv7m_wfi(); /* Enter STOP mode via WFI instruction */
        /* Position 2 */
}

Two scenarios are discussed:

  • Wake-up event occurs after the WFI instruction in xwosac_pmcb_sleep() executes: After the wake-up flow completes, code returns to Position 2 in xwosac_pmcb_sleep();
  • Wake-up event occurs before the WFI instruction in xwosac_pmcb_sleep() executes: The wake-up event interrupt interrupts the xwosac_pmcb_sleep() function, then enters the wake-up event interrupt function. As the wake-up flow executes, it calls the xwosac_pmcb_wakeup() function. The function LL_LPM_EnableSleep() clears Cortex-M’s DEEPSLEEP bit. The subsequent wake-up flow also re-enables the tick timer. When returning to Position 1 in xwosac_pmcb_sleep(), the WFI instruction only puts the CPU into ARMv7-M SLEEP mode, briefly pausing the CPU clock. The upcoming tick timer interrupt can restore normal operation.

CAPI Reference